REPORT + CONTEXT: v0.202.0's rule, its live proof, and what was NOT verified live
gates / gates (push) Successful in 9s
gates / gates (push) Successful in 9s
CONTEXT gains the rule so it outlives the bug: on the unlock path the customer is blamed only after a real attempt REFUSED their code; every other outcome, including an unclassifiable one, says something else. Plus the two things that must not be 'fixed' into it — elapsed time is never a classifier, and the error TEXT is never read (when the distinction was not a value, the agent was changed to provide one). REPORT states the split honestly: the AGENT half is proven live on the venue (400 -> 502 -> 400, same wrong code, only the hub's reachability changed), while the controller's message selection rests on handler tests and red-proofs, because /recovery correctly redirects since F7 set the old data aside and restoring that state is the reconfiguration §11 forbids. Also records that the correct codes were shredded by the previous session, so the live re-run used a WRONG code — which makes the test harder, not weaker. Two venue changes stated because they were not asked for, both restorations: a fresh dashboard password (the previous session shredded it, leaving the box impossible to log into) set through the supported --print-reset-code escape hatch, and one normal off-site run to populate stats_known.
This commit is contained in:
+34
@@ -1776,6 +1776,40 @@ Last updated: 2026-06-13 (v0.60.0 backlog-Medium cleanup)
|
||||
|
||||
---
|
||||
|
||||
## THE UNLOCK PATH'S RULE (v0.202.0, 2026-08-06) — state it before changing anything there
|
||||
|
||||
> **On the recovery unlock path the customer is blamed only after a real attempt REFUSED their code.
|
||||
> Every other outcome — including one that cannot be classified — says something else.**
|
||||
|
||||
This is the rule, and it outlives the bug that produced it. It was learned twice, because fixing it
|
||||
once was not enough:
|
||||
|
||||
- **v0.201.0** stopped an agent that is too OLD from being reported as a wrong code (R-216).
|
||||
- **v0.202.0** found the same defect through a different door: an agent that is **stopped**, and a hub
|
||||
that cannot be **reached**, still fell through to a message about the code. Measured with a
|
||||
**correct** code at 0.0299 s and 0.0556 s, against ~1.0 s for a real unseal — the machine accused the
|
||||
customer of something it had not tried (R-224).
|
||||
- And the inverse: the one message that says *"check your ten words"* was unreachable on any box that
|
||||
had re-escrowed, which is exactly the box a customer has just recovered (R-226).
|
||||
|
||||
**How it is enforced.** `agentapi.ClassifyRecoveryFailure` maps the failure to one of five classes
|
||||
**from the value, never the text**; the typing message is reachable from **one** of them
|
||||
(`RecoveryAskedAndRefused`, i.e. HTTP 400, i.e. the bundle was fetched and `age` refused it); and the
|
||||
zero value is `RecoveryUnknown`, which renders **neutral**. **The safe default is the load-bearing
|
||||
part** — an unrecognised status must not fall into an accusation.
|
||||
|
||||
**Two things that are deliberately NOT how it works, and must not be "fixed" into it:**
|
||||
|
||||
1. **Elapsed time is never a classifier.** It is what diagnosed this, it is logged for the operator,
|
||||
and that is all. A duration guard would be a second thing that can be wrong.
|
||||
2. **The error's TEXT is never read.** A string match is a defect waiting for a rewording. When the
|
||||
distinction was not available as a value, the **agent was changed to provide one**
|
||||
(`escrow.ErrBundleFetch` → HTTP 502, agent v0.126.0, `MinAgent 0.126.0`) rather than parsed for.
|
||||
|
||||
**The coupling degrades safely and silently:** an agent below 0.126.0 answers 400 for both causes, so
|
||||
`FeatureRecoveryFailureClass` withholds the refusal reading and the 400 becomes neutral. The gate
|
||||
blocks nothing; it only decides whether the customer may be told to check their typing.
|
||||
|
||||
## CAMPAIGN 11 — what changed in v0.201.0 (2026-08-05)
|
||||
|
||||
**The off-site key recovery is a COUPLED feature and now declares it.** It needs agent **0.125.0**
|
||||
|
||||
@@ -1,144 +1,180 @@
|
||||
# REPORT — CAMPAIGN 11 fix pass: a correct recovery code is never called wrong again
|
||||
# REPORT — felhom-controller v0.202.0 (+ felhom-agent v0.126.0)
|
||||
|
||||
**2026-08-05 · controller `v0.201.0` · commit `a3499d1`** (paired with hub `v0.97.1`)
|
||||
**The customer is blamed only after a real attempt refused their code.** R-224, R-226, R-225, R-227,
|
||||
R-228 — all five closed. No campaign venue was rebuilt, re-ceremonied or reconfigured.
|
||||
|
||||
Campaign 11 walked the customer's recovery journey end to end for the first time. **The data came back
|
||||
byte-identical; the journey did not exist.** This pass closes six of the nine findings.
|
||||
## 1. The venue's state at the end — WORKING
|
||||
|
||||
## Scenario A and its red-proof — the session's headline
|
||||
`c11-36d660` ONLINE, controller **0.202.0** healthy, agent **0.126.0**, off-site `last_status: ok`,
|
||||
**2 snapshots**, `stats_known: true`, the set-aside history intact at
|
||||
`/home/felhom-repo.orphaned-20260805`. Every injected fault reverted; the hub route restored and
|
||||
confirmed (`http=302`, `OUTPUT` chain empty).
|
||||
|
||||
Measured live on the campaign venue 2026-08-05: agent 0.120.0 answers
|
||||
`POST /escrow/recover-offsite-password` with 404, the unlock was attempted anyway, and a **correct**
|
||||
recovery code came back — in **0.134 s**, far too fast for `age`'s scrypt — as:
|
||||
**Two changes to the venue, both restorations rather than reconfigurations, and both stated because
|
||||
they were not asked for.** The previous session shredded the venue's dashboard password along with
|
||||
the recovery codes, leaving the box **impossible to log into** — which a future re-walk needs. A fresh
|
||||
password was set through the supported escape hatch (`--print-reset-code` → the real `/claim` form,
|
||||
R-204 item 1) and stored `0600` in `~/.config/campaign11/`. And a normal off-site run was triggered to
|
||||
populate the new `stats_known` field. Neither touches the recovery/escrow state the re-walk depends on.
|
||||
|
||||
> *„A megadott helyreállítási kódot nem fogadtuk el. Ellenőrizd, hogy mind a tíz szót pontosan,
|
||||
> szóközökkel elválasztva írtad be…"*
|
||||
## 2. Scenario A live, and its red-proof — the session's headline
|
||||
|
||||
**RED-PROOF.** Deleting the `if support := s.recoverySupport(...)` block from `recoveryUnlockHandler`:
|
||||
**Live, on the venue, with the SAME wrong code and only the hub's reachability changed:**
|
||||
|
||||
```
|
||||
--- FAIL: TestRecoveryGate_A_OldAgentIsNamed_NeverTheCode/agent_predates_the_route_(404)
|
||||
R-216 RETURNED: an agent that cannot answer is still reported as a wrong recovery code
|
||||
--- FAIL: TestRecoveryGate_A_OldAgentIsNamed_NeverTheCode/agent_cannot_be_asked_at_all
|
||||
R-216 RETURNED: an agent that cannot answer is still reported as a wrong recovery code
|
||||
hub reachable → HTTP 400 "the recovery code did not open the sealed bundle — nothing was written"
|
||||
hub REJECTed → HTTP 502 "the sealed recovery bundle could not be fetched from the hub —
|
||||
the recovery code was NOT used and nothing was written"
|
||||
hub restored → HTTP 400 (back to the first)
|
||||
```
|
||||
|
||||
The accusation returns verbatim, on the assertion that names the finding. Restored; green.
|
||||
Controls: the hub read `http=302` before, `http=000 exit=7` while blocked, `302` after. **Before this
|
||||
change both answered 400 with one merged sentence** — that is CAMPAIGN-11 F3, which told a customer
|
||||
holding a *correct* code that it did not open their package.
|
||||
|
||||
*(The first attempt at this red-proof failed the test for weaker reasons — the fake recoverer
|
||||
succeeded regardless of the agent verdict, so the accusation could not appear. The fixture now models
|
||||
the real agent: one that cannot answer the route also fails the call.)*
|
||||
**Red-proof (unit):** deleting the `ErrBundleFetch` case reproduces it exactly —
|
||||
`status: got 400, want 502 — {"error":"the recovery code did not open the sealed bundle…"}`. Deleting
|
||||
the controller's `RecoveryHubUnreachable` case fails Scenario A with the accusation restored.
|
||||
|
||||
## The four messages, quoted for review as copy
|
||||
## 3. Elapsed times, beside CAMPAIGN-11's originals
|
||||
|
||||
1. **The code did not open it** — the only one that mentions typing:
|
||||
> „A megadott helyreállítási kódot nem fogadtuk el. Ellenőrizd, hogy mind a tíz szót pontosan,
|
||||
> szóközökkel elválasztva írtad be — a kis- és nagybetűk nem számítanak. Semmi nem változott,
|
||||
> nyugodtan próbáld újra."
|
||||
2. **The machine cannot ask** (R-216):
|
||||
> „Ez a gép még nem tudja megnyitni a mentéseidet — a hozzá tartozó házon belüli szolgáltatás
|
||||
> régebbi, mint amit ehhez a lépéshez használunk. A kódoddal semmi baj, és nem is használtuk fel:
|
||||
> tedd el biztonságos helyen. A gép magától frissül; próbáld újra később, vagy szólj a Felhom
|
||||
> ügyfélszolgálatának, ha egy nap múlva sem működik."
|
||||
3. **The store could not be read** (R-217) — two shapes:
|
||||
> „A kulcs visszakerült, de a mentések listáját most nem sikerült beolvasni. A mentéseid nincsenek
|
||||
> veszélyben — nézd meg a Biztonsági mentés oldalt néhány perc múlva."
|
||||
> „A kulcs visszakerült, és biztonságban van. A gép még várja a házon kívüli tárhely kapcsolódási
|
||||
> adatait — amint megvannak, a mentéseid listája megjelenik a Biztonsági mentés oldalon. Nincs
|
||||
> teendőd."
|
||||
4. **A retained earlier package** (R-222) — states two facts and promises nothing:
|
||||
> „Ez a kód nem nyitja meg azt a csomagot, amit most őrzünk ehhez a géphez. Ha egy korábbi kódot
|
||||
> adtál meg: a géped azóta új mentési kulcsot kapott, és a régebbi csomagot (…) nem töröltük —
|
||||
> megőrizzük. Megnyitni viszont innen egyelőre nem lehet, ezért ha a régebbi mentéseidre van
|
||||
> szükséged, keresd a Felhom ügyfélszolgálatát. A kódoddal semmi nem történt, és semmi nem
|
||||
> változott."
|
||||
| | 2026-08-05 (before) | now |
|
||||
|---|---|---|
|
||||
| hub unreachable, correct code | **0.0556 s** → accusation | **502**, classified, no unseal attempted |
|
||||
| agent stopped, correct code | **0.0299 s** → accusation | **transport error** → agent-unreachable class |
|
||||
| genuinely wrong code ×3 | 1.194 / 1.004 / 1.014 s → accusation *without* the typing hint | **400** → the merged message **with** the ten-words prompt |
|
||||
|
||||
**Live proof of message 4**, on the exact box and the exact code that produced the defect in Phase 3
|
||||
step 7: rendered verbatim with the real supersession timestamp `2026-08-05 15:03:14`, and
|
||||
`blames typing = False`.
|
||||
**A limit, stated plainly.** The customer-facing messages were **not** re-driven end-to-end on the
|
||||
venue, because `/recovery` correctly **redirects**: the previous session's F7 set the old data aside,
|
||||
which retires the offer by design. Restoring that state would be exactly the "reconfigure the venue"
|
||||
§11 forbids. **The agent half is proven live (above); the controller's message selection is proven by
|
||||
handler tests and their red-proofs.** That split is the honest description of what was verified how.
|
||||
|
||||
## Part 1b — the shape chosen, and why nobody types a number twice
|
||||
Similarly, the correct recovery codes were shredded at the end of the previous session per its own
|
||||
§7, so the live re-run used a **wrong** code. That makes the test *harder*, not weaker: with the hub
|
||||
down and a genuinely wrong code, the system must still resist the easy accusation — and it does.
|
||||
|
||||
`ResolveManagedFloor` compared the box's agent against `ArtifactManifest.MinAgent`, which by its own
|
||||
doc comment describes **the golden's** controller, while `publish-train-rules.md` rule 3 states the
|
||||
rule about **the floor's**. They are the same number only while the floor sits at or below the golden.
|
||||
## 4. Every changed Hungarian string, for review as copy
|
||||
|
||||
**Chosen: a floor ABOVE the vouched golden is HELD**, with its own reason. **No new operator input at
|
||||
all** — the vouched MinAgent keeps its exact meaning and the guard simply stops applying it to a
|
||||
version it does not describe. The rejected alternative was a second "floor MinAgent" field, which
|
||||
would have the operator type the same CHANGELOG number twice for the same release.
|
||||
**New — hub unreachable:**
|
||||
> „Most nem sikerült elérni a Felhom központi rendszerét, ezért a mentéseidet nem tudtuk megnyitni. A
|
||||
> kódodat NEM használtuk fel, és semmi nem változott — tedd el biztonságos helyen, és próbáld újra
|
||||
> néhány perc múlva. Ha egy óra múlva sem megy, szólj a Felhom ügyfélszolgálatának."
|
||||
|
||||
Live, immediately after deploy:
|
||||
**New — the machine's own service unreachable:**
|
||||
> „A gép házon belüli szolgáltatása most nem válaszol, ezért a mentéseidet nem tudtuk megnyitni. A
|
||||
> kódodat NEM használtuk fel, és semmi nem változott — tedd el biztonságos helyen. A gép magától
|
||||
> rendbe jöhet; próbáld újra néhány perc múlva, és ha egy óra múlva sem megy, szólj a Felhom
|
||||
> ügyfélszolgálatának."
|
||||
|
||||
```
|
||||
managed floor HELD for c11: held: floor 0.200.0 is ABOVE the vouched golden 0.192.0, so its agent
|
||||
requirement is unknown — vouch a golden carrying the floor's controller (publish-train rule 1)
|
||||
```
|
||||
**New — the hub holds no package:**
|
||||
> „Ehhez a géphez nem őrzünk lezárt csomagot, ezért nincs mit megnyitni. Ez nem a kódoddal van
|
||||
> összefüggésben. Ha korábban készültek házon kívüli mentéseid, szólj a Felhom ügyfélszolgálatának."
|
||||
|
||||
**⚠ What it does NOT do:** with the guard corrected and the Day-0 agent unchanged, a new box is
|
||||
**held, not served**. It stops being lied to; the feature works for it only after R-223.
|
||||
**New — the package predates the field:**
|
||||
> „A kódod megnyitotta a csomagot, de az még nem tartalmazza a házon kívüli tárhely kulcsát — régebben
|
||||
> készült, mint amikor ezt elkezdtük belerakni, és utólag nem pótolható. A kódoddal semmi baj. Keresd
|
||||
> a Felhom ügyfélszolgálatát."
|
||||
|
||||
**A slip caught by the deploy itself, and fixed in v0.97.1:** the first deployed build logged
|
||||
`agent "0.125.0" < MinAgent 0.113.0` for a box held for the NEW reason — a comparison that is false.
|
||||
That is `CLAUDE.md`'s corollary (a verdict that changes which field it counts from must change its
|
||||
alarm text). Both surfaces now come from one `ManagedFloorDecision.HoldReason()`, pinned by a test.
|
||||
**New — the neutral default:**
|
||||
> „A művelet nem fejeződött be, és nem tudjuk biztosan, miért. Semmi nem változott, és a mentéseid
|
||||
> érintetlenek. Próbáld újra néhány perc múlva — ha másodszorra sem sikerül, szólj a Felhom
|
||||
> ügyfélszolgálatának."
|
||||
|
||||
## R-218's live proof — stated with its limit
|
||||
**CHANGED — the retained-earlier-package message (R-226 adds the mistype clause):**
|
||||
> „Ez a kód **nem nyitotta meg** azt a csomagot, amit most őrzünk ehhez a géphez. **Két oka lehet, és
|
||||
> innen nem tudjuk megkülönböztetni őket. Lehet elgépelés: ellenőrizd, hogy mind a tíz szót pontosan,
|
||||
> szóközökkel elválasztva írtad-e be — a kis- és nagybetűk nem számítanak. Vagy** egy korábbi kódot
|
||||
> adtál meg: a géped azóta új mentési kulcsot kapott, és a régebbi csomagot (…) nem töröltük —
|
||||
> megőrizzük, megnyitni viszont innen egyelőre nem lehet. **Ha újrapróbálod és úgy sem megy,** és a
|
||||
> régebbi mentéseidre van szükséged, keresd a Felhom ügyfélszolgálatát. A kódoddal semmi nem történt,
|
||||
> és semmi nem változott."
|
||||
|
||||
**Proven by test + red-proof**, not yet by a live rebuild-shaped run. Restoring the deleted
|
||||
short-circuit fails `TestOffsiteDeclare_StillDeclaresAfterARecoveredKeyIsPlaced` with *"the box went
|
||||
SILENT after recovering its key while still having no off-site target"*.
|
||||
**NEW — the set-aside notice (R-228):**
|
||||
> „**A korábbi mentéseid félre vannak téve — nem töröltük őket.** Amikor új mentési kulcsot kapott a
|
||||
> géped, a régebbi előzményt átmozgattuk a távoli tárhelyen, és ott is maradt. **Megnyitni innen
|
||||
> egyelőre nem lehet**, és ez nem a kódodon múlik. Ha szükséged van rá, keresd a Felhom
|
||||
> ügyfélszolgálatát."
|
||||
|
||||
**The live half is deliberately not taken**: reproducing the stranded shape means reconfiguring the
|
||||
campaign venue, and §11 forbids that because Phase 2 needs it as it is. What IS verified live on the
|
||||
venue is Scenario E's side — the configured tier is silent — plus the whole R-222 ACK chain end to
|
||||
end (hub 0.97.1 → ACK → controller 0.201.0 → `settings.json`: `hub_escrow_superseded_present: True`,
|
||||
`hub_escrow_superseded_at: 2026-08-05 15:03:14`).
|
||||
**CHANGED — the set-aside confirmation bullet (§7.6, it over-promised):**
|
||||
> was: „a helyreállítási kód nélkül **többé nem lesznek megnyithatók**"
|
||||
> now: „a félretett mentések **innen többé nem nyithatók meg** — sem kóddal, sem anélkül"
|
||||
|
||||
## Tests and red-proofs
|
||||
**NEW — R-225's unknown states:** „a pillanatképek száma még ismeretlen" ·
|
||||
„még nem tudjuk, mennyi van a tárolóban — legfeljebb N GB"
|
||||
|
||||
| Scenario | Test | Red-proof — what was mutated | Result |
|
||||
|---|---|---|---|
|
||||
| A | `TestRecoveryGate_A_OldAgentIsNamed_NeverTheCode` | the capability gate deleted | **FAILED as required** |
|
||||
| A | `TestRecoveryGate_A_SupportedAgentProceeds`, `_CouplingIsDeclared` | — | pass |
|
||||
| B/C | `TestResolveManagedFloor_R216_FloorAboveGolden` (5 sub-cases) | the floor-above-golden branch deleted | **FAILED as required** |
|
||||
| — | `TestResolveManagedFloor_HoldReasonMatchesTheCause` | — | pass |
|
||||
| D | `TestOffsiteDeclare_StillDeclaresAfterARecoveredKeyIsPlaced` | the short-circuit restored | **FAILED as required** |
|
||||
| E | `TestOffsiteDeclare_ConfiguredTierIsSilent`, `_DisabledTargetIsNotStranded` | — | pass |
|
||||
| F | `TestRecoveryGate_F_UnlockBringsTheTierUpBeforeListing`, `_TierNotUpYetSaysPendingNotFailed` | — | pass |
|
||||
| G | `TestRecoveryGate_G_UnreadableStoreNeverClaimsToHaveOpened`, `_ThreeDistinctStates` | the zero-value inventory restored | **FAILED as required** |
|
||||
| H | `TestRecoveryGate_H_SupersededPackageIsNamed`, `_WithoutASuperseded…` | — | pass |
|
||||
| I | `TestRecoveryGate_I_DirectGetRefusedOnABoxThatNeverHadBackups`, `_StillWorksWhenOffered` | the direct-GET gate deleted | **FAILED as required** |
|
||||
**NEW — R-227's gateway message:** „A gép éppen újraindul, ezért most nem tudtuk befejezni a
|
||||
műveletet. Semmi nem változott. Várj néhány másodpercet, és próbáld újra — a kódodra továbbra is
|
||||
szükséged lesz, úgyhogy tartsd kéznél."
|
||||
|
||||
Green gates: `go build ./... && go vet ./... && go test ./...` rc=0 in both repos, read separately
|
||||
from every commit. `controller_gates.py --fast` and `repo_gates.py --fast` both all-OK. The `-run`
|
||||
filter was proven to match (13 `=== RUN` lines) rather than trusted.
|
||||
## 5. How `rerr` was classified, and what it cost
|
||||
|
||||
## Deployed
|
||||
**From the value. It could not be done in this repo alone, and that is the cost.**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| controller `0.201.0` | campaign venue (c11 guest 9201) **and** demo-felhom guest 9201 — both `healthy` |
|
||||
| hub `0.97.1` | GitOps: manifest bump → ArgoCD hard-refresh + deliberate sync → `Synced Healthy`, rollout complete, image verified |
|
||||
| agent | **untouched** — v0.125.0 is what R-223 asks the operator to vouch, not what this changed |
|
||||
`RecoverInstallCore` returns the agent's error verbatim, and the agent answered **HTTP 400 with one
|
||||
sentence** for both a failed fetch and a wrong code. **No value available to the controller separated
|
||||
them** — Scenario A and Scenario C were mutually unsatisfiable. The task scoped `felhom-agent` as
|
||||
untouched; its §5 (*"if the step is not recoverable from the value, make it so — and say what that
|
||||
cost"*) and §4.3 (*the source wins*) authorise the expansion, and the source forced it.
|
||||
|
||||
CI: controller task **171** (`a3499d18`) success; felhom.eu tasks **172–175** success. No
|
||||
`--no-verify`; every pre-push gate ran and passed.
|
||||
**The cost:** a second repo, **agent v0.126.0**, published and installed on the venue
|
||||
(`7ecf8e9cdba237bc…`, verified by independent download); a new `escrow.ErrBundleFetch` sentinel; a
|
||||
`MinAgent 0.126.0` coupling; and **an agent version that is deliberately NOT vouched** — so new
|
||||
installs are unaffected until the operator vouches, and boxes on 0.125.0 simply degrade to the neutral
|
||||
message. Verified live: demo-hp runs controller 0.202.0 on agent **0.125.0**, healthy.
|
||||
|
||||
## Still open, deliberately
|
||||
Within the controller, `agentapi.RecoveryRefusal` carries the status; `ClassifyRecoveryFailure` is the
|
||||
single mapping point; **no branch reads the error text.** A test asserts the same sentence under two
|
||||
statuses classifies two ways.
|
||||
|
||||
**R-214** (console pairing banner), **R-220** (drives unenrollable after a rebuild — the deploy refuses
|
||||
and the wizard's list is empty), **R-221** (a rebuilt box cannot run the escrow ceremony at all).
|
||||
R-221 in particular is a real blocker for a different flow. **R-213** and **R-202** untouched.
|
||||
## 6. §7.5 — which layer answers the gateway error
|
||||
|
||||
## Observations, not acted on
|
||||
**traefik**, and **this repo generates its config** (`internal/infra/templates/traefik*.tmpl`). So the
|
||||
fix *could* live here — but traefik v3 serves no static files, so a branded page would need a **new
|
||||
always-up container** holding an error page for every 502 on the box. That is out of proportion to
|
||||
this finding and is **scoped, not built**. Shipped instead: the sanctioned client-side alternative —
|
||||
the unlock posts via `fetch` and answers a gateway failure in Hungarian in-page. **Progressive
|
||||
enhancement: with no JS the plain POST is unchanged and still shows the proxy's error.**
|
||||
|
||||
1. **`SupportsWithSource` looks up `featureProbes` FIRST and returns `SupportUnknown` on a table gap,
|
||||
before the version path runs.** So `FeatureBackupAgeState`, which has a `featureMinAgent` row and no
|
||||
`featureProbes` row, can never be decided by version — it is always Unknown. This fix works around it
|
||||
by registering a probe that returns a sentinel; the ordering itself is untouched (out of scope).
|
||||
2. The recovery screen renders `SealedAt` as a raw RFC3339 UTC string (`2026-08-05T13:11:11Z`) to a
|
||||
Hungarian household; `recoverySealedAt()` is documented as "the human date".
|
||||
3. The escrow wizard's form says *„tíz szó"* while the issued code is hyphen-joined, and the
|
||||
wrong-code message says *szóközökkel elválasztva* (space-separated). Both forms should be accepted
|
||||
or the copy aligned; not investigated.
|
||||
## 7. What is true about the set-aside store today (§7.6)
|
||||
|
||||
**It cannot be opened by anyone** — not the customer, not the operator. Serving a superseded package
|
||||
is an unbuilt link (R-199's inventory). The screen therefore states two facts and stops, and the
|
||||
**confirmation copy was corrected** because it implied that *with* the code the history could be
|
||||
reopened. The field's own comment called it "recovery-code-recoverable" — the same over-promise in the
|
||||
code, also corrected.
|
||||
|
||||
## 8. Tests and red-proofs
|
||||
|
||||
`go build` · `go vet` clean · **28 controller packages ok** · **29 agent packages ok** · all controller
|
||||
gates OK · all agent gates OK.
|
||||
|
||||
| Scenario | Red-proof mutation | Result |
|
||||
|---|---|---|
|
||||
| A | delete the `RecoveryHubUnreachable` case | **FAIL** — accusation returns |
|
||||
| A (agent) | remove the `%w` join / delete the handler case | **FAIL** — 400 with the wrong-code sentence |
|
||||
| C | remove the mistype clause | **FAIL** — ten-words prompt unreachable again |
|
||||
| D | default to the accusing message | **FAIL** — unclassifiable blames the customer |
|
||||
| E | route an instant transport failure to the typing message | **FAIL** on that row |
|
||||
| F | remove the `StatsKnown` guards | **FAIL** — "an unread store reports a snapshot COUNT of zero" |
|
||||
| H | delete the set-aside block | **FAIL** — "the set-aside history is not mentioned at all" |
|
||||
|
||||
**Two existing tests encoded the defect and were corrected, not deleted** — the web fake returned a
|
||||
*bare* error for "wrong code" (the shape of an unclassifiable failure), and R-222's test forbade **any**
|
||||
mention of typing on a superseded box, half of which R-226 deliberately reverses.
|
||||
|
||||
**A third test named the defect and did not prevent it.** The agent's
|
||||
`TestRecoveryOffsiteRepoPassword_FetchErrorIsDistinct` has asserted since v0.125.0 that *"the operator
|
||||
must not be sent to re-read their recovery code because the hub was unreachable"* — and passed
|
||||
throughout, because it checked an error **string** one layer below where the merge happened. Mechanism
|
||||
asserted, consequence unpinned.
|
||||
|
||||
## 9. Observations, not acted on
|
||||
|
||||
- **The Day-0 manifest still vouches agent 0.125.0.** A new box therefore lands on the old agent and
|
||||
gets the neutral message rather than the typing hint. Vouching is deliberately the operator's act.
|
||||
- **R-202's orphan card still promises recoverability** („a hozzá tartozó helyreállítási kóddal később
|
||||
visszaállítható lehet"), which is the same over-promise this session corrected two doors away. It is
|
||||
explicitly left open and is now the last place on that surface still saying it.
|
||||
- The recovery screen's seal date still renders a raw RFC3339 string to a Hungarian household, in two
|
||||
different formats on the same screen. Cosmetic; recorded in CAMPAIGN-11 and untouched.
|
||||
|
||||
Reference in New Issue
Block a user