# 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 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 **15–20 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