SIGN IN SIGN UP

fix(backup): stop losing custom-agent conversations and corrupting agent stores

`~/.codeg/acp-transcripts` holds the entire conversation text of every
custom ACP agent — the database keeps no copy — and it was packed by
neither the backup nor the restore path, so a round-trip left the
conversation rows in place with every message gone. `turn-timings`,
`backgrounds`, `pets` and `skills` were missing for the same reason:
`core.rs` and `restore.rs` each kept their own hardcoded list and the
two drifted. Both now iterate one `MANAGED_SECTIONS` table, with a
round-trip test that fails when a section is added but not wired up.

Third-party SQLite session stores (OpenCode, Hermes, Cursor,
Antigravity) lost anything still in the WAL, and restore left the live
`-wal`/`-shm` beside the replaced database for the next open to replay.
Each store is now page-copied through a read-only connection into one
self-contained archive entry — never `VACUUM`, which renumbers implicit
rowids in a schema we cannot audit, and never a read-write open, which
runs WAL recovery inside someone else's store. Restore deletes the live
sidecars before the rename with a directory fsync between, so no crash
point leaves "new db + stale wal". Pairs whose coherence cannot be
proven are refused rather than published.

Restoring an older archive no longer clears a section that format never
knew about (the declaration travels in the pending-restore marker,
because the manifest never reaches staging and the swap runs in the next
process), and the archive format version is bumped so an older binary
refuses a new backup instead of silently restoring part of it.

Safety snapshots become resumable, prunable and reversible: a durable
per-unit swap record makes a retried swap idempotent, `.codeg-snapshot.json`
records what was absent so a rollback can undo a restore that introduced
a file, and pruning keeps the two newest. Writing back to the agents'
own directories now takes a lock that also covers agents still exiting,
and downgrades to the side location instead of failing — the swap is
already committed by then, so an error would say "don't restart" while
the restore applies anyway.

Also: one decryption per restore instead of three, scratch off tmpfs,
an escape hatch for a staged restore whose restart never happened, a
result panel on both runtimes, snapshot rollback, a cancel button, real
progress totals, and the first tests for `backup-settings.tsx`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
X
xintaofei committed
f558c5638d4cf3e559bb965af4f570f5920ff52a
Parent: 5f6f2a9