P2 slice 7: backups and restore (D-30, D-31)

The server backs each book up itself: a per-book git archive, pushed
fast-forward only to a git remote and to a folder, when the books open,
after a quiet spell, before they close and on request; every commit,
push and drill is a record in ops/backup.jsonl. Drills clone each
target and verify the chain, the blobs, the vault and the head, weekly
and after a failure. `wanderbooks books restore` previews, then
restores beside or (with --replace) over, never moving books something
holds. The archive refuses anything but a plain bare repository and
pins git against the owner's configuration. books.backup and
books.back_up_now for the agent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-20 11:14:21 -06:00
1 parent 5bc0633cdd
commit 019d8d76a3
29 files changed
+6443 -136

No files matched your search

+16 -2
View File
@@ -657,6 +657,15 @@ impl Host {
if self.explicit_root {
command.arg("--root").arg(self.root.path());
}
let d = Knobs::default();
for (flag, now, default) in [
("--backup-quiet", self.knobs.backup_quiet, d.backup_quiet),
("--drill-first", self.knobs.drill_first, d.drill_first),
] {
if now != default {
command.arg(flag).arg(now.as_millis().to_string());
}
}
let mut child = command
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
@@ -701,11 +710,16 @@ impl Host {
None,
),
Intent::BackUpNow { .. } => (
HostMsg::Refused("backing up isn't built yet (it comes later in P2)".into()),
HostMsg::Refused(
"the books back themselves up; to back up now, ask your agent (books.back_up_now)"
.into(),
),
None,
),
Intent::Restore { .. } => (
HostMsg::Refused("restoring isn't built yet (it comes later in P2)".into()),
HostMsg::Refused(
"restore from a terminal for now: `wanderbooks books restore BACKUP`".into(),
),
None,
),
Intent::ConsentFormatUpgrade { .. } => (