Skip to main content

Local Database

The local database (lib/local-db.ts) is a SQLite cache powered by expo-sqlite. It mirrors a subset of the Convex cloud data for offline access and instant UI rendering. The database file is named commit.db and uses WAL journaling mode for concurrent read performance.

Schema Version 12 (Instance-Dependent Architecture)

The current schema defines two core tables with zero foreign key constraints:
There are no foreign key constraints in this schema. This is intentional. The task_id column in task_instances references local_tasks.id logically but not physically. Orphaned instances (whose parent task was deleted) are first-class citizens that continue to enforce penalties.

Why No Foreign Keys?

The removal of FK constraints was a direct response to a production database corruption bug. The prior schema required toggling PRAGMA foreign_keys on and off around every write: By removing FKs entirely, all PRAGMA toggles were eliminated, and write paths became race-free.

Nuke and Pave Migration Strategy

There are no incremental migrations. If the on-device PRAGMA user_version does not match the current DATABASE_VERSION constant, the entire database is destroyed and rebuilt:
The VACUUM command physically reconstructs the entire database file from scratch. This eliminates ghost WAL boundaries that caused Android’s native CursorWindow to misread corrupted lengths (triggering 124MB OOM allocations on the JVM).

Three Recovery Tiers

1

Surgical Purge

Clears all data rows without dropping tables. Preserves file descriptors so the Kotlin Accessibility Service keeps its database handle alive. Used by Manual Resync.
2

Nuke and Pave

Drops all tables, deploys the unified schema, and runs VACUUM. Used on schema version mismatch or terminal corruption. Requires a full re-sync from Convex (Amnesia Mode).
3

OS-Level Clear

If both tiers above fail, the user must clear the app data through Android Settings. This destroys the SQLite file entirely and forces a fresh install state.