leafittome/docs/handoff.md
2026-07-23 17:12:30 +02:00

76 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Handoff: LeafItToMe (Pflanzenpflege-App)
Stand: 2026-07-23. Sprache mit dem Nutzer (Chris): **Deutsch**.
## Projekt in einem Satz
Private Flutter-App (iOS+Android) zur Pflanzenpflege für Haushalt + Pflanzen-Sitter: Foto-Erkennung (PlantNet + Claude), Gieß-/Düngeplan mit Tages-Checkliste, tägliche Sammel-Push, Haushalt-Teilen per Einladungscode mit Rollen.
## Wo alles liegt
- **Code:** `~/Documents/Projekte/Planty` (Ordner heißt noch Planty, Projekt heißt seit Umbenennung `leafittome`, App-Name „LeafItToMe", Bundle-ID-Platzhalter `dev.leafittome.app`)
- **Git-Remote:** selbst gehostetes Forgejo `https://git.cshomelabs.work/cschlaefke/leafittome.git` (HTTPS + Token im macOS-Schlüsselbund, `git push` läuft ohne Abfrage)
- **Doku (im Repo, deutsch, aktuell halten!):** `README.md`, `docs/architektur.md`, `docs/firebase-einrichtung.md` (Schritt-für-Schritt-Historie aller Backend-Einrichtungen), `docs/git-forgejo.md`
- **Claude-Memory:** Projektplan, Nutzerprofil und Backlog liegen im persistenten Memory-Verzeichnis dieser Claude-Code-Installation (werden automatisch geladen)
- **Firebase-Projekt:** `leaf-it-to-me-app` (Blaze aktiv, Budget-Alarm eingerichtet; Firebase CLI ist eingeloggt)
- **Functions:** `functions/` (TypeScript, Node 22, Region europe-west3): `identifyPlant` (PlantNet + Claude, Secrets `PLANTNET_API_KEY`/`ANTHROPIC_API_KEY` im Secret Manager), `sendDailyReminders` (alle 15 Min, sammelt über alle Haushalte eines Nutzers), `joinHousehold` (Einladungs-Einlösung), `leaveHousehold` (Haushalt verlassen), `removeMember` (Mitglied entfernen, nur Besitzer)
- **Auth:** E-Mail/Passwort + **Anmelden mit Apple** (iOS nativ, Android Web-Flow über Firebase-Handler) — Code in `lib/features/auth/data/auth_repository.dart`, Login-UI in `.../presentation/login_screen.dart`. Haushalts-Bootstrap (Profil + eigener Haushalt beim ersten Login) im gemeinsamen Helper `_bootstrapHouseholdIfNeeded`.
## Verifizierter Stand
- **V1 komplett** und auf Android + iOS Ende-zu-Ende getestet: Foto → Erkennung → vorbefülltes Profil; Tages-Checkliste; Sammel-Push kam im regulären Scheduler-Lauf auf dem Sperrbildschirm an. **iOS-Push seit 2026-07-20 eingerichtet und laufend** (Apple Developer Account freigeschaltet, APNs-Schlüssel in Firebase, Push-/Background-Capability in Xcode — Details + Stolperfalle „Personal Team" in `docs/firebase-einrichtung.md` Schritt 6).
- **V2 implementiert, deployt und vom Nutzer erfolgreich getestet** (Einladungscodes member/sitter, `joinHousehold`-Function, Rules-Enforcement für Sitter, „bestätigt von"-Anzeige).
- **V2.1 Multi-Haushalt implementiert und getestet**: Nutzer bleiben Mitglied aller Haushalte (`memberUids` ist Quelle der Wahrheit, `users.householdId` nur noch aktiver Zeiger), `sendDailyReminders` sammelt Aufgaben aus allen Haushalten.
- **V2.2 implementiert, deployt und vom Nutzer vollständig getestet und bestätigt**: Besitzer-Konzept (`ownerUid`, Alt-Haushalte: memberUids[0]), Besitzer-Anzeige, Haushalt umbenennen (Rules: Clients dürfen nur noch `name` ändern), Haushalts-Wechsler im Drawer, Tages-Checkliste gruppiert Aufgaben aus allen Haushalten, Functions `leaveHousehold`/`removeMember` (biegen den aktiven Zeiger des Betroffenen um, legen notfalls frischen Haushalt an). Zwei Nachbesserungen aus dem Live-Test, beide behoben und bestätigt:
- `leaveHousehold`/`removeMember` warfen zunächst HTTP 401 — fehlende Cloud-Run-Invoker-Berechtigung bei frisch angelegten Gen-2-Functions (kein Auth-Bug im Code); Fix war ein gezielter Neu-Deploy dieser beiden Functions.
- Besitzer-Anzeige blieb bei Alt-Haushalten aus der V1-Zeit leer (kein `members`-Eintrag für den Ersteller, die Map kam erst mit V2). Fix: `memberEntrySyncProvider` trägt beim App-Start die eigene E-Mail in allen eigenen Haushalten nach, plus eine eng gefasste Rules-Ausnahme (jeder darf nur den eigenen `members`-Eintrag pflegen, Rollenwechsel bleibt gesperrt).
## Offen / Nächstes
### 🔴 AKTIVER BUG (hier weitermachen): Apple-Login legt keinen Haushalt an
**Symptom:** Nutzer hat Apple-Login vollständig konfiguriert (Apple-Portal Services-ID + Sign-in-Key, Firebase-Console Apple-Provider, Xcode „Sign in with Apple"-Capability) — **Anmeldung klappt**. Aber: Mit dem **neuen Apple-Account** schlägt das **Anlegen einer Pflanze** mit generischer Meldung „kann nicht gespeichert werden" fehl (`saveError`, aus dem `catch (_)` in `plant_form_screen.dart:154`).
**Leithypothese (noch nicht bestätigt):** Beim Apple-Login wurde **kein Haushalt angelegt**. Mechanismus: `signInWithProvider` meldet „angemeldet" → `authStateChanges` feuert → der Router (`app_router.dart`, redirect prüft nur `currentUser != null`, **nicht** ob ein Haushalt existiert) schiebt sofort in die App. Der Bootstrap (`_bootstrapHouseholdIfNeeded`) läuft erst **danach** in `signInWithApple`. Wirft er, wird der Fehler zwar in `_signInWithApple` (login_screen) gefangen und als `authErrorAppleFailed` gezeigt — aber der Nutzer ist bereits eingeloggt und weggeroutet, sieht die Meldung also faktisch nicht. Ohne Haushalt ist `householdId` null bzw. der Haushalt fehlt in `memberUids` → jeder Schreibzugriff (Storage-Foto-Upload **und** Firestore-Plant-Write) wird von den Rules abgelehnt. **Die Rules selbst sind geprüft und korrekt** (Plant-`create` verlangt nur `isMember && roleOf=='member'`, exakt was der Bootstrap schreibt) — Problem liegt an fehlenden Bootstrap-Daten, nicht an den Rules.
**Warteschritt (auf Nutzer-Antwort, dann entscheiden):** Nutzer sollte prüfen und melden:
- **A)** App → Menü → Haushalt: wird für den Apple-Account ein Haushalt angezeigt (Mitglied/Besitzer) oder leer?
- **B)** Firebase-Konsole → Firestore: existiert `users/{uid}` für den Apple-Account mit Feld `householdId`? Falls ja, hat `households/{id}.memberUids` die UID + `members`-Map mit `role:"member"`?
- **C)** Hatte die Pflanze ein **Foto** (trennt Storage- von Firestore-Fehler)?
**Wenn Hypothese bestätigt (Haushalt fehlt) → Fix-Richtung:** Bootstrap **race-/schluck-sicher** machen, statt ihn nur an `signInWithApple` zu hängen. Optionen: (a) reaktiver Bootstrap — ein Provider/Guard, der bei „eingeloggt aber kein `users`-Doc" den Haushalt anlegt und bis dahin einen Lade-/Redirect-Zustand zeigt (deckt auch künftige Provider ab); oder (b) Router-Redirect zusätzlich an „Haushalt vorhanden" koppeln + Bootstrap-Fehler sichtbar machen. Vorschlag (a) bevorzugen. Danach mit frischem Apple-Account (unter <https://appleid.apple.com> Freigabe für „LeafItToMe" entfernen → nächster Login ist wieder „erster Login") Ende-zu-Ende testen.
**Wenn Haushalt existiert (Hypothese falsch):** dann liegt es doch an Rules/IAM — Storage-`isMember` macht `firestore.get()` (braucht das bereits erteilte IAM-Recht) bzw. Firestore-Plant-`create`. Dann konkret den fehlschlagenden Schritt (Storage vs. Firestore, via Frage C) isolieren.
### Zuletzt erledigt
- **2026-07-23:** Apple-Login **implementiert und committet** (`signInWithApple` via eingebautem `AppleAuthProvider`, „oder"-Trenner + Button im Login, iOS-Entitlement `com.apple.developer.applesignin`, l10n-Strings, Doku `firebase-einrichtung.md` Schritt 7). Analyzer + alle 8 Tests grün. **Konfiguration vom Nutzer erledigt, Login funktioniert — aber der obige Bug ist offen.**
- **2026-07-20:** iOS-Push eingerichtet (Apple Developer Account freigeschaltet, APNs/Firebase/Xcode). V1 damit auch auf iOS vollständig.
### Weitere Kandidaten (nach dem Bug)
1. **V3:** Krankheits-Diagnose per Foto, Stellplatz-Bewertung (Standort-Analyse pro Stellplatz + Eignungs-Sterne pro Pflanze — Zwei-Ebenen-Konzept siehe Memory), Umtopf-Erinnerungen.
2. **Backlog:** Google-Login (nach Apple, `firebase-einrichtung.md` Schritt 8); Button „KI-Zweitmeinung" bei unbefriedigendem PlantNet-Ergebnis; Push-Anzeige auch bei App im Vordergrund; App Check vor App-Store-Release; Englisch (`app_en.arb`).
## Stolperfallen (teuer erkauft, nicht erneut zahlen)
- `firebase functions:log` hinkt **1520 Minuten** hinterher — „keine Läufe" heißt meist nur Log-Verzögerung. Scheduler-Status im Cloud-Scheduler-Console prüfen lassen.
- Android zeigt FCM-Pushes **nur bei App im Hintergrund**.
- `flutter_timezone` ≥5 liefert `TimezoneInfo`-Objekt → `.identifier` verwenden (Objekt crasht Firestore-Writes still).
- Storage-Rules mit `firestore.get()` brauchen die IAM-Rolle „Firebase Rules Firestore Service Agent" fürs Storage-Dienstkonto (wurde manuell erteilt).
- Erster Functions-Deploy in frischem Projekt kann mit 403 `iam.serviceaccounts.actAs` scheitern → wenige Minuten warten, erneut deployen.
- Analyzer: `build/` und `ios/build/` sind in `analysis_options.yaml` ausgeschlossen (SwiftPM legt dort Fremdcode ab).
- iOS-Fehler „requires minimum platform version 15.0 … but this target supports 13.0": Das von Flutter generierte SwiftPM-Paket (`ios/Flutter/ephemeral/…/Package.swift`) wird bei `pub get` mit iOS 13 erzeugt; erst `flutter build ios`/`flutter run` hebt es auf das Projekt-Target (15.0) an. Nach `flutter clean` oder beim Bauen direkt aus Xcode daher einmal `flutter build ios --config-only` ausführen.
- Nutzer wünscht zu jedem Backend-Baustein **begleitende deutsche Doku** in `docs/` (keine Firebase-Vorerfahrung).
## Arbeitsabläufe
- Prüfen: `flutter analyze && flutter test` (Tests mocken Firebase via `firebase_auth_mocks`/`fake_cloud_firestore`); Android-Verifikation: `flutter build apk --debug`
- Texte: nur in `lib/l10n/app_de.arb` (typografische Anführungszeichen „…“ verwenden, gerade `"` brechen das JSON), dann `flutter gen-l10n`
- Deploy: `firebase deploy --only functions|firestore|storage --project leaf-it-to-me-app`; nach jedem Arbeitsstand committen + `git push`
## Suggested skills
- `/verify` — nach nichttrivialen Änderungen den echten Flow treiben (Gerät/Simulator), nicht nur Tests
- `/code-review` — vor größeren Merges über den Diff laufen lassen
- `/run` — App starten/screenshotten, wenn Änderungen sichtbar geprüft werden sollen