- main.dart: App-Check-Provider per kReleaseMode umgeschaltet (Release = PlayIntegrity/AppAttest, Debug = Debug-Provider). Nötig, da die Functions App Check erzwingen — ein Release-Build mit Debug-Provider würde auf fremden Geräten bei jedem Function-Aufruf scheitern. - App-Icon aus assets/icon/icon.png via flutter_launcher_icons generiert (iOS-Set + Android-Mipmaps). - docs/legal/datenschutz.html: Platzhalter ausgefüllt, interner Hinweis raus. - firebase.json: hosting-Block (public: docs/legal, Redirect / -> /datenschutz). URL: https://leaf-it-to-me-app.web.app/datenschutz - Doku: firebase-einrichtung.md Schritt 9/10 + Hosting fortgeschrieben, handoff.md aktualisiert (iOS/TestFlight abgeschlossen, nur noch Android offen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMRhL16hB14xcfef1qez7N
17 KiB
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 Umbenennungleafittome, App-Name „LeafItToMe", Bundle-ID-Platzhalterdev.leafittome.app) - Git-Remote: selbst gehostetes Forgejo
https://git.cshomelabs.work/cschlaefke/leafittome.git(HTTPS + Token im macOS-Schlüsselbund,git pushlä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, SecretsPLANTNET_API_KEY/ANTHROPIC_API_KEYim Secret Manager),diagnosePlant(V3: Krankheits-Diagnose per Foto, Claude Vision, Modellclaude-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 inlib/features/auth/. Haushalts-Bootstrap läuft reaktiv überhouseholdBootstrapProvider+BootstrapGate(Maßstab:householdIdim users-Doc, NICHT Doc-Existenz — die Push-Registrierung legt das users-Doc nebenbei an!). Details der Entscheidung indocs/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.mdSchritt 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
householdIdvorab 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 +checklistWarmupProviderinapp.dartgegen 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.mdSchritt 8; Debug-SHA-1/SHA-256 perfirebase apps:android:sha:createhinterlegt — 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.dartgemeinsam 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 unterhouseholds/{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 Aufgabentyprepotting, als Einziger opt-in (repottingIntervalMonthsoptional) und in Kalender-Monaten (31. → Monatsletzter; Logik gespiegelt indue_tasks_providerundreminders.ts). Formular mit Intervall-Feld + „Zuletzt umgetopft"-Datum, Checkliste + Push („Umtopfen: …"), Sitter dürfen bestätigen. Rules +sendDailyReminderssind 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 ausdue_tasks_provider.dartpublic gemacht (nextWateringDue/nextFertilizingDue/nextRepottingDue) statt dupliziert. Neue l10n-KeysnextWatering/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 FunctionsanalyzeLocation(Foto → Lichtkategorie + Freitext, gespeichert aufPlantLocation) undassessPlantFit(textbasiert, Pflegeprofil + Standort-Analyse → 1-5 Sterne + Begründung, gespeichert aufPlant). Neue SeiteLocationDetailScreen(/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 indocs/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,
tsckompiliert, 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 perkReleaseModeumgeschaltet — Release nutztAndroidPlayIntegrityProvider/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: verhunztASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONSinproject.pbxprojvonYESaufAppIcon— danach zurücksetzen.) - Datenschutzerklärung: Platzhalter in
docs/legal/datenschutz.htmlausgefüllt (Christopher Schläfke / cschlaefke@live.com / 8. September 2026), interner Hinweiskasten raus. Gehostet via Firebase Hosting (hosting-Block infirebase.json,public: "docs/legal"), URLhttps://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 infirebase-einrichtung.mdSchritt 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 = falseinInfo.plist(vermeidet wiederkehrende Export-Compliance-Nachfrage bei jedem TestFlight-Upload). - Android: Release-Signing vorbereitet (
android/app/build.gradle.ktsliestkey.properties, Vorlageandroid/key.properties.example; Datei +.jkssind in.gitignore). Der Upload-Key selbst existiert noch nicht — Chris muss ihn perkeytoolerzeugen (Befehl infirebase-einrichtung.mdSchritt 11) und sofort sichern. flutter_launcher_iconsvorbereitet (pubspec.yaml), zeigt aufassets/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 infirebase-einrichtung.mdjetzt 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) — Commit248f3be. - 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.propertieslokal anlegen (Vorlageandroid/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
! <kommando>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:loghinkt 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 liefertTimezoneInfo-Objekt →.identifierverwenden (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 FeldhouseholdIdschauen, 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/undios/build/sind inanalysis_options.yamlausgeschlossen (SwiftPM legt dort Fremdcode ab). - iOS-Fehler „requires minimum platform version 15.0 … but this target supports 13.0": nach
flutter cleanoder beim Bauen direkt aus Xcode einmalflutter build ios --config-onlyausführen (SwiftPM-Paket wird beipub getmit 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 buildvor 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 codesigningmuss die Zeile zeigen), dann aus Xcode archivieren (Product → Archive), nicht per CLI. flutter_launcher_iconsv0.14.4 schreibt beim GenerierenASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon(stattYES) inios/Runner.xcodeproj/project.pbxproj— danachgit checkoutauf 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 viafirebase_auth_mocks/fake_cloud_firestore); bei TS-Änderungen zusätzlichnpm run buildinfunctions/; Android-Verifikation:flutter build apk --debug - Texte: nur in
lib/l10n/app_de.arb, danachflutter 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 sollenclaude-api— vor Änderungen an den Claude-Aufrufen infunctions/src/laden (Modell-IDs, Vision, Prompt-Konventionen)