# Relationship Saver Progress Log Updated: 2026-02-15 ## 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=`