Context
PR #636 performed an irreversible forward-only migration on the user's primary database: dropped detections.workspace_id, deduped predictions, table-rewrote both, backfilled prediction_review. The CHANGELOG describes what changed but does not tell users to back up before upgrading. There is no automatic backup. If anything goes wrong on a particular user's DB (corruption, edge-case data, partial run on power loss), there is no rollback.
The storage-philosophy roadmap (#645) plans seven more migrations of similar shape. Each one is a fresh chance for an unrecoverable failure on a real user's DB.
What to build
On first boot of a new schema version (after the migration runner from #652 lands), before any DDL runs:
- Detect that
schema_version will be advanced this boot.
- Snapshot the current DB to
~/.vireo/backups/vireo.db.pre-v<old>-to-v<new>-<YYYYMMDD-HHMMSS>.bak (or similar). Use SQLite's online backup API (sqlite3.connect(...).backup(...)) so it works on a live WAL-mode DB without races.
- Log the backup path at INFO so users can find it in
~/.vireo/vireo.log.
- Surface it in the UI on first launch ("Database upgraded. Pre-upgrade backup saved at ").
- Cap retention: keep the most recent N (e.g. 3) pre-migration backups and prune older ones, or cap by total size, to avoid silently filling the user's disk.
Behavior on backup failure
If the backup itself fails (disk full, permissions), abort the migration and surface a clear error rather than proceeding without a snapshot. Better to refuse to upgrade than to upgrade unsafely.
Out of scope
- Continuous / scheduled backups (this is not the right place for that).
- Backups before non-schema operations (scan, classify, etc.).
- Cross-machine sync of backups.
Why now
The same reasoning as #652: do this once, before #646 (Phase 1) and the six phases that follow each get their own migration. Cheap insurance, and the moment a Phase 6 cleanup goes wrong on a 100k-photo DB it's the difference between "ugh, restore" and "lost everything."
References
Context
PR #636 performed an irreversible forward-only migration on the user's primary database: dropped
detections.workspace_id, deduped predictions, table-rewrote both, backfilledprediction_review. The CHANGELOG describes what changed but does not tell users to back up before upgrading. There is no automatic backup. If anything goes wrong on a particular user's DB (corruption, edge-case data, partial run on power loss), there is no rollback.The storage-philosophy roadmap (#645) plans seven more migrations of similar shape. Each one is a fresh chance for an unrecoverable failure on a real user's DB.
What to build
On first boot of a new schema version (after the migration runner from #652 lands), before any DDL runs:
schema_versionwill be advanced this boot.~/.vireo/backups/vireo.db.pre-v<old>-to-v<new>-<YYYYMMDD-HHMMSS>.bak(or similar). Use SQLite's online backup API (sqlite3.connect(...).backup(...)) so it works on a live WAL-mode DB without races.~/.vireo/vireo.log.Behavior on backup failure
If the backup itself fails (disk full, permissions), abort the migration and surface a clear error rather than proceeding without a snapshot. Better to refuse to upgrade than to upgrade unsafely.
Out of scope
Why now
The same reasoning as #652: do this once, before #646 (Phase 1) and the six phases that follow each get their own migration. Cheap insurance, and the moment a Phase 6 cleanup goes wrong on a 100k-photo DB it's the difference between "ugh, restore" and "lost everything."
References
~/.vireo/vireo.dbis the path used by the running app (set invireo/app.py)vireo/db.py__init__— where the new logic would slot in (afterconnect, before any DDL)