# Data Model The app stores most relationship data in `LocalDataState`, persisted by `LocalRepository`. ## Aggregate State Source: `lib/app/state/local_data_state.dart` | Field | Type | Meaning | | --- | --- | --- | | `people` | `List` | primary relationship profiles | | `moments` | `List` | interaction and capture history | | `ideas` | `List` | gift, activity, and follow-up ideas | | `reminders` | `List` | local reminder rules | | `tasks` | `List` | dashboard task surface | | `dismissedSignalIds` | `List` | dismissed generated signal IDs | | `sourceLinks` | `List` | external sender/thread to profile mapping | | `sharedMessages` | `List` | imported shares attached to profiles | | `sharedInbox` | `List` | unresolved or ambiguous shares | | `personFacts` | `List` | structured relationship facts | | `importantDates` | `List` | birthdays, anniversaries, events | | `preferenceSignals` | `List` | inferred/confirmed likes/dislikes | | `aiSuggestionDrafts` | `List` | pending AI review items | ## Profile Model Source: `lib/features/people/domain/person_models.dart` `PersonProfile` is the stable profile root. Important fields: - `id`: internal local ID - `name`, `aliases`: local identity only - `relationship`: user-facing relationship category - `affinityScore`: rough closeness score - `nextMoment`: next planned contact or relationship moment - `tags`, `notes`, `location`: user-authored context - `lastUpdatedAt`, `lastInteractedAt`: operational recency fields `SourceProfileLink` maps an external source to a local profile. It is used by share intake to avoid asking again after the user resolves a source identity. ## Share Models Source: `lib/features/share_intake/domain/share_models.dart` `SharedPayload` is normalized input from share sheets, action extensions, or simulation tools. Key timestamp fields: - `createdAt`: when the app created the normalized payload - `receivedAt`: when the app received/imported the share - `sharedMessageDateTime`: best-effort original message timestamp extracted from shared text, or null Use `sharedMessageDateTime` as temporal evidence. Do not assume it exists. Do not infer it from arbitrary content dates such as "birthday is May 20". `CapturedFactDraft` is the review/edit draft before a share becomes a durable fact or important date. `SharedMessageEntry` is the durable record attached to a person. Its `sharedAt` getter prefers `sharedMessageDateTime` when present. `SharedInboxEntry` is unresolved intake. Reasons include missing identity, ambiguous profile match, near-profile conflict, and manual selection needs. ## Facts, Dates, And Signals `PersonFact` is durable structured context: - `type`: note, like, dislike, importantDate, giftIdea, placeIdea, activityIdea, misc - `sourceKind`: manual, shareSheet, or shareInbox - `sharedMessageId` or `inboxEntryId`: provenance - `needsReview` and `isSensitive`: prompt and UI filtering controls `PersonImportantDate` is a dated profile-owned record. It can come from manual input, share review, or AI-assisted extraction. `PersonPreferenceSignal` is inferred or confirmed preference evidence. It tracks first/last seen timestamps, occurrence count, source apps, message IDs, and evidence snippets. ## AI Suggestion Drafts Source: `lib/features/ai_digest/domain/ai_digest_models.dart` AI suggestions are saved as drafts first. Accepted drafts can become local ideas, reminders, or tasks. Dismissed drafts stay in history so future digest prompts can avoid repeats. ## Schema Versioning `LocalRepository` owns `_schemaVersion`. When adding persisted fields: 1. Add nullable/defaulted fields where possible. 2. Update `toJson`, `fromJson`, and `copyWith`. 3. Increase `_schemaVersion` only when a migration has meaning beyond default compatible parsing. 4. Add repository and serialization tests. 5. Verify legacy/missing field behavior. The current `fromJson` patterns are intentionally tolerant for many optional lists and fields. Keep that property.