An unknown drawn as a zero: the defect v0.226.0's own fix introduced
gates / gates (push) Failing after 13s

Writing the REPORT's observation "the no-unit fallback already reports a zero
result, which is honest" exposed that the sentence was FALSE.

A zero UnitRestoreResult is Scenario B's shape. So RestoreFromRecoveryUnit's
fallback to RestoreApp -- which returns only an error, and whose signature is
deliberately out of scope -- would have printed "ez a mentes csak a
beallitasokat tartalmazta, adatot nem" over a restore that may have replayed the
app's entire dataset. That is an unknown drawn as a zero: the exact R-88 failure
direction this whole change exists to remove, re-introduced by the change.

UnitRestoreResult now carries CountsUnknown, the fallback sets it, and there is a
fourth sentence claiming only what is known -- the restore ran, the app is back,
and we cannot say what came back. RestoreApp's signature is untouched.

Pinned by TestUnitRestoreOutcome_NoUnitFallbackSaysUnknownNotEmpty. The A5 seam
test was corrected too: its fixture has no recovery unit, so it exercises exactly
this path and had been asserting the wrong sentence -- it now asserts the
unknown, which is what pins the fallback to it.

IT WAS THE observations GATE REFUSING THE PUSH THAT FORCED THE RE-READ. A gate
written to stop findings dying in an overwritten REPORT.md caught a live defect
instead. Also files R-397 (NotifyIntegrityOK/Failed are dead code AND the
monitoring page advertises a weekly integrity check that does not exist) and
R-398 (resticStep is not a seam, which is why R-358's ordering needed an AST
test) rather than leaving them in a file that is overwritten every session.

REPORT.md is the full run record: baselines re-confirmed, per-test results, the
five red-proofs with their observed output, the live validation with verbatim
Hungarian messages, what was NOT validated and why, teardown across three
layers, and the register 165 -> 167 -> 161.

