# Handoff: LeafItToMe (Pflanzenpflege-App) Stand: 2026-09-08 (Session-Ende). 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ünge-/Umtopfplan mit Tages-Checkliste, tägliche Sammel-Push, Krankheits-Diagnose per Foto mit Historie, 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` (inkl. Kernentscheidungen: berechnete Aufgaben, reaktiver Haushalts-Bootstrap, Umtopfen als Opt-in-Monatsintervall), `docs/firebase-einrichtung.md` (Schritt-für-Schritt-Historie aller Backend-Einrichtungen, zuletzt Schritt 8 Google-Login), `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), `diagnosePlant` (V3: Krankheits-Diagnose per Foto, Claude Vision, Modell `claude-sonnet-5`), `analyzeLocation` (V3: Standort-Analyse per Foto, Claude Vision, Lichtverhältnisse), `assessPlantFit` (V3: Eignungs-Bewertung einer Pflanze für ihren Stellplatz, rein textbasiert ohne Foto), `sendDailyReminders` (alle 15 Min, sammelt Gießen/Düngen/Umtopfen über alle Haushalte eines Nutzers), `joinHousehold`, `leaveHousehold`, `removeMember` - **Auth:** E-Mail/Passwort + **Apple** (iOS nativ, Android Web-Flow) + **Google** (beide Plattformen Web-Flow, eingebauter `GoogleAuthProvider`) — Code in `lib/features/auth/`. Haushalts-Bootstrap läuft **reaktiv** über `householdBootstrapProvider` + `BootstrapGate` (Maßstab: `householdId` im users-Doc, NICHT Doc-Existenz — die Push-Registrierung legt das users-Doc nebenbei an!). Details der Entscheidung in `docs/architektur.md`. ## Verifizierter Stand - **V1 komplett** (Foto → Erkennung → Profil; Checkliste; Sammel-Push), auf Android + iOS Ende-zu-Ende getestet. iOS-Push seit 2026-07-20 eingerichtet und laufend (`firebase-einrichtung.md` Schritt 6). - **V2/V2.1/V2.2 komplett getestet und bestätigt:** Einladungscodes member/sitter, Multi-Haushalt (`memberUids` = Quelle der Wahrheit, `users.householdId` = aktiver Zeiger), Besitzer-Konzept, Umbenennen, Haushalts-Wechsler, `leaveHousehold`/`removeMember`. - **Apple-Login: implementiert, Bug behoben, vom Nutzer bestätigt** (2026-07-23). Der Bug (neuer Apple-Account konnte nichts speichern) hatte zwei Schichten: Bootstrap hing am Login-Aufruf und wurde vom Router-Redirect überholt; UND die Push-Registrierung hatte das users-Doc ohne `householdId` vorab angelegt, weshalb ein Existenz-Check nicht reichte. Fix: reaktiver Bootstrap + `householdId`-Maßstab + merge-Writes; selbstheilend für kaputte Accounts. Zusätzlich beseitigt: Riverpod-3-Warnung „markNeedsBuild during build" (BootstrapGate als Stack-Overlay statt Baum-Austausch + `checklistWarmupProvider` in `app.dart` gegen das Pausieren unbeobachteter Provider). - **Google-Login: implementiert, deployt und auf Gerät getestet** (2026-07-23 implementiert, Gerätetest 2026-07-24 bestätigt; `firebase-einrichtung.md` Schritt 8; Debug-SHA-1/SHA-256 per `firebase apps:android:sha:create` hinterlegt — der SHA-Konsolen-Hinweis betrifft nur den nativen Flow und ist für unseren Browser-Flow irrelevant). - **V3 Baustein 1 — Krankheits-Diagnose per Foto: komplett, deployt, vom Nutzer bestätigt** („sieht richtig gut aus"). Function `diagnosePlant` + „Pflanze untersuchen" im Pflanzen-Detail (Kamera/Galerie → Ergebnis-Sheet mit Ampel-Icon, Behandlung, Vorbeugung, KI-Disclaimer; `diagnosis_sheet.dart` gemeinsam genutzt). Nachbesserungen aus Live-Tests, alle bestätigt: Art-Angabe ist unbestätigter Hinweis (`matchesSpecies=false` → Warnkasten „Foto zeigt andere Pflanze"), kein HTML/Markdown im Text, konsequentes Duzen. **Untersuchungshistorie** unter `households/{id}/plants/{id}/diagnoses` (Rules: read/create alle im Haushalt, delete nur member, update nie), auf eigener Seite `/plants/:id/diagnoses`; Button „Frühere Untersuchungen" im Detail ist bei leerer Historie deaktiviert. **Historie-Seite auf Gerät getestet und bestätigt** (2026-07-24). - **V3 Baustein 2 — Umtopf-Erinnerungen: implementiert und deployt** (2026-07-23, Commit `81d990d`). Dritter Aufgabentyp `repotting`, als Einziger opt-in (`repottingIntervalMonths` optional) und in Kalender-Monaten (31. → Monatsletzter; Logik gespiegelt in `due_tasks_provider` und `reminders.ts`). Formular mit Intervall-Feld + „Zuletzt umgetopft"-Datum, Checkliste + Push („Umtopfen: …"), Sitter dürfen bestätigen. Rules + `sendDailyReminders` sind deployt. **Gerätetest komplett bestätigt** (2026-08-19, inkl. Nachbesserung „Nächstes Fälligkeitsdatum"). Pflanzen-Detailseite zeigt bei Gießen/Düngen/Umtopfen zusätzlich zum letzten Datum das **nächste Fälligkeitsdatum** an (bzw. „heute fällig"). Berechnung dafür aus `due_tasks_provider.dart` public gemacht (`nextWateringDue`/`nextFertilizingDue`/`nextRepottingDue`) statt dupliziert. Neue l10n-Keys `nextWatering`/`nextFertilizing`/`nextRepotting`/`dueToday`. **V3 Baustein 2 damit vollständig abgeschlossen.** - **V3 Baustein 3 — Stellplatz-Bewertung: implementiert, deployt und vom Nutzer bestätigt** („sieht gut aus", 2026-08-19). Functions `analyzeLocation`/`assessPlantFit` + Firestore-Rules live. Konzept mit dem Nutzer abgestimmt: Standort-Daten nur per Foto (keine Zusatzfragen zu Himmelsrichtung), Eignungs-Sterne ausschließlich auf Abruf (Kostenkontrolle, eigener Anthropic-Key). Zwei Functions `analyzeLocation` (Foto → Lichtkategorie + Freitext, gespeichert auf `PlantLocation`) und `assessPlantFit` (textbasiert, Pflegeprofil + Standort-Analyse → 1-5 Sterne + Begründung, gespeichert auf `Plant`). Neue Seite `LocationDetailScreen` (`/locations/:id`, erreichbar per Tap in der Stellplätze-Liste): Foto/Analyse-Bereich (nur Mitglieder) + Liste zugeordneter Pflanzen mit Sternen oder „Eignung prüfen"-Button (auch für Sitter, informativ). Pflanzen-Detail zeigt den Stellplatz jetzt kompakt mit Sternen und verlinkt dorthin. Veraltete Bewertungen (Pflanze verschoben oder Stellplatz neu analysiert) werden erkannt und als solche markiert statt falscher Sterne. Firestore-Rules erweitert (Sitter dürfen die vier Fit-Felder schreiben). Details/Begründung der Entscheidungen in `docs/architektur.md`. **V3 Baustein 3 damit vollständig abgeschlossen.** - **Damit ist die komplette V3-Roadmap (Diagnose, Umtopf-Erinnerungen, Stellplatz-Bewertung) implementiert, deployt und gerätegetestet.** - Testabdeckung: 18 Widget-Tests grün (u. a. Bootstrap-Heilung, Untersuchungshistorie bis ins Sheet, Umtopf-Flow Ende-zu-Ende inkl. Detailanzeige, Stellplatz-Bewertung inkl. Veraltet-Erkennung), Analyzer sauber, `tsc` kompiliert, Android-Debug-Build ok. ## Offen / Nächstes ### 1. App-Store-Vorbereitung (iOS/TestFlight abgeschlossen 2026-09-08; Android offen) Ziel fürs Erste: **nur TestFlight + Play-Internal-Testing**, kein öffentlicher Store-Eintrag (mit Chris abgestimmt — spart Screenshots/Store-Texte/Altersfreigabe für später). Bundle-ID bleibt bewusst `dev.leafittome.app` (eigene Domain steht noch aus, ist aber kein Blocker für TestFlight/Internal-Testing). **iOS / TestFlight: fertig (2026-09-08).** Erster Build `1.0.0 (1)` hochgeladen, in TestFlight „Bereit zum Testen", auf dem iPhone installiert und nutzbar. Dabei erledigt: - `lib/main.dart`: App-Check-Provider per `kReleaseMode` umgeschaltet — Release nutzt `AndroidPlayIntegrityProvider` / `AppleAppAttestProvider`, Debug weiter Debug-Provider. Nötig, weil die Functions App Check erzwingen. - App-Icon: `assets/icon/icon.png` (1024×1024, ohne Alpha) + `dart run flutter_launcher_icons` → iOS-/Android-Icon-Sätze generiert. (Tool-Macke v0.14.4: verhunzt `ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS` in `project.pbxproj` von `YES` auf `AppIcon` — danach zurücksetzen.) - Datenschutzerklärung: Platzhalter in `docs/legal/datenschutz.html` ausgefüllt (Christopher Schläfke / cschlaefke@live.com / 8. September 2026), interner Hinweiskasten raus. Gehostet via **Firebase Hosting** (`hosting`-Block in `firebase.json`, `public: "docs/legal"`), URL **`https://leaf-it-to-me-app.web.app/datenschutz`** — in App Store Connect eingetragen. Deploy: `firebase deploy --only hosting`. - App Store Connect: App-Eintrag + App-Datenschutz-Fragebogen ausgefüllt (E-Mail, Fotos, Nutzerinhalte, Nutzer-ID — alle verknüpft, kein Tracking). - **Stolperfalle (teuer erkauft):** `flutter build ipa` über die CLI signiert headless nur mit „Apple Development" → App-Store-Upload lehnt mit „Invalid Signature / Code 90035" jede Framework-Datei ab. Fix: in Xcode → Settings → Accounts → Manage Certificates → „+" → **Apple Distribution** anlegen, dann **aus Xcode archivieren** (Product → Archive) statt CLI. Details in `firebase-einrichtung.md` Schritt 10. - Die „Upload Symbols Failed / dSYM"-Meldungen für Firebase/gRPC/absl/Recaptcha beim Upload sind **harmlos** (vorkompilierte Frameworks ohne Debug-Symbole, nur Crash-Symbolik). **Offene Kleinigkeit iOS:** Launch-Screen ist noch der weiße Flutter-Platzhalter (Warnung beim Build, kein Blocker) → Backlog. **App Check: vollständig abgeschlossen** (2026-08-20). Play Integrity (Android) + App Attest (iOS) in der Firebase-Console registriert, Debug-Token hinterlegt, „Verified requests" für Cloud Functions auf beiden Plattformen bestätigt, `enforceAppCheck: true` auf allen 7 `onCall`-Functions deployt (Commit `49b7f44`) und per Gerätetest bestätigt (Pflanze anlegen/Diagnose funktionieren normal). **Hinweis für später:** Unverified Requests bei Firestore/Storage in der Console sind erwartet/harmlos — dafür wurde nie Enforcement eingerichtet, nur für die Functions. Ebenso die einmalige iOS-Kaltstart-Meldung „App not registered … exchangeDeviceCheckToken" — bekannte, folgenlose Plugin-Race, siehe `firebase-einrichtung.md` Schritt 9. **Weiteres Code-seitig erledigt:** - iOS: `ITSAppUsesNonExemptEncryption = false` in `Info.plist` (vermeidet wiederkehrende Export-Compliance-Nachfrage bei jedem TestFlight-Upload). - Android: Release-Signing vorbereitet (`android/app/build.gradle.kts` liest `key.properties`, Vorlage `android/key.properties.example`; Datei + `.jks` sind in `.gitignore`). **Der Upload-Key selbst existiert noch nicht** — Chris muss ihn per `keytool` erzeugen (Befehl in `firebase-einrichtung.md` Schritt 11) und sofort sichern. - `flutter_launcher_icons` vorbereitet (`pubspec.yaml`), zeigt auf `assets/icon/icon.png` — Datei existiert noch nicht. - Entwurf Datenschutzerklärung: `docs/legal/datenschutz.html` (Platzhalter `[Name]`/`[E-Mail-Adresse]`/`[Datum]` müssen noch von Chris ausgefüllt werden). Hosting-Anleitung in `firebase-einrichtung.md` jetzt konkret auf Chris' Setup zugeschnitten: eigene Domain + Cloudflare (Subdomain per A-Record auf den Hetzner-VPS, SSL-Modus „Full (strict)" mit Cloudflare-Origin-Certificate, nginx-Server-Block-Vorlage). - Nebenbei behoben: SPM-Versionskonflikt durch `firebase_app_check`-Einführung (inkonsistente Firebase-Paket-Minor-Versionen verhinderten den iOS-Build) — Commit `248f3be`. - Neue Doku-Schritte in `firebase-einrichtung.md`: **Schritt 9** (App Check, jetzt komplett abgeschlossen), **Schritt 10** (TestFlight einrichten), **Schritt 11** (Play Internal Testing einrichten) — inkl. Textvorlagen für Apples App-Privacy-Fragebogen und Googles Data-Safety-Formular. **Noch offen — nur noch Android (Play Internal Testing), dokumentiert in `firebase-einrichtung.md` Schritt 11:** - Android-Upload-Key erzeugen (`keytool`, Schritt 11) und sofort sichern (Passwort-Manager + Backup außerhalb des Repos). - `android/key.properties` lokal anlegen (Vorlage `android/key.properties.example`). - Release-Fingerabdruck (SHA-1/SHA-256 des Upload-Keys) in Firebase hinterlegen (`firebase apps:android:sha:create`) — für App Check / Play Integrity im Release. - `flutter build appbundle --release` → Play Console: App anlegen, Datenschutz-URL (`https://leaf-it-to-me-app.web.app/datenschutz`) + Data-Safety-Formular + Content-Rating, internen Test-Track + Tester. - App-Icon ist bereits generiert (auch Android-Mipmaps), Datenschutzerklärung ist bereits gehostet — beides gilt für Android mit. ### 2. Backlog Button „KI-Zweitmeinung" bei unbefriedigendem PlantNet-Ergebnis; Push-Anzeige auch bei App im Vordergrund; Englisch (`app_en.arb`); `firebase-functions`-Paket-Upgrade (Deploy-Warnung, mit Breaking Changes — bei Gelegenheit). ## Stolperfallen (teuer erkauft, nicht erneut zahlen) - **Deploys und Produktions-Datenzugriffe blockiert der Auto-Mode-Berechtigungsfilter der Session.** Nicht dagegen anarbeiten: Kommando fertig vorbereiten und den Nutzer bitten, es per `! ` in der Eingabezeile auszuführen — macht er zuverlässig und prompt. - Frisch **angelegte** Gen-2-Functions können beim ersten Aufruf HTTP 401 liefern (fehlende Cloud-Run-Invoker-Berechtigung) → dieselbe Function einfach erneut deployen. Updates bestehender Functions sind nicht betroffen. - `firebase functions:log` hinkt **15–20 Minuten** hinterher — „keine Läufe" heißt meist nur Log-Verzögerung. Scheduler-Status in der 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). - Die **Push-Registrierung legt `users/{uid}` per merge selbst an** (fcmTokens/timezone). Jede Logik, die „Profil existiert" prüfen will, muss auf das Feld `householdId` schauen, nie auf die Dokument-Existenz. - Riverpod 3 **pausiert Provider ohne aktive Zuhörer**; beim Einhängen von Screens mitten im Provider-Update gibt das „setState() or markNeedsBuild() called during build". Gegenmittel im Repo: Overlay statt Baum-Austausch (BootstrapGate) und dauerhaftes Warmhalten kritischer Ketten (`checklistWarmupProvider`). - Storage-Rules mit `firestore.get()` brauchen die IAM-Rolle „Firebase Rules Firestore Service Agent" fürs Storage-Dienstkonto (wurde manuell erteilt). - 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": nach `flutter clean` oder beim Bauen direkt aus Xcode einmal `flutter build ios --config-only` ausführen (SwiftPM-Paket wird bei `pub get` mit iOS 13 erzeugt). - In `.arb`-Dateien UND in deutschen Texten innerhalb von TS-Strings (Function-Prompts!) **typografische Anführungszeichen „…“** verwenden — gerade `"` brechen das JSON bzw. den TS-String. - Die Bash-Session wechselt das Arbeitsverzeichnis **nicht zurück**: nach `cd functions && npm run build` vor Flutter-Kommandos wieder explizit ins Projekt-Root. - **iOS-App-Store-Upload braucht ein „Apple Distribution"-Zertifikat.** `flutter build ipa` über die CLI zieht es headless nicht und signiert nur mit „Apple Development" → Upload scheitert mit „Invalid Signature / Code 90035". Zuerst in Xcode → Settings → Accounts → Manage Certificates → „+" → Apple Distribution anlegen (`security find-identity -v -p codesigning` muss die Zeile zeigen), dann **aus Xcode archivieren** (Product → Archive), nicht per CLI. - `flutter_launcher_icons` v0.14.4 schreibt beim Generieren `ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon` (statt `YES`) in `ios/Runner.xcodeproj/project.pbxproj` — danach `git checkout` auf die Datei, die generierten Icons bleiben. - Nutzer wünscht zu jedem Backend-Baustein **begleitende deutsche Doku** in `docs/` (keine Firebase-Vorerfahrung). ## Arbeitsabläufe - Prüfen vor jedem Commit: `flutter analyze && flutter test` (Tests mocken Firebase via `firebase_auth_mocks`/`fake_cloud_firestore`); bei TS-Änderungen zusätzlich `npm run build` in `functions/`; Android-Verifikation: `flutter build apk --debug` - Texte: nur in `lib/l10n/app_de.arb`, danach `flutter gen-l10n` - Deploy (führt der Nutzer aus, s. Stolperfallen): `firebase deploy --only functions|firestore|storage --project leaf-it-to-me-app` - Nach jedem Arbeitsstand: committen + `git push` + dieses Handoff aktualisieren (deutsche Commit-Messages, Muster siehe Git-Log) - Der Nutzer testet auf echten Geräten gegen das Produktions-Firebase und meldet präzise Beobachtungen; gezielte Diagnosefragen (AskUserQuestion) haben sich bewährt ## 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 - `claude-api` — vor Änderungen an den Claude-Aufrufen in `functions/src/` laden (Modell-IDs, Vision, Prompt-Konventionen)