# Relationship Saver Progress Log Updated: 2026-02-23 ## Collaboration Rule (Carry Forward) - After every sensible code/documentation change set, create a git commit as the last step so the next agent session can pick up from clean checkpoints. ## Latest Milestone (2026-02-23): Relationship Graph Preview + Full-Screen Explorer Reworked the graph interaction model to be mobile-friendly on the dashboard and still support smooth pan/zoom exploration. - `lib/features/dashboard/dashboard_view.dart` - refactored graph rendering into a shared canvas widget with two modes: - `preview` (dashboard, scroll-first) - `explorer` (full-screen, pan/zoom-first) - dashboard graph card is now a scroll-safe preview: - keeps node long-press for `Person Insights` - disables pan/zoom interaction on the dashboard canvas - adds explicit `Explore` / `Open Explorer` CTA button - updated helper copy to clarify dashboard preview vs explorer behavior - added full-screen `Relationship Graph` explorer page: - pan enabled - pinch-to-zoom enabled - `Reset view` control - same long-press-to-open-person-insights behavior - shared legend display - extracted shared graph helpers (legend + navigation helpers) to keep the preview and explorer behavior consistent - `test/features/dashboard/dashboard_graph_interactions_test.dart` - extended coverage to verify: - dashboard preview renders `Explore` CTA - explorer page opens from dashboard - `Reset view` action is available and works - returning to dashboard still works - UX decision captured in implementation - dashboard remains scroll-first on mobile (no hidden pan-mode toggle) - graph exploration happens in a dedicated full-screen context - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Quick-Capture Dialog Regression Test (People) Added explicit widget-test coverage so controller lifecycle issues in quick capture dialogs are caught automatically in CI/local runs. - `test/features/people/people_quick_capture_dialog_test.dart` (new) - mounts `PeopleView` in mobile layout - opens profile-context `Capture` quick action dialog - exercises `Cancel` path and pumps through dismissal frames - reopens dialog, saves a capture, and pumps through dismissal frames - asserts: - no framework exceptions surfaced (`tester.takeException() == null`) - local moments count increases after save - specifically protects against the disposed `TextEditingController` race - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Quick-Capture Dialog Controller Lifecycle Fix Fixed a runtime crash when creating captures/notes from profile-context quick actions (`TextEditingController was used after being disposed`) caused by a dialog animation/disposal race. - Root cause - quick-capture dialogs in `People` and graph `Person Insights` created a local `TextEditingController`, passed it into `showDialog(...)`, then disposed it immediately after `await showDialog(...)` - the dialog route can still rebuild during dismiss animation, which caused the `TextField` to touch a disposed controller - Fixes - `lib/features/people/people_view.dart` - replaced ad-hoc quick text capture dialog with stateful `_QuickTextCaptureDialog` that owns and disposes its controller internally - `lib/features/dashboard/dashboard_view.dart` - replaced ad-hoc quick text capture dialog with stateful `_InsightQuickTextCaptureDialog` with the same lifecycle fix - both dialogs now also show inline `Required` validation instead of silent no-op when pressing save with empty text - Similar issue scan - searched the codebase for the same anti-pattern (local `TextEditingController` created outside dialog widget and disposed after `showDialog`) - no remaining occurrences found after patching these two flows - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Preference Review UI (People + Graph Insights) Exposed the new chat-derived preference signals in the main person detail surfaces and added a user review loop to confirm or dismiss inferred context. - `lib/features/people/people_view.dart` - added `Chat-derived preferences` section to the People detail workspace - shows inferred/confirmed/dismissed signals sorted by status/confidence - per-signal actions: - `Why?` (evidence preview bottom sheet) - `Confirm` - `Dismiss` - evidence preview includes: - signal metadata (category/polarity/confidence) - evidence snippets extracted from shared chat messages - source app list - added explicit `Close` action in evidence sheet (better UX + testability) - `lib/features/dashboard/dashboard_view.dart` - added matching `Chat-derived preferences` section to graph long-press `Person Insights` page - same confirm/dismiss/why review flow and evidence preview - keeps profile review experience aligned between People page and graph flow - `test/features/dashboard/dashboard_graph_interactions_test.dart` - extended graph interaction test to: - seed an inferred preference signal - assert section renders in `Person Insights` - open `Why?` evidence preview - confirm signal and verify local status updates to `confirmed` - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): MVP Chat Preference Extractor + Ingest Enrichment Implemented the first local semantic-enrichment pass so shared chat messages can start building inferred person preferences automatically. - `lib/features/local/chat_preference_extractor.dart` (new) - added a pure Dart, local-first rule-based extractor - extracts evidence-backed preference candidates from message text: - `prefer X over Y` (creates like/dislike pair) - `favorite ... is ...` - `I love/like/enjoy ...` - `I hate/dislike/don’t like ...` - `allergic to ...` - adds category heuristics (e.g. drink/food/hobby/environment/health) - returns confidence-scored candidates with evidence snippets - `lib/features/local/local_repository.dart` - integrated extractor into resolved shared-message ingest flow - when a chat share is imported and linked to a profile: - store raw shared message + capture moment (existing behavior) - extract preference candidates from message text - upsert local inferred preference signals with evidence + source metadata - Tests - `test/features/local/chat_preference_extractor_test.dart` (new) - extractor unit coverage for positive/negative/comparative/allergy patterns - `test/features/local/local_repository_test.dart` - added ingest test proving shared chat import creates inferred signals - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Local Preference Signal Scaffolding (Inferred/Confirmed) Added a local-first data model and repository APIs for chat-derived preference signals so the app can gradually build profile context from shared messages. - `lib/features/local/local_models.dart` - new enums: - `PreferenceSignalPolarity` (`like`, `dislike`, `neutral`) - `PreferenceSignalStatus` (`inferred`, `confirmed`, `dismissed`) - new model: - `PersonPreferenceSignal` - key/category/label - confidence - status - first/last seen timestamps - occurrence count - evidence message ids/snippets - source apps - `LocalDataState` now stores `preferenceSignals` - added `copyWith`, `toJson`, `fromJson` support (backward-compatible with existing stored state) - `lib/features/local/local_repository.dart` - added local-only repository APIs: - `upsertInferredPreferenceSignalObservation(...)` - aggregates repeated observations - merges evidence and source apps - updates confidence and occurrence count - `upsertPreferenceSignal(...)` - `setPreferenceSignalStatus(...)` - `confirmPreferenceSignal(...)` - `dismissPreferenceSignal(...)` - updated person lifecycle consistency: - deleting a person removes linked preference signals - merging profiles rebinds source preference signals to target - remote person delete application also removes preference signals - intentionally local-only for now (not yet mapped to backend sync envelopes) - `test/features/local/local_repository_test.dart` - added coverage for inferred signal upsert aggregation + status transitions - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Person Editor Inline Validation + Duplicate Warning Improved the People add/edit profile experience to make validation explicit and support safer profile creation in a sharing-first workflow. - `lib/features/people/people_view.dart` - added inline required-field validation for `Name` and `Relationship` (no more silent no-op when save is pressed with empty required fields) - added live duplicate-name warning banner in add/edit form: - checks normalized profile names against existing local profiles - excludes the current profile during edit - warns before save while still allowing save (with follow-up merge flow) - routed existing profile list into the responsive editor panel/sheet so duplicate detection works on mobile + desktop/web - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): Responsive People Add/Edit Form (Sheet + Dialog) Aligned the People profile creation/edit flow with the newer insights-style UI language and improved platform-specific presentation behavior. - `lib/features/people/people_view.dart` - replaced the old `AlertDialog` person editor with a shared responsive form panel: - mobile (`<720px`): `showModalBottomSheet` with a rounded sheet layout - desktop/web/tablet: larger custom dialog panel - added richer form UX with: - hero preview (avatar + live preview chips for name/relationship/location/tags) - grouped sections (`Identity`, `Context`) matching the People/Insights card style - preserved existing labels and `Save` action text for test compatibility - kept add/edit repository behavior unchanged (local-first) - fixed narrow-width section header overflow discovered by widget test - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-22): People Page UX Redesign (Insights-Aligned) Reworked the `People` page to feel closer to the graph long-press `Person Insights` experience and improved the compact/mobile interaction flow. - `lib/features/people/people_view.dart` - redesigned the selected person detail area into an insights-style workspace: - hero summary card with quick actions (`Capture`, `Idea`, menu + edit/delete) - stats tiles (affinity, next moment, captures, ideas) - section cards for: - profile context (relationship/location/tags/source links + notes) - ideas - moments - reminders - shared message history - added richer person list cards: - next moment pill - optional location pill - tag pills - added People list controls: - search - relationship filter chips - sort menu (affinity / next / A-Z) - empty state when filters yield no results - improved compact/mobile flow: - selected profile workspace now renders above the full list (instead of after all profiles) - explicit `All profiles` section header with count - improved add-person flow UX: - newly created person is automatically selected/focused immediately - also fixed the mobile CRUD widget test expectation after layout changes - migrated page-local filter state to Riverpod 3 `Notifier` providers (instead of unavailable legacy `StateProvider` API) - Validation - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-18): Quick-Add Actions on Graph Person Insights Closed a UX gap where long-pressing a graph node opened person insights without the expected context add action. - Added `Quick add` menu to person profile header in graph-based insights page: - `lib/features/dashboard/dashboard_view.dart` - supports: - Add Capture - Add Note - Add Idea - Add Reminder - Wired actions directly to `LocalRepository` so entries are immediately written to local-first state and reflected in insights sections. - Added local dialogs for quick idea/reminder creation in insights flow. - Updated dashboard interaction coverage: - `test/features/dashboard/dashboard_graph_interactions_test.dart` - now asserts `Quick add` is present after opening `Person Insights`. - Added visible floating `+` button on `Person Insights` for mobile-first discoverability: - opens a bottom sheet with the same quick actions as the header menu - tooltip: `Add to person` ## Latest Milestone (2026-02-18): Duplicate Profile Merge + Share Deep-Link Polish Improved identity continuity from WhatsApp intake with merge tooling and direct navigation: - Added duplicate-profile merge operation in local repository: - `lib/features/local/local_repository.dart` - new `mergePersonProfiles(sourcePersonId, targetPersonId)`: - merges target profile metadata (tags/notes/location/affinity/next moment) - rebinds related records from source -> target: - moments - ideas - reminders - source links - shared message history - unresolved inbox candidate IDs - enqueues sync envelopes for merged target + source delete + rebounded related entities - Added merge action in People UX: - `lib/features/people/people_view.dart` - new `Merge` / `Merge duplicates` action in People header - duplicate-name detection and merge dialog for selecting source/target - Share destination deep-link polish: - `lib/features/share_intake/whatsapp_share_listener.dart` - initial app launch from share intent now auto-opens destination context: - `Share Inbox` for unresolved imports - `People` with selected imported profile for resolved imports - snackbar action updated to `Open Profile` / `Open Inbox` - `lib/features/settings/settings_view.dart` - simulated share snackbar now offers direct navigation to profile/inbox - Added merge test coverage: - `test/features/local/local_repository_test.dart` - verifies merge rebinding across related records ## Latest Milestone (2026-02-18): Stable Share Fingerprints + Near-Match Conflict Detection Strengthened WhatsApp identity mapping to reduce accidental mis-links: - Added stable source fingerprint strategy: - `lib/features/local/local_models.dart` - `SourceProfileLink` now stores optional `sourceFingerprint` - `SharedInboxEntry` also keeps the same fingerprint when unresolved - fingerprint is derived from stable source identifiers (`sourceApp` + source user/thread ids) - Updated ingest matching order: - `lib/features/local/local_repository.dart` - fingerprint match is now the primary lookup path - backward-compatible fallback to existing user/thread/display matching - legacy links without stored fingerprint still match by derived fingerprint - Added near-match conflict handling: - new inbox reason: `nearProfileConflict` - fuzzy name similarity heuristic (edit-distance + containment) now detects likely profile conflicts and routes to Share Inbox for explicit user choice - Updated Share Inbox UI reason labels: - `lib/features/share_intake/share_inbox_view.dart` - Added test coverage: - `test/features/local/local_repository_test.dart` - verifies: - fingerprint persistence on source links - near-match conflict queueing - follow-up routing succeeds via fingerprint after manual resolution ## Latest Milestone (2026-02-18): Share Inbox Conflict Review UX Upgraded unresolved-share resolution from a basic picker to guided review: - Added conflict review modal in Share Inbox: - `lib/features/share_intake/share_inbox_view.dart` - new flow: - tap `Review & Match` - inspect side-by-side comparison: - incoming share identity panel (sender, source ids, fingerprint, message) - selected profile panel (name, relationship, tags/location, confidence) - review candidate list with confidence percentages and suggestion marker - confirm explicit link with `Confirm Match` - Added confidence scoring + rationale hints: - combines normalized-name similarity, prior candidate suggestion, and source identity signals into a 1-99% confidence display - shows short rationale bullets to make matching decisions transparent - Added widget coverage: - `test/features/share_intake/share_inbox_view_test.dart` - verifies conflict review dialog opens and confirm mapping resolves inbox item ## Latest Milestone (2026-02-18): Share Inbox Resolution Flow Added manual resolution UX and data paths for ambiguous/unidentifiable WhatsApp shares: - Introduced unresolved-share queue model: - `lib/features/local/local_models.dart` - new `SharedInboxEntry` + `SharedInboxReason` - persisted in `LocalDataState.sharedInbox` - Extended ingest result + repository APIs: - `lib/features/local/local_repository.dart` - `SharedMessageIngestResult` now reports imported vs queued-for-resolution - ambiguous/missing-identity shares are routed to inbox instead of forced auto-link - added resolution actions: - resolve inbox item to existing profile - resolve inbox item by creating a new profile (with optional location) - dismiss inbox item - Added dedicated Share Inbox view: - `lib/features/share_intake/share_inbox_view.dart` - shows unresolved items, suggested matches, create-profile dialog, dismiss - Wired entry points: - `lib/features/share_intake/whatsapp_share_listener.dart` - snackbar now routes unresolved imports to Share Inbox - `lib/features/settings/settings_view.dart` - added `Open Share Inbox` action and updated simulation feedback - Added tests: - `test/features/local/local_repository_test.dart` - unresolved queue behavior - resolve-to-existing-profile flow - `test/features/share_intake/share_inbox_view_test.dart` - inbox render + dismiss behavior ## Latest Milestone (2026-02-17): WhatsApp Share Intake + Profile Quick Actions Implemented the next major usage-flow slice focused on capture-to-action speed: - WhatsApp share intake pipeline (MVP): - `lib/features/share_intake/whatsapp_share_listener.dart` - `lib/features/share_intake/whatsapp_share_parser.dart` - listener now ingests shared WhatsApp text into local state while authenticated - parser resolves sender-prefixed message shapes (including timestamped export line format) - Source identity linking for repeat routing: - `lib/features/local/local_models.dart` - `lib/features/local/local_repository.dart` - added persisted source->profile link records and shared-message history - repeated shares with same source identity resolve to the same profile - Auto profile creation on first unresolved share: - `LocalRepository.ingestSharedMessage(...)` - creates profile (when needed), writes/updates source link, and appends WhatsApp moment capture to that profile history - Profile model enhancement: - added optional `location` field on `PersonProfile` - wired into serialization, repository payload mapping, and People editor UI - People context quick-add: - `lib/features/people/people_view.dart` - profile detail now includes `+` quick actions: - Add Capture - Add Note - Add Idea - Add Reminder - each action is person-scoped and writes directly to local state - Settings support for dev/testing: - `lib/features/settings/settings_view.dart` - added `Simulate WhatsApp Share` flow to test ingestion without OS share UI - App bootstrap wiring: - `lib/main.dart` wraps authenticated shell with `WhatsAppShareListener` - runtime flag in `lib/core/config/app_config.dart`: `ENABLE_WHATSAPP_SHARE_INTAKE` - Added tests: - `test/features/share_intake/whatsapp_share_parser_test.dart` - `test/features/local/local_repository_test.dart` share-ingest/link reuse case - updated migration expectation in `test/features/local/storage/local_repository_hive_migration_test.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-17): Interactive Relationship Graph + Person Insights Drilldown Upgraded the dashboard graph from static visualization to interactive navigation: - Graph interactions: - `lib/features/dashboard/dashboard_view.dart` - graph area now supports: - pan by drag gesture - pinch-to-zoom scaling - long-press on person bubbles - Long-press drilldown behavior: - opens a dedicated person insights page with back navigation to the graph - page sections are category-separated: - Ideas - Promotions & Signals - Moments - Reminders - Tasks & Follow-Ups - includes profile summary (relationship type, affinity, tags, notes) - Added interaction test coverage: - `test/features/dashboard/dashboard_graph_interactions_test.dart` - verifies: - graph contains interactive viewer - long-press on a person node opens insights screen - back button returns to graph Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-17): Obsidian-Style Relationship Graph on Dashboard Reworked the initial Dashboard section to lead with a relationship network visual: - Replaced the old top summary-only start with a graph-first hero: - `lib/features/dashboard/dashboard_view.dart` - new `Relationship Graph` card renders: - center `YOU` node - surrounding person bubbles from local people data - custom-painted connection edges (radial + mesh links) - relationship-type icon badges + color-coded edge/node styling - Added relationship-type visual mapping for labels such as: - spouse, fiance, partner/girlfriend/boyfriend, friend, bestie, family, sister, brother, fallback connection - Added adaptive layout behavior: - compact/mobile and wide/web radius/node limits - hidden-node indicator for large lists - relationship legend chips under the graph - Preserved existing dashboard flow below the graph: - stats cards and `Next Moves` tasks remain available Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-17): Connectivity Event-Driven Background Sync Implemented online-transition-triggered sync so background sync reacts faster when network returns: - Added connectivity stream support to reachability abstraction: - `lib/core/network/reachability/network_reachability.dart` - new `watch(...)` API for reachability change events - Added IO implementation using connectivity plugin + internet probe: - `lib/core/network/reachability/network_reachability_io.dart` - `connectivity_plus` integrated as transport signal source - Updated background runner: - `lib/features/sync/sync_background_runner.dart` - now listens for offline -> online transitions and triggers immediate sync - keeps existing startup/resume/periodic behavior - Added coverage: - `test/features/sync/sync_background_runner_test.dart` - verifies transition-trigger behavior and avoids duplicate triggers when already online - Updated setup docs: - `docs/SETUP.md` background sync section now includes connectivity-return behavior note Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-17): Sync Repair Test Stability + Mobile CRUD/Sync Flow Coverage Completed two test-depth upgrades: - Stabilized rejected-sync repair widget flow: - `test/features/sync/sync_rejection_repair_flow_test.dart` - fixed viewport-aware assertions/actions for `ListView` content by scrolling before interacting with rejection controls - verifies rejected item -> open related repair screen -> save -> local state mutation applied - Added mobile-first integration-style flow: - `test/features/app/app_mobile_crud_sync_flow_test.dart` - covers compact-shell path: sign-in -> People -> add person -> sync queue envelope asserted -> open Sync from `More` menu - validates both UI behavior and queued backend envelope side effect in `SyncQueueRepository` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Direct Entity Repair Forms From Sync Implemented deep-link repair forms for rejected sync items: - Updated `lib/features/sync/sync_view.dart`: - `Open Related Screen` now routes into dedicated entity-specific repair forms, not just general list screens - supported entity repair forms: - `person` - `capture` - `giftIdea`/`eventIdea` - `reminderRule` - each form updates local state via `LocalRepository` directly - focus banner keeps exact `entityType:entityId` context with copy action - Added widget flow coverage: - `test/features/sync/sync_rejection_repair_flow_test.dart` - verifies reject -> open repair form -> save -> local state updated Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Notification Tap Routing Implemented initial in-app routing for reminder notification taps: - Added notification intent bus: - `lib/features/reminders/scheduling/reminder_notification_intent_bus.dart` - Wired local notifications scheduler to emit tap payloads: - `lib/features/reminders/scheduling/reminder_scheduler_local_notifications.dart` - Added app-level intent listener wrapper: - `lib/features/reminders/scheduling/reminder_notification_listener.dart` - opens reminder management screen when a reminder notification is tapped - Mounted listener in authenticated app shell path: - `lib/main.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Sync Rejection Repair Navigation Added direct repair navigation from rejected sync items: - Updated `lib/features/sync/sync_view.dart`: - each rejected item now includes `Open Related Screen` - routes user to a relevant maintenance screen based on entity type: - `person` -> People - `capture` -> Moments - `giftIdea`/`eventIdea` -> Ideas - `reminderRule` -> Reminders - keeps existing payload inspect + requeue/dismiss actions - opened repair screen now shows exact `entityType:entityId` focus banner Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Notification Permission UX Added runtime notification permission flow through the product UI: - Extended reminder scheduler interface: - `lib/features/reminders/scheduling/reminder_scheduler.dart` - new `requestPermissions()` API - Implemented permission handling in local notifications scheduler: - `lib/features/reminders/scheduling/reminder_scheduler_local_notifications.dart` - Android: `requestNotificationsPermission()` - iOS/macOS: `requestPermissions(alert/badge/sound)` - Added settings action: - `lib/features/settings/settings_view.dart` - `Request Notification Access` button with result feedback snackbar - Added test coverage: - `test/features/reminders/scheduling/reminder_scheduler_local_notifications_test.dart` - verifies permission delegation behavior Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Local Notifications Reminder Runtime Implemented reminder delivery with local notifications runtime integration: - Added dependency: - `flutter_local_notifications` - Added concrete scheduler: - `lib/features/reminders/scheduling/reminder_scheduler_local_notifications.dart` - `LocalNotificationsReminderScheduler` - reconciles local reminders into platform notification schedule - supports recurring cadence mapping: - daily -> `DateTimeComponents.time` - weekly -> `DateTimeComponents.dayOfWeekAndTime` - monthly -> `DateTimeComponents.dayOfMonthAndTime` - web/linux gracefully no-op (scheduling fallback) - Provider wiring now defaults to local notifications scheduler: - `lib/features/reminders/scheduling/reminder_scheduler_provider.dart` - gated by `ENABLE_LOCAL_NOTIFICATIONS` - Added config flag: - `lib/core/config/app_config.dart` -> `enableLocalNotifications` - Added tests: - `test/features/reminders/scheduling/reminder_scheduler_local_notifications_test.dart` - Updated setup docs: - `docs/SETUP.md` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Sync Rejection Payload Inspection Improved conflict handling UX in Sync view: - Updated `lib/features/sync/sync_view.dart`: - each rejected mutation now supports payload inspect/expand - shows formatted local envelope payload JSON for failed mutations when available - keeps requeue/dismiss actions for operator control Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Reachability-Gated Auto Sync Added connectivity-aware gating to background sync auto-triggers: - Added network reachability abstraction: - `lib/core/network/reachability/network_reachability.dart` - `lib/core/network/reachability/network_reachability_provider.dart` - platform impls: - `network_reachability_io.dart` - `network_reachability_web.dart` - `network_reachability_stub.dart` - Integrated into background sync runner: - `lib/features/sync/sync_background_runner.dart` - auto-triggers now skip when reachability gate reports offline (REST mode) - fake backend mode bypasses reachability gate - Extended sync trigger controller: - `lib/features/sync/sync_auto_trigger_controller.dart` - added trigger gate callback support - Added tests: - `test/features/sync/sync_auto_trigger_controller_test.dart` - validates denied-trigger behavior Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): App Session Flow Test Coverage Added higher-level widget coverage for a core authenticated user journey: - New test: - `test/features/app/app_auth_flow_test.dart` - validates end-to-end path: - sign-in screen shown by default - sign-in transitions into app shell - navigate to settings and sign out - sign-out returns to sign-in screen - Uses deterministic in-memory overrides for token, local data store, sync state store, and backend gateway fake. Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Rejected-Mutation Requeue Flow Extended sync conflict handling so rejected mutations can be retried after local fixes: - Expanded sync state model: - `lib/features/sync/sync_state.dart` - stores `lastRejectedChanges` alongside rejection reasons - Updated sync queue repository: - `lib/features/sync/sync_queue_repository.dart` - captures rejected envelopes during push - adds `requeueRejectedChanges()` to move rejected envelopes back into pending queue - keeps clear/dismiss behavior for rejected metadata - Updated sync UX: - `lib/features/sync/sync_view.dart` - rejected section now includes: - entity context (`entityType:entityId`) - `Requeue` action for manual retry flow - existing `Dismiss` action - Added/updated tests: - `test/features/sync/sync_queue_repository_test.dart` - verifies rejected envelope capture and requeue behavior Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Sync Auto-Trigger Backoff Improved sync trigger resilience for repeated failures: - Updated `lib/features/sync/sync_auto_trigger_controller.dart`: - tracks consecutive sync failures - applies exponential cooldown backoff before auto-trigger retries - resets backoff after a successful sync run - Added coverage: - `test/features/sync/sync_auto_trigger_controller_test.dart` - validates cooldown behavior after failures Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Reminder Delivery Interface Wiring Implemented the notification scheduling boundary and connected it to local data state transitions: - Added reminder scheduler abstraction: - `lib/features/reminders/scheduling/reminder_scheduler.dart` - `ReminderScheduler` + `NoopReminderScheduler` - Added provider wiring: - `lib/features/reminders/scheduling/reminder_scheduler_provider.dart` - Integrated schedule reconciliation with local repository: - `lib/features/local/local_repository.dart` - reconcile runs on repository boot and each persisted state update - failures are swallowed to preserve offline-first behavior - Added tests: - `test/features/local/local_repository_test.dart` now verifies reminder schedule reconciliation calls - Updated setup docs: - `docs/SETUP.md` now documents reminder scheduler scaffold Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Sync Hardening + CI Baseline Implemented the next production-readiness slice: - Added lifecycle-aware background sync runner: - `lib/features/sync/sync_auto_trigger_controller.dart` - `lib/features/sync/sync_background_runner.dart` - mounted while authenticated in `lib/main.dart` - behavior: one trigger on app startup, trigger on app resume, periodic sync ticks (default every 180s), cooldown/concurrency guarded - Added runtime config flags: - `ENABLE_BACKGROUND_SYNC` (default `true`) - `BACKGROUND_SYNC_INTERVAL_SECONDS` (default `180`) - in `lib/core/config/app_config.dart` - Added user-facing rejected mutation handling: - `lib/features/sync/sync_queue_repository.dart` -> `clearRejections()` - `lib/features/sync/sync_view.dart` now renders a `Rejected Changes` section with dismiss action - Added tests: - `test/features/sync/sync_auto_trigger_controller_test.dart` - `test/features/sync/sync_queue_repository_test.dart` rejection clear case - Added CI workflow: - `.github/workflows/flutter_ci.yml` (`flutter pub get`, `flutter analyze`, `flutter test`) - Updated setup docs: - `docs/SETUP.md` with background sync flags Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ### Open Phases (Current) 1. Reminder delivery runtime integration: - implemented for Android/iOS/macOS/windows with web/linux safe fallback. - permission UX implemented in settings. - notification tap routing implemented for reminder screen handoff. 2. Conflict resolution UX: - reject/requeue + payload inspect + direct entity repair forms implemented. - future enhancement: auto-select and highlight the exact row in main feature screens after save. 3. Sync trigger maturity: - implemented via reachability gating + online-transition event triggers. - future: add richer platform-specific network quality awareness if needed. 4. Test depth: - expanded with mobile compact CRUD+sync flow and rejected-repair flow. - future: add desktop + web-specific end-to-end sync error/retry scenario. ## Latest Milestone (2026-02-15): Sync Queue Storage Adapter + Hive Migration Extended the storage abstraction approach to sync queue persistence: - Added sync storage abstraction: - `lib/features/sync/storage/sync_state_store.dart` - Added concrete sync stores: - `lib/features/sync/storage/sync_state_store_shared_prefs.dart` - `lib/features/sync/storage/sync_state_store_hive.dart` - `lib/features/sync/storage/sync_state_store_in_memory.dart` - provider wiring: `lib/features/sync/storage/sync_state_store_provider.dart` - Refactored sync queue repository: - `lib/features/sync/sync_queue_repository.dart` - no direct `shared_preferences` dependency - store-agnostic read/write path via `SyncStateStore` - one-time migration path from legacy shared prefs sync payload into Hive - Updated tests to use explicit in-memory store overrides where deterministic: - `test/features/sync/sync_queue_repository_test.dart` - `test/features/sync/sync_coordinator_test.dart` - `test/features/local/local_repository_test.dart` - `test/features/responsive/responsive_views_smoke_test.dart` - Added migration coverage: - `test/features/sync/storage/sync_queue_hive_migration_test.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Local Store Adapter Layer (DB Migration Scaffold) Implemented a persistence boundary so local app state is no longer tightly coupled to `shared_preferences` internals: - Added local persistence abstraction: - `lib/features/local/storage/local_data_store.dart` - `LocalDataStore` + `LocalDataRecord` - Added concrete store implementations: - `lib/features/local/storage/local_data_store_shared_prefs.dart` - `lib/features/local/storage/local_data_store_hive.dart` - `lib/features/local/storage/local_data_store_in_memory.dart` - provider wiring: `lib/features/local/storage/local_data_store_provider.dart` - Refactored local repository to use store adapter: - `lib/features/local/local_repository.dart` - build/read/write/migration paths now go through `LocalDataStore` - schema migration hook retained and now store-agnostic - Added config flag for local backend selection: - `lib/core/config/app_config.dart` -> `USE_HIVE_LOCAL_DB` - default now set to Hive (`true`) - Added one-time migration path from legacy shared prefs into Hive: - `lib/features/local/local_repository.dart` - when Hive is active and empty, legacy payload is imported then cleared - Added setup docs for switching local persistence backend: - `docs/SETUP.md` - Added migration/storage tests: - `test/features/local/storage/local_data_store_shared_prefs_test.dart` - `test/features/local/storage/local_repository_store_override_test.dart` - `test/features/local/storage/local_repository_hive_migration_test.dart` This prepares the app for a true DB migration path while keeping existing UX and tests stable. ## Latest Milestone (2026-02-15): Cross-View Responsive Pass Audited and adjusted the main product views for mobile-first behavior while preserving desktop/web UX: - Updated responsive behavior and compact spacing for: - `lib/features/dashboard/dashboard_view.dart` - `lib/features/moments/moments_view.dart` - `lib/features/ideas/ideas_view.dart` - `lib/features/reminders/reminders_view.dart` - `lib/features/signals/signals_view.dart` - `lib/features/settings/settings_view.dart` - `lib/features/sync/sync_view.dart` - `lib/features/auth/sign_in_view.dart` - `lib/features/people/people_view.dart` - Removed narrow-width overflow hotspots: - stacked compact headers/actions instead of single fixed rows - converted key action rows to `Wrap`/column where needed - improved list-item text constraints (`maxLines`, `ellipsis`) - made editor dialogs width-safe using max-width constraints Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ### Breakpoint QA follow-up Ran targeted responsive smoke checks for: - iPhone SE (`320x568`) - iPhone Pro Max (`430x932`) - iPad portrait (`768x1024`) - Web/desktop (`1280x800`) Follow-up fixes from QA: - `lib/features/auth/sign_in_view.dart` - added scroll-safe container for short-height screens to prevent vertical overflow - `lib/features/home/app_shell.dart` - made sidebar brand text ellipsize-safe to prevent horizontal overflow - Added automated responsive regression suite: - `test/features/responsive/responsive_views_smoke_test.dart` - validates `AppShell` + all primary views across the breakpoints above ## Latest Milestone (2026-02-15): People Mobile Layout Fix Fixed the `People` view so it is mobile-friendly and no longer overflows on iOS narrow screens: - `lib/features/people/people_view.dart` - added responsive breakpoint logic (`LayoutBuilder`) - desktop keeps split-pane list/detail layout - mobile now uses a single-column flow (header, people list, selected person detail) - improved text safety with `maxLines`/`ellipsis` and compact detail action row Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Sync Queue + Orchestrator Implemented the next sync phase so local mutations are now queued and synced through a real coordinator flow: - Added persisted sync queue state: - `lib/features/sync/sync_state.dart` - `lib/features/sync/sync_queue_repository.dart` - stores pending `ChangeEnvelope`s, cursor, last attempt/sync, and rejection info - Added sync orchestration service: - `lib/features/sync/sync_coordinator.dart` - supports `pushPending`, `pullRemote`, and `syncNow` - keeps offline-first behavior by recording failures without blocking local usage - Wired local CRUD to sync envelopes: - `lib/features/local/local_repository.dart` - all person/moment/idea/reminder mutations now enqueue typed change envelopes - added `applyRemoteChanges(...)` to apply pulled backend envelopes into local state - Upgraded Sync UI: - `lib/features/sync/sync_view.dart` - now shows queue/cursor/error status and provides real sync actions (sync/push/pull/clear) - Added sync tests: - `test/features/sync/sync_queue_repository_test.dart` - `test/features/sync/sync_coordinator_test.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Auth Session UX Implemented app-level authentication/session flow so navigation is now session-aware: - Added session lifecycle controller: - `lib/features/auth/session_controller.dart` - bootstraps from token store on app startup - supports sign-in, sign-out, and profile refresh - enforces offline-safe sign-out (always clears local session) - Added sign-in experience: - `lib/features/auth/sign_in_view.dart` - supports Password / Magic Link / OIDC request shapes - shows mode status (Fake/REST) - Updated app bootstrap: - `lib/main.dart` now gates between `SignInView` and `AppShell` based on session state - Updated settings with session actions: - `lib/features/settings/settings_view.dart` - shows current identity and provides `Sign Out` + `Refresh Profile` - Added auth tests: - `test/features/auth/session_controller_test.dart` - updated `test/widget_test.dart` for sign-in default route Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Ideas + Reminders Phase Implemented the next Phase 2 slice by introducing Ideas and Reminder Rules as first-class local features. - Expanded local domain/state: - `IdeaType`, `ReminderCadence`, `RelationshipIdea`, `ReminderRule` - `LocalDataState` now persists `ideas` and `reminders` - Expanded local repository CRUD: - ideas: add/update/archive/delete - reminders: add/update/toggle/delete - people deletion now cascades to moments/ideas/reminders - Added new feature views: - `lib/features/ideas/ideas_view.dart` - `lib/features/reminders/reminders_view.dart` - Updated shell navigation: - desktop sidebar now includes `Ideas` and `Reminders` - mobile keeps compact primary tabs and exposes `Ideas`/`Reminders` under `More` - Expanded local repository tests for new CRUD surface: - `test/features/local/local_repository_test.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Latest Milestone (2026-02-15): Local Persistence + CRUD Baseline Implemented the first real local data foundation pass (replacing static demo-only reads): - Added persisted local state using `shared_preferences`: - `lib/features/local/local_repository.dart` - schema key/version scaffold + seed fallback + persistence logic - Upgraded local models with serialization/copy methods: - `lib/features/local/local_models.dart` - Added local CRUD operations: - people: add, edit, delete - moments: add, delete - dashboard tasks: done/undone toggle - Wired views to async persisted state: - `DashboardView`, `PeopleView`, `MomentsView` - Updated signals fallback to read from persisted local state: - `lib/features/signals/signals_controller.dart` - Added local repository tests: - `test/features/local/local_repository_test.dart` Validation for this milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Workflow Rule - After every sensible change, the final step is a Git commit with a clear message. - Keep changes in small, logical commits so future sessions can revert or continue safely. ## Latest Milestone (2026-02-15): Product UI Flow Implemented The app now has a real multi-screen user experience on macOS (and other platforms), not just a placeholder screen. Delivered UI architecture: - App shell with responsive navigation: - desktop: left sidebar + content pane - compact: bottom navigation (reduced to 4 primary tabs) - compact secondary actions (`Sync`, `Settings`) moved into a `More` menu to avoid tab overload on iOS/mobile - Views implemented: - Dashboard - People - Moments - Signals - Sync - Settings - Reference-aligned visual style: - WorkSans typography (copied from the UI reference assets) - soft gradients, rounded glass-like cards, accent palette similar to the reference repo - Offline-safe UX: - fake backend mode now defaults to `true` unless overridden via `--dart-define=USE_FAKE_BACKEND=false` - signals view gracefully falls back to local recommendations when backend calls fail Key new files: - `lib/features/home/app_shell.dart` - `lib/features/dashboard/dashboard_view.dart` - `lib/features/people/people_view.dart` - `lib/features/moments/moments_view.dart` - `lib/features/signals/signals_controller.dart` - `lib/features/signals/signals_view.dart` - `lib/features/sync/sync_view.dart` - `lib/features/settings/settings_view.dart` - `lib/features/shared/frosted_card.dart` - `lib/features/local/local_models.dart` - `lib/features/local/local_repository.dart` - `assets/fonts/WorkSans-*.ttf` Other updates: - `lib/main.dart` now launches the new app shell. - `lib/core/config/app_theme.dart` updated to a stronger visual system. - `pubspec.yaml` includes local WorkSans font family. - `test/widget_test.dart` updated for the new flow. Validation status after UI milestone: - `flutter analyze` -> pass - `flutter test` -> pass ## Current Gap Analysis (2026-02-15) ### What we have now - Backend gateway architecture with REST + fake implementations, typed DTOs, retries, refresh, and tests. - OpenAPI + ADR documentation baseline. - Product UI shell with responsive desktop/mobile navigation and key views. - Offline-safe default behavior with fake backend mode. ### What is still missing - Local database persistence and repository wiring (currently demo in-memory data). - Full CRUD flows (create/edit/delete) for people, captures, reminder rules, gift/event ideas. - Auth UX flows (sign-in, session-expired recovery, sign-out confirmation). - Conflict policy UI and merge/resolution UX for rejected remote/local mutations. - Notifications/reminders delivery and scheduling integration. - End-to-end and integration tests for app flows beyond unit/widget basics. ## Implementation Plan (Next) ### Phase 1: Data Foundation 1. Introduce local DB schema + repository interfaces for core entities. 2. Replace `LocalRepository` demo data with persisted data access. 3. Add migration/test coverage for DB reads/writes. ### Phase 2: Core Product Flows 1. Build People CRUD and detail editing screens. 2. Build Moments/Captures create-and-list workflows. 3. Add Ideas and Reminder Rule management. ### Phase 3: Sync + Auth UX 1. Add background sync scheduling/triggers (app start, manual retry, connectivity hooks). 2. Add conflict handling strategy and user-facing UI states for rejected mutations. 3. Add session-expired recovery UX and guarded routes for protected actions. ### Phase 4: Quality + Release Readiness 1. Add CI for analyze/test and optional build smoke checks. 2. Expand widget/integration tests for primary user journeys. 3. Add observability hooks and production hardening pass. ## Current Status The Flutter app now includes a production-grade backend integration scaffold with an offline-first posture: - Local/offline UX remains primary; backend is optional for core usage. - A transport-agnostic gateway interface is in place. - REST implementation with auth refresh/retry/error mapping is implemented. - Deterministic fake gateway is available for local/dev without backend. - OpenAPI contract + ADR + setup docs + unit tests are in place. - `flutter analyze` and `flutter test` are passing. ## What Was Implemented ### 1. Core Config + Theme Shell - Added backend config with build-time + runtime override: - `lib/core/config/app_config.dart` - Added lightweight app theme aligned with reference style direction: - `lib/core/config/app_theme.dart` - Updated app entry shell and Riverpod root: - `lib/main.dart` ### 2. Network Foundation Implemented a Dio stack with request metadata, auth, refresh, retry, and error mapping: - `lib/core/network/dio_factory.dart` - `lib/core/network/network_constants.dart` - `lib/core/network/backend_exception.dart` - `lib/core/network/dio_error_mapper.dart` - Interceptors: - `lib/core/network/interceptors/request_metadata_interceptor.dart` - `lib/core/network/interceptors/auth_interceptor.dart` - `lib/core/network/interceptors/refresh_token_interceptor.dart` - `lib/core/network/interceptors/retry_interceptor.dart` Behavior implemented: - Adds per-request `X-Request-Id`. - Adds `Authorization: Bearer ` when session exists. - On `401`, attempts one refresh (`/v1/auth/refresh`) then retries original request. - If refresh fails, session is cleared and `AuthExpiredException` surfaced. - Retry policy (max 2 retries, exponential backoff): - Retry transient failures (`timeouts`, `connection`, `5xx`). - Safe methods (`GET/HEAD/OPTIONS`) retry automatically. - Non-idempotent endpoints only retry when `Idempotency-Key` exists. - Maps Dio failures into typed backend exceptions with metadata. ### 3. Auth Token Storage - Interface: - `lib/core/auth/token_store.dart` - Production implementation: - `lib/core/auth/secure_token_store.dart` - Test/dev implementation: - `lib/core/auth/in_memory_token_store.dart` ### 4. Backend Integration Layer Domain boundary and implementations: - Interface: - `lib/integrations/backend/backend_gateway.dart` - REST: - `lib/integrations/backend/backend_gateway_rest.dart` - Fake: - `lib/integrations/backend/backend_gateway_fake.dart` - Optional Riverpod providers: - `lib/integrations/backend/backend_gateway_provider.dart` - Sync validation: - `lib/integrations/backend/sync_envelope_validator.dart` Implemented methods: - sign-in/out + me - sync pull/push (with idempotency key on push) - signals feed + ack - upload init + download URL scaffold Offline-first hardening: - `signOut()` clears local token store in `finally`, even when network call fails. ### 5. Typed Models (Freezed + JSON) - `lib/integrations/backend/models/backend_models.dart` - Generated files: - `backend_models.freezed.dart` - `backend_models.g.dart` Includes DTOs/enums for: - Auth (`SignInRequest`, `AuthSession`, refresh request/response, `UserProfile`) - Sync (`ChangeEnvelope`, push/pull requests/results, ack/rejection) - Signals (`SignalsFeed`, `SignalItem`, ack request/action) - Uploads (`UploadInitRequest/Result`, `DownloadUrl`) ### 6. Contract + Architecture Docs - OpenAPI spec: - `docs/api/openapi.yaml` - ADR: - `docs/ADR/0002-backend-protocol-rest-openapi.md` - Setup instructions: - `docs/SETUP.md` - Previous concise checklist log: - `docs/PROGRESS.md` ### 7. Tooling + Quality - Strict analyzer/lints: - `analysis_options.yaml` - Dependencies added in `pubspec.yaml`: - runtime: `dio`, `freezed_annotation`, `json_annotation`, `flutter_secure_storage`, `uuid`, `clock`, `flutter_riverpod` - dev: `freezed`, `json_serializable`, `build_runner`, `mocktail` ### 8. Test Coverage Added Core network and model tests: - `test/core/network/token_refresh_interceptor_test.dart` - `test/core/network/dio_error_mapper_test.dart` - `test/core/network/retry_interceptor_test.dart` - `test/integrations/backend/backend_models_serialization_test.dart` - `test/integrations/backend/sync_envelope_validator_test.dart` - helper adapter: `test/helpers/queue_http_client_adapter.dart` - updated smoke widget test: `test/widget_test.dart` ## Verification Snapshot Most recent local checks: - `flutter analyze` -> pass - `flutter test` -> pass ## Known Gaps / Intentional Scope Limits - No realtime transport yet (SSE/WebSocket intentionally deferred). - No backend-generated client code from OpenAPI yet. - No persistence layer wiring to local DB yet (gateway is ready to plug in). - No UI features using backend gateway yet beyond app shell readiness. - Fake gateway data is deterministic sample content, not scenario-configurable yet. ## Handoff Plan For Next Agent Sessions ### Priority 1: Repository Hygiene + CI 1. Add CI workflow for `flutter analyze` + `flutter test`. 2. Add branch protections / PR template if desired. 3. Keep generated files committed policy explicit in README. ### Priority 2: Local DB Integration (Offline-First Core) 1. Define local domain entities and repositories for `person`, `capture`, `giftIdea`, `eventIdea`, `tag`, `reminderRule`. 2. Map local mutations to `ChangeEnvelope` and enqueue for sync. 3. Create sync orchestrator service that: - pushes pending local mutations, - applies pull changes with conflict policy, - stores/advances cursor. 4. Add tests for conflict handling and cursor progression. ### Priority 3: App Wiring + UX 1. Replace local demo repository data with actual local DB repositories. 2. Add create/edit flows for people, captures, reminders, and ideas (forms + validation). 3. Add session state and auth entry screens (sign-in, expired-session recovery). 4. Add explicit offline badges and sync conflict resolution UI. ### Priority 4: Security + Hardening 1. Add central log redaction helper for headers/bodies. 2. Add token-expiry pre-check strategy (optional proactive refresh). 3. Evaluate secure storage behavior on web target and fallback policy. 4. Add tests for concurrent 401 refresh race conditions. ### Priority 5: API Evolution 1. Version strategy in OpenAPI (`/v1` already set). 2. Define backend error payload schema formally in OpenAPI. 3. Add capabilities endpoint for future realtime negotiation. 4. Add SSE/WebSocket design ADR when backend is ready. ## How To Continue Quickly 1. Run: `flutter pub get` 2. If models change: `dart run build_runner build --delete-conflicting-outputs` 3. Validate: `flutter analyze && flutter test` 4. Start from: - gateway surface: `lib/integrations/backend/backend_gateway.dart` - sync transport: `lib/integrations/backend/backend_gateway_rest.dart` - contract: `docs/api/openapi.yaml` ## Important Notes - Current default base URL is placeholder (`https://api.example.com`). - Use fake mode for local iteration: - `flutter run --dart-define=USE_FAKE_BACKEND=true` - Build-time URL override: - `flutter run --dart-define=BACKEND_BASE_URL=`