v0.231.0 - the box proves its own off-site copy still holds something (R-87)
gates / gates (push) Successful in 11s
gates / gates (push) Successful in 11s
R-87 re-scoped by its own spike and built as Option C. MinAgent 0.129.0 unchanged. THE QUESTION NOTHING ASKED. The weekly check proves the stored bytes are the bytes we stored; it cannot tell us we stored the WRONG thing. A hollow recovery unit backs up cleanly, checks cleanly at 100 percent depth, restores cleanly and gives the customer nothing back - measured on demo-hp 2026-08-31, 120082104 B to 7036 B in one nightly run recorded as a success (R-403). No tier and no cadence asked it. Now offsite-proof does, nightly, on one app. IT DOES NOT prove a restore puts data back into a running app. That stays drill work and 07 section 8 matrix row 4 is NOT moved. THE ACCEPTANCE RULE HAS TWO PARTS AND THE OBVIOUS ONE IS A TRAP. "Check the unit against its own packing list" PASSES a hollow unit, because a hollow unit declares nothing. So: (1) everything declared is present, AND (2) the manifest declares what the app is supposed to have. Part 2 is the whole value. RED-PROOFED: the naive rule makes the hollow-unit test read verdict "pass". THE EXPECTATION COMES FROM INSIDE THE UNIT, never the live box - the snapshot may predate the app's shape, and GetDockerVolumes describes the running app. Database half is DBServiceNames, the same discriminator RestoreFromRecoveryUnit uses. Volume half is ParseComposeNamedVolumes as an EXISTENCE check, not a name match: tars are <project>_<volume>.tar and ResolveDockerVolumeNames derives the project from the compose file's parent dir, which inside a unit is the literal string "compose". Measured on all eight real units on demo-hp the counts match exactly and the naming held every time - but "held on eight" is not "derivable" (R-355). Half a rule that is true beats a whole rule that is invented. THREE OUTCOMES: pass, fail (readable and empty), cannot judge. An app that legitimately has neither a database nor volumes PASSES. RED-PROOFED: alarming on any empty unit makes that test read verdict "fail". IT NEVER WRITES TO THE REPOSITORY and that is asserted on the ARGV as a non-effect: --no-lock, no unlockStale, and m.runner() rather than resticStep so the unlock --remove-all escalation is unreachable. RED-PROOFED: routing it the customer path's way makes the test fail on "unlock" appearing in the argv. IT TAKES acquireRunning ITSELF and skips rather than waits, because RestoreOffboxScratch does not take it (R-408) while offbox_integrity.go states that invariant as universal. DUE-NESS IS PER SNAPSHOT (R-86's model), never per clock. RED-PROOFED: recording a timestamp fails the stored-value test AND breaks the rotation - night 2 re-picks night 1's app. ITS SCRATCH IS A SEPARATE ROOT (backups/offsite-proof) and that is a safety decision, not tidiness: the job deletes its copy on every path, and sharing backups/offsite-restore/<app> would mean a nightly background job deleting the verification copy a CUSTOMER is looking at. It is also invisible to placement, so a proof copy can never be pushed into a live app. SHARED RATHER THAN FORKED: offboxScratchDirIn parameterises the scratch resolver on its ROOT builder, and unitOnlyHeadroom extracts the free-space gate, so the customer path and the proof refuse at the same floor with the same Hungarian sentence. RestoreOffboxScratch's behaviour is unchanged. NEW EVENT offsite_proof_empty, severity error, operator-only - deliberately NOT backup_integrity_failed, whose hub template says the store is DAMAGED. Here the store is sound and the content is absent: different cause, different action. The hub half shipped FIRST, in felhom.eu 1aeaa30 (hub v0.110.0, live and verified), because an unallowlisted type is 400'd and vanishes. 33 new tests, all groups green; full suite 1689 tests, 28 packages, rc=0. All 13 controller gates OK. Five red-proofs run and recorded in REPORT.md. A golden carrying 0.231.0 is OWED - the fleet is on 0.230.0. Viktor's call (R-242).
This commit is contained in:
@@ -1,3 +1,64 @@
|
||||
## v0.231.0 — the box proves its own off-site copy still holds something (2026-08-31, R-87)
|
||||
**MinAgent: 0.129.0** (unchanged)
|
||||
|
||||
**The weekly check proves the stored bytes are the bytes we stored. It cannot tell us we stored the
|
||||
WRONG thing.** A hollow recovery unit backs up cleanly, checks cleanly at 100 % depth, restores
|
||||
cleanly and gives the customer nothing back — R-403 measured that on demo-hp on 2026-08-31, 120 082
|
||||
104 B to 7 036 B in one nightly run, recorded as a success. **Nothing in the product asked that
|
||||
question, on any tier, at any cadence.** Now one job does, every night, on one app.
|
||||
|
||||
**Scope is the SPIKE's verdict, not the row as filed.** `audits/SPIKE-restic-restore-test-2026-08-31.md`
|
||||
measured that an unattended scratch restore would have caught **one** of the five restore-path defects
|
||||
drills found in six days. As a test of our restore code it is not worth an evening; as the only thing
|
||||
asking *"is there anything in there?"* it is. **This does NOT prove a restore puts data back into a
|
||||
running app** — that stays drill work, and `07` §8 matrix row 4 does not move.
|
||||
|
||||
- **THE ACCEPTANCE RULE HAS TWO PARTS AND THE OBVIOUS ONE IS A TRAP.** "Check the unit against its own
|
||||
packing list" passes on an empty package — a hollow unit declares nothing, so everything it declares
|
||||
is present. So: (1) everything declared is present, **and** (2) the manifest declares what the app is
|
||||
supposed to have. Part 2 is the whole value. `TestR87_HollowUnitForAnAppWithADatabaseFAILS` is the
|
||||
fence, red-proofed against the naive rule.
|
||||
- **THE EXPECTATION COMES FROM INSIDE THE UNIT, never from the live box** — the snapshot may predate
|
||||
the app's current shape, and `GetDockerVolumes` enumerates from live Docker, which answers a
|
||||
different question. The unit's own `compose/docker-compose.yml` answers both halves: `DBServiceNames`
|
||||
(the same discriminator `RestoreFromRecoveryUnit` uses) and `ParseComposeNamedVolumes`.
|
||||
- **THE VOLUME HALF IS AN EXISTENCE CHECK, NOT A NAME MATCH, deliberately.** Volume tars are
|
||||
`<project>_<volume>.tar` and `ResolveDockerVolumeNames` derives the project from the compose file's
|
||||
parent directory — which inside a unit is the literal string `compose`. Measured on all eight real
|
||||
units on demo-hp the counts match exactly and `<stack>_<volume>.tar` held every time, but "held on
|
||||
eight" is not "derivable" (R-355). Half a rule that is true beats a whole rule that is invented.
|
||||
- **THREE OUTCOMES, NOT TWO:** pass, fail (readable and empty), and **cannot judge**. Collapsing the
|
||||
third into a pass hides a real gap; into a failure, it alarms on our own blind spot. An app that
|
||||
legitimately has neither a database nor volumes **PASSES** — `TestR87_AppWithNoDatabaseAndNoVolumesPasses`,
|
||||
red-proofed by alarming on any empty unit.
|
||||
- **IT NEVER WRITES TO THE REPOSITORY, and that is asserted as a NON-EFFECT.** `--no-lock`, no
|
||||
`unlockStale`, and `m.runner()` rather than `resticStep` so the `unlock --remove-all` escalation is
|
||||
unreachable rather than unlikely. The spike measured that `restic restore` takes no lock and `restic
|
||||
check` does (R-407). `TestR87_NeverWritesToTheRepository` asserts the argv.
|
||||
- **IT TAKES THE SINGLE-WRITER FLAG ITSELF and skips rather than waits.** `RestoreOffboxScratch` does
|
||||
not take it (R-408) while `offbox_integrity.go` states that invariant as universal; this job does not
|
||||
wait for that to be fixed.
|
||||
- **DUE-NESS IS PER SNAPSHOT (R-86's model), never per clock.** `ProvedSnapshots[stack]` holds the
|
||||
snapshot ID proved. A timestamp re-proves the same snapshot forever — red-proofed, and it also breaks
|
||||
the rotation.
|
||||
- **ITS SCRATCH IS A SEPARATE ROOT** (`backups/offsite-proof`), and that is not tidiness: the job
|
||||
deletes its copy on every path, and sharing `backups/offsite-restore/<app>` would mean a nightly
|
||||
background job deleting the verification copy a CUSTOMER is looking at. It is also invisible to
|
||||
placement, so a proof copy can never be pushed into a live app.
|
||||
- **NEW EVENT TYPE `offsite_proof_empty`, severity `error`, operator-only** — deliberately NOT
|
||||
`backup_integrity_failed`, whose Hungarian template says the store is damaged. Here the store is
|
||||
sound and the content is absent: a different fact, a different action. **The hub half ships in the
|
||||
same commit** (allowlist + `operatorOnlyEvents`), because an unallowlisted type is 400'd and vanishes.
|
||||
- Verdict published on `OffboxReportStatus` with the `StatsKnown` absence rule: `last_proof_result`
|
||||
absent means NOT RECORDED, never "failed".
|
||||
- Slot **05:30**, chosen from the live schedule read off demo-hp (offbox-backup 04:15 / 2m52s,
|
||||
abandon-sweep 05:10, offsite-integrity 06:00 / 40.3 s). Measured cost: **one app ~2.3–4.0 s**, all
|
||||
eight 25 s — cheaper than the weekly check beside it.
|
||||
- 33 new tests. `unitOnlyHeadroom` extracted so the proof and the customer restore share one gate and
|
||||
one Hungarian refusal.
|
||||
|
||||
---
|
||||
|
||||
## v0.230.0 — a poorer copy must never delete a richer one (2026-08-31, R-403)
|
||||
**MinAgent: 0.129.0** (unchanged)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user