The reaper reads .beads/backup/backup_state.json but nothing writes it, so bulk prune never runs and it escalates every cycle while backups are healthy #22
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happens
The reaper skips its bulk prune on every cycle and raises a MEDIUM escalation each time, with this message:
The backups are fine. The file it reads is not the file anything writes.
Measured, gc-scratch, 2026-09-07 22:11Z
The path the reaper reads does not exist:
The path that IS written, and is fresh:
6h old against an 86400s threshold, so comfortably inside it.
And the backups themselves are healthy. Manifest mtimes, which
CLAUDE.mdalready names as the instrument to trust here rather than any state file:Five of seven stores backed up within the last three minutes.
backup_state.jsonappears in the gc binary's strings alongside%s: read backup_state.json: %v, so the reader is in gc, not in a script.Why it costs something
The prune never runs. That is the actual function being skipped, silently and indefinitely, so closed beads and wisps accumulate with nothing reclaiming them. The city gets slower and nobody is told, because the message the operator sees is about backups, which are the one thing that is working.
It floods the escalation channel. 58 identical MEDIUM escalations were sitting in the mayor's inbox when this was written, and that inbox is where real escalations arrive. A false alarm at that volume is not merely noise: it trains the reader to archive on subject line, which is exactly how a genuine escalation gets missed. This is the inverse of the stale-ticket problem — not a finding that reaches nobody, but a non-finding that reaches everybody, repeatedly.
The message actively misleads. An operator who acts on it goes and investigates a backup system that is healthy.
CLAUDE.mdalready carries an entry aboutbd backup statusreporting "No backup has been performed yet" for stores backed up every six hours, which is a different blind instrument in the same area. That makes two reporters that disagree with reality about backups, pointing at two different state files, neither of which is the artifact.Suggested fix
Pick one deliberately, they are not equivalent:
.beads/dolt-backup-state.json, the filebd backup syncactually writes..dolt-backup/<db>/manifestmtime. This is what the operator instructions already tell humans to do, and it cannot drift out of sync with the writer because there is no separate writer.Option 2 is the one that removes the class: any scheme where a reporter reads a state file some other component is responsible for writing can drift the moment either side moves, and this is the second instance of exactly that in the backup area.
Separately, the escalation should be rate-limited or deduplicated. Whatever the underlying condition, emitting the same MEDIUM 58 times is a defect on its own.
Filed from gc-scratch. No bead: no polecat in this city can change the gc binary.
A second false alarm in the same area, found in the same sweep: "Dolt backup: 1/7 databases failed to sync — hq(backup add failed)".
Also untrue, and it strengthens the case for fix option 2 rather than option 1.
Measured 2026-09-07 22:14Z, from
.beads/dolt/hq:So the sync job runs
dolt backup add, that call fails because the remote is already registered, and the job reports it as a database that failed to sync. The sync itself is fine:.dolt-backup/hq/manifestwas 3 minutes old at the time of measurement and the directory holds 2.1 GB across 86 entries.backup addis not idempotent and the job treats its failure as a sync failure. Those are different things. Registering a remote that already exists is the normal steady state, not an error worth escalating.Why this belongs on this issue rather than its own. Three separate reporters now disagree with reality about backups in this city, all in the same direction — claiming failure while the artifact is fresh:
backup_state.jsonpath (this issue),bd backup status, which reports "No backup has been performed yet" for stores backed up every six hours (already in gc-scratch'sCLAUDE.md),backup addhandling (this comment).Each has its own proximate cause, but the shape is identical: a reporter infers backup health from something other than the backup. Option 2 above — take freshness from the newest
.dolt-backup/<db>/manifestmtime — is the only one of the three suggested fixes that closes all of them, because the manifest is the artifact and cannot disagree with itself.The operational cost is now measurable rather than theoretical. Between this and the reaper escalation, the mayor's inbox held 58 reaper escalations plus repeated hq sync failures, all false, sitting above three genuine
JSONL spike detected [HIGH]escalations showing the hq database growing 138 → 222 → 301 → 366. The real signal was underneath the noise, and the growth it reports is plausibly a consequence of the bulk prune this issue describes never running.Root cause found, and it is upstream of everything above:
bd backup synccannot parse dolt's version string, so it refuses to run and therefore never writes the state file the reaper reads.A fourth escalation in the same sweep,
Dolt backup: dolt-too-old for backup sync [HIGH]:Measured on the same host, same moment:
2.3.1 is above the 2.1.0 floor. The version is not too old. It is being read as
unknown, and the comparison then fails closed.This makes the whole chain one defect rather than three
CLAUDE.mdalready records that two independent pipelines write the same artifact tree:mol-dog-backupsyncs every Dolt DB via thedoltCLI and stamps no state file, whilebd backup syncis the sole writer of.beads/dolt-backup-state.json. That explains every observation at once:bd backup syncmisparsesdolt version 2.3.1asunknown, decides it is below the floor, and skips..beads/dolt-backup-state.jsonis stuck at2026-09-07T16:06:27Zand.beads/backup/backup_state.jsonnever appears at all.mol-dog-backupkeeps running via thedoltCLI, which is why every manifest is 3 minutes old and the backups are genuinely healthy.The consequence, measured
The hq store now holds 12,587 closed beads against 23 open (87 with wisps). That is what the three genuine
JSONL spike detected [HIGH]escalations were reporting as they climbed 138 → 222 → 301 → 366.So the prune has a real job that is not being done, and the growth is not cosmetic.
What this changes about the fix
The path mismatch in the issue body is still worth fixing, but it is the second-order problem. Fix the version parse first:
bd backup syncshould readdolt version 2.3.1as 2.3.1. Whatever the parser is doing with a 2.x string, it is returningunknownrather than failing loudly, and "unknown is below required 2.1.0" is a comparison against a value that was never obtained.Two hardening notes while that is being touched:
dolt version 2.3.1"), not silently coerced to a sentinel that then loses a comparison. The current behaviour is indistinguishable from a genuinely old dolt, which is what made this take four escalations to unpick.