docs/references/data/app-state-overview.md
app_state is a SQLite-backed key-value table holding durable internal continuity markers — the app's own record of one-time work it has performed (data migration, seeding, one-off setup).
It exists to preserve functional continuity across restarts: losing a value does not lose user data, but makes the app re-run a one-time flow the user has already been through.
Write to app_state only when all three hold:
| Question | Required answer |
|---|---|
| Is this internal app/module state, not a user-facing setting? | Yes |
| Must it survive restarts? | Yes |
| Would losing it make the user re-experience a one-time flow? | Yes |
Otherwise use another system:
| Data | System |
|---|---|
| User-facing setting | PreferenceService |
| Regenerable / silently rebuildable | CacheService |
| Business data from user activity | DataApiService |
| Process-level flag needed before lifecycle | BootConfigService |
src/main/data/db/schemas/appState.ts:
| Column | Type | Notes |
|---|---|---|
key | text, PK | <scope>:<name> (see Key Naming) |
value | text (JSON) | shape owned by the consumer |
description | text, optional | human-readable note on the key's purpose |
createdAt / updatedAt | timestamps | updatedAt doubles as applied-at |
No dedicated service — the owning module reads/writes appStateTable directly through its own DbType handle.
Why none today: every current consumer runs during app startup, at or before the lifecycle's earliest phase, so a lifecycle-managed service would be unavailable to them — preboot migration even supplies its own DB connection. App-state content is inherently startup-stage; no consumer yet needs it during a later lifecycle phase.
Future: if consumers with a confirmed need arise during or after the lifecycle, a shared app-state access service may be introduced then. Do not add one preemptively.
app_state read.<scope>:<name>, where scope identifies the owner module (reuse its loggerService context or service name).Every key currently in app_state. Add a row when introducing a key.
| Key | Owner | Value shape | Notes |
|---|---|---|---|
seed:<name> | SeedRunner | { version: string } | Seeding journal, one row per seeder. See Database Seeding Guide. |
seedRunner:bootstrapCompleted | SeedRunner | { completedAt: number } | Bootstrap-window marker — set after the first fully-successful seeding pass; bootstrap-only seeders never run once present. Done-event key (see Disposability exception): never rename once shipped. |
fileManager:contentMetadataGeneration | FileManager | { version: number } | Trust generation for internal-file size / contentHash; a version change atomically invalidates old hashes before background reconciliation. |
migration_v2_status | MigrationEngine | MigrationStatusValue | Grandfathered exception. Bare key predating the <scope>: convention; serves only the one-time v1→2.0.0 migration and disappears when the migration module is removed after 2.0.x. Do not rename; do not model new keys on it. |
| File | Purpose |
|---|---|
src/main/data/db/schemas/appState.ts | Table schema |
src/main/data/db/seeding/SeedRunner.ts | seed:* owner |
src/main/data/migration/v2/core/MigrationEngine.ts | migration_v2_status owner |
seed:* journal usage