focus-flow/Codex Documentation/Completed Plans/Persistence Plan 1 - SQLite Runtime Persistence/PERSISTENCE_PLAN_1_SUMMARY.md

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

  1. The project is SQLite-first and V1 has no MongoDB runtime.
  2. packages/scheduler_persistence_sqlite already contains a Drift database and repository implementations for task/project/locked-time/settings/snapshot persistence.
  3. The Flutter app still starts through DemoSchedulerComposition.seeded() and an InMemoryApplicationUnitOfWork.
  4. The dev runner already passes SCHEDULER_SQLITE_PATH into Flutter as a Dart define, but the Flutter app does not yet consume that path.
  5. V1ApplicationCommandUseCases and GetTodayStateQuery already operate against the public ApplicationUnitOfWork boundary.
  6. Existing command flows write more than tasks: completion can also write task activities, project statistics, and operation records.
  7. 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:

  1. main.dart no longer boots the normal app through static seeded demo data.
  2. App startup opens an on-disk SQLite database at the configured path.
  3. The composition root builds scheduler queries/use cases with a SQLite-backed ApplicationUnitOfWork.
  4. The app creates or loads required owner bootstrap data, including owner settings and a default Inbox/Home project, without inserting arbitrary demo timeline tasks.
  5. Quick capture persists a task to SQLite.
  6. Scheduling a backlog task persists status, durationMinutes, scheduledStart, and scheduledEnd.
  7. Completing and uncompleting a task persist status, actual/completion timestamps, and updated metadata.
  8. Closing and reopening the app against the same database reloads the task, schedule placement, and done/not-done state.
  9. Widgets/controllers still consume read models/controllers and do not contain scheduling, Drift, SQL, or OS path logic.
  10. Temp-file lifecycle tests prove close/reopen persistence.
  11. Existing backend gates and Flutter gates pass, or every unavailable command is documented with the reason.

Non-goals

  1. No UI redesign beyond empty/loading/error text needed for real persisted data.
  2. No new scheduling algorithm behavior.
  3. No manual SQL in Flutter widgets.
  4. No backup/restore UI.
  5. No JSON/CSV export UI.
  6. No calendar sync.
  7. No notifications wiring.
  8. No week/month views.
  9. 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.