REPORT: v0.213.0 pinned-fingerprint condition, red-proofs, claim guard
gates / gates (push) Successful in 12s

This commit is contained in:
2026-08-12 15:39:08 +02:00
parent 89712563a0
commit 3168a78935
+85 -47
View File
@@ -1,66 +1,104 @@
# REPORT — v0.212.0: the orphan card's second promise (2026-08-12) # REPORT — v0.213.0: the banner promises only what the box can still see is true (2026-08-12)
Commit `68f3e12`. Green gate: build/vet clean, **28 packages ok, 0 FAIL**; all ten controller gates OK. Commit `8971256`. Green gate: build/vet clean, **28 packages ok, 0 FAIL**; all **eleven** controller
Golden 0.212.0 baked and published; **the Day-0 vouch is the operator's and was not made here.** 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.
## R-299 — the same claim, one paragraph up, in the plural ## 1. Is the pinned fingerprint provably the right key? — NO. It is a recorded assumption.
`internal/web/templates/backups_remote.html` line 98 — the **always-visible** half of the orphan card — Asked first, as instructed. **Nothing on the box records which key wrote the set-aside copies** — that
still ended *„a hozzájuk tartozó helyreállítási kóddal később **visszaállíthatók lehetnek**"*. The key is gone, which is why they were set aside. What is pinned is the package the hub held at the
paragraph fixed in v0.211.0 renders only after the customer clicks „Új távoli mentés indítása…", so on decision, which in the ordinary rebuilt-box story IS the pre-rebuild escrow covering the pre-rebuild
first view **the sentence a customer actually read was the one still promising.** 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.**
It survived for two independent reasons: 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.
1. `SPEC-orphan-card-copy-2026-08-10.md` §1 listed the line as *"Accurate; keep"* — corrected. What it *does* detect, and what the rejected proxy could not: the package being **replaced** after the
2. **The regression guard matched one inflection.** It asserted `visszaállítható lehet` (singular); the decision — the exact act that cost both demo boxes their history on 2026-08-04.
card carried `visszaállíthatók lehetnek` (plural), which does not contain that substring. **A guard
matching one inflection of a Hungarian verb guards one sentence, not the claim.**
Guard now matches the stem `visszaállíthat`. The plain forms the rest of the UI uses („visszaállítás", ## 2. Red-proofs — mutation asserted applied each time
„visszaállítani") do not contain it, so it does not over-match.
**Shipped copy** (the two knowable halves kept — declining a promise must not become telling the | # | Mutation | Outcome |
customer less than we know): |---|---|---|
| 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 |
> A távoli tárhelyen lévő mentések egy korábbi, már nem elérhető kulccsal készültek (jellemzően **Red-proof 4 is worth reading.** My Scenario E (pin present, hub hash empty) **passed with the
> újratelepítés után). Emiatt új mentés jelenleg nem írható a tárolóba. **A meglévő mentések nem `cur != ""` guard removed**, because its pin is non-empty so the equality fails on its own. The test
> sérültek. Azt viszont ez a gép nem tudja megállapítani, hogy később megnyithatók-e — ez attól függ, guarded the sentence, not the guard. The state where emptiness actually bites is **both sides empty**
> megvan-e még a hozzájuk tartozó kulcs. Ha szükséged van rájuk, írj nekünk.** 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.
Bytes verified as hex: `á/é/ő/ü/ú/í/ó` and `—` all correct, no double-encoding, no replacement chars. ## 3. The three strings as shipped
## Plant → convict → remove → pass 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.
| step | result | 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 |
|---|---| |---|---|
| plant the exact shipped plural | stem guard **FAILS**, quoting the sentence back | | `backups.html`, `backups_apps.html` | local backup/restore, unrelated to the escrow claim — allowlisted |
| would the OLD narrow guard have caught it? | **No**`"visszaállítható lehet" in plural` is `False`, shown as a pure string fact rather than a source grep (my first attempt at that check was contaminated by my own template comment) | | `backups_remote.html` (RecoveryOffer entry) | true where it renders; deliberately left, per the previous session |
| remove the plant | 5/5 orphan-card tests pass | | `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 |
Two branches of the gate are covered: card shown, and a healthy store seeing none of it. 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.
## Instrument defect fixed in the guard itself ## 6. Was R-296 correctly closed? Yes.
Its failure message sliced the rendered HTML at a **byte** offset, which cuts Hungarian mid-character Its sentence was `backups_remote.html:98`; v0.212.0 replaced it and the stem guard covers it. Verified
and printed a replacement char — a garbled failure message reads like an encoding bug in the product before closing, not assumed.
rather than in the message. It now slices on rune boundaries.
## Does the card read coherently as a whole? ## 7. Observations, not acted on
**Mostly yes, with one thing I did not touch and one I introduced.** - **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.
- **Not touched, and reportable: `layout.html:143`.** The abandon countdown says *"Addig még ## 8. Out of scope, so it does not read as forgotten
visszaszerezheted őket a helyreállítási kóddal"* — the retired promise a third time and **un-hedged**.
It is probably TRUE where it renders (gated on an active abandon of a recovery offer that exists),
which is why I left it; but it renders on **every page**, and a rebuilt box can have an active
countdown while its store is orphaned. **Filed as R-301** with the wording question, not improvised.
- **Introduced: a repetition.** Once the customer clicks through, "we cannot determine / depends on the
key / write to us" appears in both the explanation and the confirm block. On first view — the common
case — only the explanation shows, and each is self-contained. I read this as reinforcement at the
decision point rather than a defect, and left it rather than churn customer copy.
## NOT DONE 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
- The Day-0 vouch (the operator's). nine grey claims; and **proving that a retained key can actually open an old store** — still unshown,
- No box was deployed to, so this is unit- and render-proven only, not exercised through a live endpoint. and the same knowledge this whole design turns on.