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

9.9 KiB
Raw Blame History

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