4.2 KiB
Persistence Plan 1 Summary — SQLite Runtime Persistence
Status: Complete on 2026-07-02. Scope level: XHIGH implementation plan. Primary outcome: The Flutter desktop app persists real task state to an on-disk SQLite database instead of recreating arbitrary demo seed data at launch.
Current repo facts this plan is based on
- The project is SQLite-first and V1 has no MongoDB runtime.
packages/scheduler_persistence_sqlitealready contains a Drift database and repository implementations for task/project/locked-time/settings/snapshot persistence.- The Flutter app still starts through
DemoSchedulerComposition.seeded()and anInMemoryApplicationUnitOfWork. - The dev runner already passes
SCHEDULER_SQLITE_PATHinto Flutter as a Dart define, but the Flutter app does not yet consume that path. V1ApplicationCommandUseCasesandGetTodayStateQueryalready operate against the publicApplicationUnitOfWorkboundary.- Existing command flows write more than tasks: completion can also write task activities, project statistics, and operation records.
- Current SQLite schema files do not yet expose all app-layer repository data needed for durable application semantics across restarts.
Definition of done
Persistence Plan 1 is complete when all of the following are true:
main.dartno longer boots the normal app through static seeded demo data.- App startup opens an on-disk SQLite database at the configured path.
- The composition root builds scheduler queries/use cases with a SQLite-backed
ApplicationUnitOfWork. - The app creates or loads required owner bootstrap data, including owner settings and a default Inbox/Home project, without inserting arbitrary demo timeline tasks.
- Quick capture persists a task to SQLite.
- Scheduling a backlog task persists
status,durationMinutes,scheduledStart, andscheduledEnd. - Completing and uncompleting a task persist
status, actual/completion timestamps, and updated metadata. - Closing and reopening the app against the same database reloads the task, schedule placement, and done/not-done state.
- Widgets/controllers still consume read models/controllers and do not contain scheduling, Drift, SQL, or OS path logic.
- Temp-file lifecycle tests prove close/reopen persistence.
- Existing backend gates and Flutter gates pass, or every unavailable command is documented with the reason.
Non-goals
- No UI redesign beyond empty/loading/error text needed for real persisted data.
- No new scheduling algorithm behavior.
- No manual SQL in Flutter widgets.
- No backup/restore UI.
- No JSON/CSV export UI.
- No calendar sync.
- No notifications wiring.
- No week/month views.
- No Shield/Recovery UX beyond preserving existing backend recovery contracts.
Architecture rule
The SQLite runtime must sit behind the existing application boundaries:
Flutter widgets/controllers
-> app composition/root runtime object
-> V1ApplicationCommandUseCases / GetTodayStateQuery
-> ApplicationUnitOfWork
-> SQLite application repositories / Drift database
The UI may request capture/schedule/done actions. The scheduler core remains the source of truth for schedule state and task transitions.
Important implementation decision
The current scheduler_persistence_sqlite adapter package implements
repository-conformance contracts, but the app command layer needs the richer
scheduler_core application repository surface. This plan therefore adds a
SQLite-backed ApplicationUnitOfWork adapter and the missing app-layer rows it
requires instead of wiring Flutter directly to low-level persistence
repositories.
Recommended default: implement the application unit of work in
packages/scheduler_persistence_sqlite and expose it from sqlite.dart.
Optional stricter boundary: create a small pure-Dart runtime package that owns
SQLite file opening and depends on scheduler_persistence_sqlite; Flutter then
imports only that runtime package. Use this option only if keeping all SQLite
package imports out of apps/focus_flow_flutter/lib/ remains a hard boundary.