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