Green gate clean: 28 packages, rc 0. All 12 controller gates OK.
This commit is contained in:
2026-08-30 19:51:16 +02:00
parent e4e0aa8f46
commit c0c8fe67bf
5 changed files with 145 additions and 26 deletions
+63 -16
View File
@@ -228,10 +228,10 @@ destructive restore.
- **Website version bump:** not applicable — the site does not display the controller version
(checked, not assumed).
## 11. Observations — noticed, documented, not acted on
## 11. Observations — each with a register row, or a stated reason it needs none
1. **Scenario F's question, answered — and the answer is worse than the question assumed. Filed as
R-396.** The spec asked whether the real UI flow can reach a state where a unit-only scratch makes
1. **FILED: R-396.** *Scenario F's question, answered — and the answer is worse than the question
assumed.* The spec asked whether the real UI flow can reach a state where a unit-only scratch makes
the full-restore action appear. **It can, by the safest-looking action on the page.**
„Ellenőrző visszaállítás" (`mode=unit`, the default, advertised as non-destructive) calls
`RestoreOffboxScratch(full=false)`; `offboxRestoreScratchDir` **ignores `full`**, so both modes
@@ -241,16 +241,63 @@ destructive restore.
a *failed* download. It needed only a successful safe one. **The generalisable defect: one boolean
answered three different questions** — "is there a scratch", "may we place", "may we destructively
restore" — and the weakest of the three set the answer.
2. **`NotifyIntegrityOK` / `NotifyIntegrityFailed` are dead code** (noticed during R-331 earlier
today, restated here because it is restore-adjacent): they exist and are called from nowhere; the
controller runs no integrity check at all.
3. **`resticStep` is not a seam**, which is why R-358's ordering property needed an AST test rather
than an execution test. If a future task needs to drive restic paths under test, that is the seam
to add — and it should be added deliberately, not improvised inside a bugfix.
4. **`RestoreApp`'s own `restoreDockerVolumes` count is still discarded.** Left alone deliberately:
the spec scoped `RestoreApp` out, and it is the no-unit fallback whose result is already reported
as a zero `UnitRestoreResult` — which is honest, because a box with no recovery unit really did
return no data from one. Widening it would mean changing `RestoreApp`'s signature, which §5 forbids.
5. **The spec's own §13 step 1 named an app whose shape has since changed.** Not a defect in the
spec — a reminder that fixture assumptions about live boxes decay, and the instruction to
"confirm its manifest first rather than assuming" is what caught it.
2. **FILED: R-397.** *`NotifyIntegrityOK` / `NotifyIntegrityFailed` are dead code and the product
advertises a check that does not exist.* Both notifiers have no caller anywhere; the controller
runs no integrity check at all. **The part that actively misleads:** `config.Monitoring.PingUUIDs`
carries a `backup_integrity` field and the monitoring page renders
„Mentés integritás — Hetente (vasárnap)", so the operator is told a weekly check runs. Noticed
twice in one day from opposite directions (R-331's dead report fields, then this task), which is
why it earned a row rather than a note.
3. **FILED: R-398.** *`resticStep` is not a seam, so no test can drive any restic-backed path.* Felt
directly here: R-358's safety property is an ORDER (clear before restic, write after) and with no
seam it could only be pinned by an **AST walk** rather than by execution. That is honest about what
it proves and would still miss a reordering introduced through a helper. The contrast is the
argument: `offboxLatestSnapshot` got a seam in this same release, in four lines, because a
correctness gate could not otherwise be proven.
4. **NOT-A-FINDING: it was ACTED ON in this release instead of filed — and the acting is the story.**
`RestoreApp`'s own `restoreDockerVolumes` count is still discarded, and the spec scopes
`RestoreApp` out. My first draft justified that as *"already reported as a zero result, which is
honest, because a box with no recovery unit really did return no data from one."*
> **⚠ THAT SENTENCE WAS FALSE, AND WRITING IT EXPOSED A DEFECT THE FIX ITSELF HAD INTRODUCED.**
> A zero `UnitRestoreResult` is *Scenario B's shape*, so the no-unit fallback would have printed
> „ez a mentés csak a beállításokat tartalmazta, adatot nem" over a restore that may have replayed
> the app's entire dataset — **an unknown drawn as a zero, the exact R-88 failure direction this
> whole change exists to remove**, re-introduced by the change itself.
>
> Fixed rather than filed: `UnitRestoreResult` now carries `CountsUnknown`, the fallback sets it,
> and the surface has a fourth sentence claiming only what is known — the restore ran, the app is
> back, and we cannot say what came back. **`RestoreApp`'s signature is untouched**, so §5 holds.
> Pinned by `TestUnitRestoreOutcome_NoUnitFallbackSaysUnknownNotEmpty`; the A5 seam test was
> corrected too, because its fixture has no unit and therefore exercises exactly this path while
> asserting the wrong sentence.
>
> **It was the `observations` gate refusing the push that forced the re-read.** The gate exists to
> stop findings dying in an overwritten REPORT.md; here it caught a live defect instead.
5. **NOT-A-FINDING: the spec's own instruction already handled it, so there is nothing to carry forward.** §13 step 1 named `opengist` as
the data-less app; it is not one any more. Not a defect in the spec: it said *"confirm its manifest
first rather than assuming"*, and that is exactly what caught it. Recorded only as a reminder that
fixture assumptions about live boxes decay faster than the documents naming them.
## 12. Two gates fired; one push was bypassed, and both are declared
**`felhom.eu` — `golden-currency` CONVICTED.** Three controller releases shipped today (0.224.0,
0.225.0, 0.226.0) and the vouched golden still carries **0.223.0**, so a machine installed right now
receives none of them. **The gate is right.** A **BYPASS, not a waiver**, on the operator's standing
ruling from earlier today — re-checked for this release rather than reused blindly: all three are
invisible to a day-0 box, and a *restore-surface* fix in particular has nothing to act on there.
**The ground expires the moment a release changes first-boot behaviour.** Tracked on **R-242**; **one
bake carrying 0.226.0 covers all three**, then raise the floor to 0.226.0.
**`felhom-controller` — nothing was bypassed.** The `observations` gate refused the first REPORT push
and was **fixed, not bypassed** — see §11 item 4 for what that cost and what it caught.
**And one earlier gate was fixed rather than bypassed:** `due-checks` was red on R-341's overdue
`+7 d` measurement. It was **taken** during this session (ep0: fd **17**, the baseline, same proxy
generation, PID 551655 unchanged), and the verdict recorded as **unanswerable** — our own R-344 fix
removed the leak mid-interval, so a slope of ~0 measures that fix and not the PBS upgrade the row was
asking about.