fix(hooks): make the tracker swap safe on every failure path

Review of #7 found the swap could corrupt or lose the database: the move
of the live database out of the way was unchecked, so if it failed the
next move put the new database *inside* the old directory and still
reported success; the restore was unchecked too, and the `rm -rf "$TMP"`
that followed could then delete the only remaining copy.

All three are real. The root cause was doing the critical swap across
filesystems, where a move is a copy that can half-finish.

install_tracker() removes the class instead of patching the cases. The
staged copy is brought alongside the live one first, so every step after
that is a same-filesystem rename: it either happens or it does not. The
previous database is removed only once the new one is in place, and every
move is checked, with a loud message if a restore fails and leaves it
parked. The caller collapses to one condition, which also removed a
duplicated pair of warning branches.

Proved rather than asserted, since this block has now been wrong twice.
`mv` was stubbed to fail at each point in turn:

  - old-move fails     -> rc=1, live database intact, no nesting, no leftovers
  - final rename fails -> rc=1, live database restored, no leftovers
  - missing source     -> rc=1, live database intact

A first attempt at the third case used chmod, which proves nothing as
root; the stub is what actually exercises the path.

The transient .incoming/.previous directories are gitignored so a crash
mid-swap cannot leave untracked noise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
This commit is contained in:
Claude
2026-09-19 06:59:00 +00:00
parent f0729e9e5b
commit 00565796e5
2 changed files with 39 additions and 16 deletions
+2
View File
@@ -36,3 +36,5 @@ prolog-sessions/*
# Beads runtime locks (bd creates these next to .beads/)
*.gate.lock*
.beads/embeddeddolt.incoming/
.beads/embeddeddolt.previous/