v0.103.0 — R-84: an agent restart no longer triggers a redundant backup

Observed live: three redundant local backups on demo-felhom in one afternoon of
deploys. The backup Store is in-memory ('lost on restart; the cadence
re-populates'), so after every restart /backup/due said 'no successful backup
recorded yet' and the controller took another one. On the offsite tier that is a
wasted multi-hour WAN upload after every agent deploy.

- BackupRunner.NewestArchiveTime: when a backup last LANDED on this tier's
  storage, read from the storage.
- localapi.BackupArchiveLister (optional BackupService extension): the due-check
  takes whichever is newer, the in-memory record or the storage.

Asking the storage rather than persisting the store is deliberate: it is ground
truth (a pruned archive correctly stops counting, where a persisted record would
keep claiming a backup that no longer exists), needs no new on-disk state, and
answers only 'when did a backup last land' — the richer fields stay with real
records so the host-report never carries invented numbers.

Fail-safes: read error -> fall back to memory (never fake freshness, never
suppress); genuinely empty -> due; old archive -> still due; service without the
lister -> unchanged.

Red-proof observed; full suite green (29 packages).
This commit is contained in:
Claude Code
2026-07-26 18:20:59 +02:00
parent e4f22f4c4f
commit 5acf1033a2
4 changed files with 259 additions and 8 deletions
+39
View File
@@ -1,3 +1,42 @@
## v0.103.0 — R-84: an agent restart no longer triggers a redundant backup (2026-07-26)
**Observed live, not theorised.** Three redundant local backups ran on demo-felhom in a single
afternoon of deploys (2026-07-26). The backup `Store` is in-memory — its own doc comment says *"lost
on restart; the cadence re-populates"* — so after every restart `/backup/due` answered *"no
successful backup recorded yet"* and the controller dutifully took another one.
On the local tier that is wasted minutes. **On the OFFSITE tier it is a wasted multi-hour WAN upload
after every agent deploy** — and agent deploys are routine. That is what moved R-84 from a tidy-up to
something close to a prerequisite for running the offsite tier at all.
### Changed
- **`BackupRunner.NewestArchiveTime`** — when a backup last LANDED on this tier's storage, read from
the storage.
- **`localapi.BackupArchiveLister`** (optional extension to `BackupService`) — the due-check consults
it and takes whichever is newer, the in-memory record or the storage.
### Why ask the storage instead of persisting the store
- **It is ground truth, not remembered state.** A pruned or deleted archive correctly stops counting;
a persisted record would keep claiming a backup that no longer exists.
- **No new on-disk state, no migration**, and it is the same source `latestArchive` already trusts to
build the post-backup record.
- It answers only *"when did a backup last land"*, which is exactly what the due-check needs. The
richer fields (size, duration, uncovered volumes, error) stay with the real in-memory records — a
synthesized record would put invented numbers into the host-report, and the hub already covers
history via its own retained-report window (R-81).
### Fail-safe directions
- Storage read **error** → fall back to the in-memory record. An unreadable storage must never make a
tier look freshly backed up, and must not suppress a backup either.
- Storage genuinely **empty** → due. The fix must not invent a backup.
- Archive present but **old** → still due. This is not a blanket suppressor.
- Service **without** the optional lister → pre-R-84 behaviour, unchanged.
### Tests
+6, full suite green (29 packages). Red-proof observed: removing the fold-in reproduces
`Due:true Reason:no successful backup recorded yet` against a storage holding a 2 h-old backup.
## v0.102.0 — R-82 Slice D: an unprovisioned tier DEFERS instead of failing (2026-07-26)
Prerequisite for putting the offsite tier into the installer defaults (host-install 1.20.0).