Restoring Simple Tasks

Every bundle in /home/joe/.hermes/backups/simple-tasks/ is self-describing and verifiable on its own. Do not copy a live WAL database by hand; do not restore a bundle that fails verify.

What a bundle contains

tasks.sqlite3          consistent online snapshot (SQLite backup API)
manifest.json          created_at, health verdict, per-table row counts and SHA-256,
                       source hash, code and unit hashes
code/                  app.py store.py importer.py scheduler.py ops.py
                       calendar_sync.py mcp_cal.py schema.sql index.html RESTORE.md
units/                 simple-tasks*.service / *.timer definitions
RESTORE.md             this file

Retention: 48 hourly auto bundles plus 30 days of dailies.

1. Verify before trusting

cd /home/joe/workspace/simple-tasks
python3 ops.py verify /home/joe/.hermes/backups/simple-tasks/<bundle>.tar.gz

ok: true means every member hash matched the manifest, the database passed quick_check, integrity_check and foreign_key_check, and table counts equal the manifest. Any other result: stop, keep the bundle, do not delete.

2. Rehearse the restore (never into the live path)

DRILL=$(mktemp -d /home/joe/.hermes/cache/scratch/restore.XXXXXX)
python3 ops.py restore-check <bundle>.tar.gz --dest "$DRILL" \
        --live-db /home/joe/.local/share/simple-tasks/tasks.sqlite3

drift_vs_live reports per-table identical flags: differences are expected for tables that changed since the bundle was taken, and are the evidence you use to decide whether restoring would lose newer edits.

Prove the runtime starts from the restored copy alone:

cd "$DRILL/code"
python3 app.py --host 127.0.0.1 --port 8680 --db "$DRILL/tasks.sqlite3" &
curl --fail --silent http://127.0.0.1:8680/api/health
kill %1

{"ok": true, ...} and a rendered All view are the acceptance evidence. Stop the process afterwards; never leave a second listener running.

3. Restore in place

Only with Joe’s decision, and only after step 2 shows no lost edits:

systemctl --user stop simple-tasks.service
cp /home/joe/.local/share/simple-tasks/tasks.sqlite3 \
   /home/joe/.local/share/simple-tasks/tasks.sqlite3.pre-restore
# remove -wal/-shm siblings so the restored file is authoritative
mv <live>-wal <live>-wal.discard 2>/dev/null; mv <live>-shm <live>-shm.discard 2>/dev/null
python3 - <<'PY'
import tarfile, shutil
tarfile.open('<bundle>.tar.gz').extract('tasks.sqlite3', '/tmp/restore')
shutil.copy('/tmp/restore/tasks.sqlite3', '/home/joe/.local/share/simple-tasks/tasks.sqlite3')
PY
systemctl --user start simple-tasks.service
curl --fail --silent http://127.0.0.1:8672/api/health

Then check /api/status: mode, counts and last_backup must be coherent. Keep tasks.sqlite3.pre-restore until Joe has used the app once.

4. What restore cannot bring back

  • Edits made after the bundle was taken (compare drift_vs_live first).
  • Memory: the guarded-memory archive is a separate bundle in /home/joe/.hermes/backups/mnemosyne/ with its own manifest, ledger and receipts. Restoring it without the current suppression ledger re-exposes forgotten facts — follow guarded_memory_ops.py and never drop the ledger.