# Architecture ## High-Level Shape The app is mid-migration to vertical slices. ```text lib/ main.dart app/ app bootstrap, shell, aggregate repository/state core/ cross-cutting config, auth, networking, LLM, theme features/ product slices integrations/ backend gateway contracts and transport models ``` Canonical feature structure: ```text lib/features// domain/ models and pure rules application/ orchestration, providers, side effects presentation/ screens, widgets, dialogs, UI state data/ slice-owned persistence or adapters, when needed ``` Many files at `lib/features//.dart` are compatibility exports. New implementation should go into the canonical subfolders. ## Runtime Flow 1. `lib/main.dart` initializes Flutter, Sentry/Bugsink, Workmanager, local notifications, Hive, and Riverpod. 2. `RelationshipSaverApp` wires auth gates, share listeners, notification listeners, and global app listeners. 3. `AppShell` owns responsive navigation between Dashboard, People, Moments, Ideas, Reminders, Signals, Sync, and Settings. 4. `LocalRepository` exposes the aggregate `LocalDataState`. 5. Feature views read state through Riverpod providers and call repository operations. 6. Repository writes persist state, reconcile reminder schedules, and enqueue sync envelopes where applicable. ## State And Persistence Boundary `LocalDataState` is the primary local aggregate. It contains: - people - moments - ideas - reminders - dashboard tasks - source links - shared messages - share inbox - person facts - important dates - preference signals - AI suggestion drafts Most product writes should pass through `LocalRepository` because it handles: - local state update - persistent storage write - reminder schedule reconciliation - sync queue enqueue - derived side effects such as preference extraction The sync queue is intentionally separate from the product aggregate and lives in `SyncQueueRepository`. ## Important Providers | Provider | Purpose | | --- | --- | | `localRepositoryProvider` | primary local product state and write boundary | | `syncQueueRepositoryProvider` | pending outbound sync mutations and sync status | | `backendGatewayProvider` | fake vs REST backend transport selection | | `sessionControllerProvider` | fake/REST auth session lifecycle | | `llmConfigProvider` | LLM provider, endpoint, model, and API key state | | `llmDigestConfigProvider` | digest cadence and grounding policy | | `reminderSchedulerProvider` | local notification scheduler abstraction | ## Design Decisions ### Local-first aggregate first The app persists one aggregate snapshot for product state. This is simple, offline-safe, and pragmatic for a prototype. It also means entity-level migrations and partial writes need care. ### Feature-owned models Even though storage is aggregate-based, models live with features. This keeps the migration toward vertical slices open without forcing a storage rewrite. ### Fake backend by default Fake backend mode must stay stable because the product is not yet backend dependent. REST transport exists behind `BackendGatewayRest`. ### Inbox-first share capture Share intake should prefer user review when identity evidence is weak. A wrong automatic profile match is more damaging than an extra inbox step. ### Pseudonymous AI digest Scheduled digest prompts use person tokens and safe summaries. Identifying details stay local. Share timestamps can be included as temporal evidence, but raw shared text and sender identity should not be sent. ### Local notifications as best effort Reminder and digest notifications should not block local data writes. Scheduler errors are intentionally swallowed at the repository boundary. ## Cross-Cutting Rules - Keep `AppConfig` flags as build-time `--dart-define` friendly values. - Add tests around parsing, local repository state changes, and prompt payload shape when changing sensitive flows. - Keep docs aligned with verified code, especially native share and background behavior.