Files
felhom-controller/REPORT.md
T
2026-08-12 15:39:08 +02:00

6.6 KiB

REPORT — v0.213.0: the banner promises only what the box can still see is true (2026-08-12)

Commit 8971256. Green gate: build/vet clean, 28 packages ok, 0 FAIL; all eleven controller gates OK (one is new). Golden 0.213.0 baked, published and round-trip verified. The Day-0 vouch is the operator's and was not made here. --no-verify used nowhere.

1. Is the pinned fingerprint provably the right key? — NO. It is a recorded assumption.

Asked first, as instructed. Nothing on the box records which key wrote the set-aside copies — that key is gone, which is why they were set aside. What is pinned is the package the hub held at the decision, which in the ordinary rebuilt-box story IS the pre-rebuild escrow covering the pre-rebuild repo password. On a twice-rebuilt box that presumption can be wrong: the hub may hold rebuild #2's package while the copies are rebuild #1's, and no key on file opens them. The pin cannot detect that.

Recorded as an assumption in the field comment on settings.AbandonPinnedEscrowKeySHA256, in the test header, and in R-302 — so it can be narrowed later rather than hardening into a fact.

What it does detect, and what the rejected proxy could not: the package being replaced after the decision — the exact act that cost both demo boxes their history on 2026-08-04.

2. Red-proofs — mutation asserted applied each time

# Mutation Outcome
1 the rejected proxy (hub fingerprint vs the CURRENT key, at render) FAIL — Scenario B and Scenario D flip back to promising. Yes, the false promise reproduced. This is the evidence for choosing the pin
2 pin re-read at render instead of used from the decision FAIL — B and D promise again
3 empty pin backfilled from the current value FAIL — a legacy countdown gets promised at
4 emptiness guards dropped PASSED — and that was a finding about my own test, see below
4b same mutation, against a new both-sides-empty test FAIL — two absences compared equal and became a promise
5 condition forced false FAIL — Scenario A loses the clause; the guard is reachable in both directions, not merely always-off

Red-proof 4 is worth reading. My Scenario E (pin present, hub hash empty) passed with the cur != "" guard removed, because its pin is non-empty so the equality fails on its own. The test guarded the sentence, not the guard. The state where emptiness actually bites is both sides empty — a legacy countdown on a box whose hub reports a legacy package. Added ScenarioE2, which fails as it should. Without running the mutation I would have shipped a guard nothing pinned.

3. The three strings as shipped

Always, both surfaces:

A korábbi távoli mentéseidet {{N}} nap múlva ({{dátum}}) véglegesen töröljük, a kérésed szerint.

Retrieval clause, only when the pin matches:

Addig még visszaszerezheted őket a helyreállítási kóddal.

Cautious branch, everything else:

Hogy ezek még visszaszerezhetők-e a helyreállítási kóddal, azt innen nem tudjuk megállapítani — ha vissza szeretnéd kapni őket, írj nekünk a törlés előtt.

Composed from explicit codepoints and hex-verified end to end: é=c3a9 ő=c591 á=c3a1 í=c3ad ó=c3b3 —=e28094 ü=c3bc ö=c3b6, no double-encoding, no replacement characters, in both templates.

4. A legacy countdown renders the cautious branch

A countdown started before v0.213.0 has no pin, so it renders the deletion sentence plus „Hogy ezek még visszaszerezhetők-e … írj nekünk a törlés előtt." Pinned by ScenarioD, and deliberately not backfilled.

5. The stem guard — and why it became a claim guard

The stem cannot be banned, and this is the honest answer to §5's last question. The replacement copy contains visszaszerezhet, inside a question about knowability. A guard forbidding the stem would force the product to avoid a normal Hungarian verb — a guard shaping the product around itself.

So scripts/retrieval_promise_gate.py registers each claim with a reason and fails on unregistered ones. Plant → convict → remove → pass run for all three historical wordings: each convicted (rc=1), all cleared on removal (rc=0).

The full sweep of all 36 templates found five sites, and I do NOT believe "nothing else" would have been the right answer — given the same promise surfaced one at a time across three sessions, the sweep finding two new instances is exactly what the history predicts:

site verdict
backups.html, backups_apps.html local backup/restore, unrelated to the escrow claim — allowlisted
backups_remote.html (RecoveryOffer entry) true where it renders; deliberately left, per the previous session
backups_remote.html (abandon block) FOURTH instance — same condition applied. Fixing the strip and not the page would have left one contradicting the other
layout.html (the banner) the subject of this session
recovery.html (abandon confirmation) FIFTH instance — deliberately LEFT. It renders at the moment of the decision, where the promise is true by construction: that is the package about to be pinned

One instrument defect found and fixed on the way: the gate's first draft matched by proximity window and reported two live entries as stale, because the promise and the disclaimer sit ~100 characters apart. It now matches by span.

6. Was R-296 correctly closed? Yes.

Its sentence was backups_remote.html:98; v0.212.0 replaced it and the stem guard covers it. Verified before closing, not assumed.

7. Observations, not acted on

  • R-303 — markOrphaned has no guard against an active countdown (offbox.go:804). This session makes the co-render harmless, not impossible. Ranked LOW, deliberately: it is a coherence question, not a correctness one, and the tempting fix — suppressing the orphan card during a countdown — would hide a genuine second fault (the box's new store also being unopenable).
  • The cautious sentence now appears on both the strip and the page below it when both render. Each is self-contained; I read it as reinforcement rather than a defect, and did not churn copy to avoid it.

8. Out of scope, so it does not read as forgotten

The drill session unblocking the two unpublished installer fixes; the hub half of the code naming (its file:line enumeration is still owed); the CI runs that fail with no log; the twenty unread facts; the nine grey claims; and proving that a retained key can actually open an old store — still unshown, and the same knowledge this whole design turns on.