v0.259.0 — the claim page and the backup warnings answer in the reader's language (R-596, R-598)
gates / gates (push) Successful in 28s

The 2026-09-20 English drill ended one screen short: the claim page was English
and its answers were Hungarian, so a household who mistyped the code from their
e-mail could not tell a typo from a dead code. Fourteen call sites carrying nine
messages now go through s.msg; the backup page's two protection warnings — which
are promises about whether the customer's files are safe — follow the same route.

Hungarian is byte-identical, proved structurally by the go-parity gate against the
frozen base capture and red-proofed on a single added full stop.

data["Title"] was DEAD (claim.html is standalone; .Title is layout.html's) and is
deleted rather than translated — a translated dead field is a permanent false
signal about where the page's title comes from.

Six existing copy-contract tests were kept, not weakened: each now resolves its key
through the real bundle, so it still convicts on a reworded Hungarian sentence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-21 07:45:56 +02:00
parent f6909492cc
commit 0fe04bb6d5
15 changed files with 736 additions and 143 deletions
+53 -70
View File
@@ -1,83 +1,66 @@
# REPORT — v0.258.0: the last four Hungarian things, and the walk that judged them (R-561 slice 6)
# REPORT — controller v0.259.0: the claim page and the backup warnings in the reader's language
2026-09-20 · `main` at the v0.258.0 commit · image `gitea.dooplex.hu/admin/felhom-controller:0.258.0`
· live on demo-hp guest 9201 and on the drill box built from scratch the same day. MinAgent 0.131.0,
unchanged. Hungarian byte-identical.
**R-596 (P1), R-598.** Base `f6909492cc6e` → v0.259.0. MinAgent 0.131.0 unchanged.
Architecture read first and named: `felhom.eu/documentation/architecture/10-localisation.md` §1 (the
Hungarian must not move), §4 (fallback), §5 (voice per language), §10.6b (this slice).
## Claims in the task that turned out wrong
1. **"Sixteen Hungarian literals reach that page."** Fifteen literal sites reach it, carrying **ten**
distinct messages (four are repeats). Of the fifteen, **one is dead**: `data["Title"]` is read only
by `layout.html`, and `claim.html` is standalone with its own bundle-backed `<title>`. So
**fourteen live sites, nine messages** were converted and the dead one was deleted.
2. **"`backup_handlers.go` (12 Hungarian literals)."** Nine are code; the other three are Hungarian
inside COMMENTS (`"Mentés most"`, `"Rendszermentés"`, a label quoted in a struct doc). The offer
file's ten is right.
3. **"the recovery code (10 words — find its caller)" in the hub.** The hub does not mint it.
**felhom-agent** does, in `internal/escrow`, from the **EFF large wordlist** — so the recovery code
**has always been English**, ten words, ≈129 bits. R-597's recovery-code leg needed no work and
this repo does not own that secret. No row was added for it: inventing a second definition here is
how two sources of truth start.
4. **"the mail says 'three words'."** No claim mail states a count; they say `Setup code: %s`. The
only count wording in the product is the **bind page's** passphrase hint/placeholder ("five
words" / „öt szó"). The English pair is now count-free; the Hungarian is untouched.
5. **§16's phone-safe filter** ("no two words differing by one letter within the first six") was
measured before adoption: it removes **5270 of 7772** words, 12.92 → 11.29 bits/word. **Not
adopted** — see the hub REPORT for the three reasons and what replaced it.
6. **Line numbers** in the task were accurate. **"29 633 words"** is right to the line (29 634 lines,
29 609 after dedup). **"`customer.language` reaches the anonymous claim page"** is TRUE and is now
pinned by a test rather than assumed.
7. **"the box checks a hash and needs no change" (for R-597)** is TRUE — verified and pinned by
`TestClaimAcceptsAnEnglishWordCode`.
## What shipped
Four rows, all of them Go-side — which is why neither the template parity fixtures nor
`TestI18nEnglishPages` could see them, and why **slice 5's LIVE proof is what found them**.
- `internal/web/claim.go` — 14 sites → `s.msg(r, "claim.msg.*")`; the dead `Title` deleted with the
reason recorded in place.
- `internal/web/backup_target_offer.go` — three copy constants → bundle keys; `degradedMessageFor`
returns the KEY (decision language-free, in one place); `backupTargetView(ctx, lang)`.
- `internal/web/backup_handlers.go` — `buildTierViews`/`backupTargetLabel`/`loadGuestBackup` take the
reader's language, the `buildDataPathCards` shape.
- 23 new keys in both bundles; all 23 listed in `scripts/i18n_go_keys.json`.
| row | what | how |
|---|---|---|
| **R-589** | the update badge („Naprakész" + its tooltip) and the abandoned-lifecycle badge | bundle-backed forms in `localeFuncs`, reusing `compareInstalledToTemplate` and `EffectiveLifecycle` so **the decision cannot drift between languages — only the words do**; the badge's `Class` is asserted equal across languages for the same stack |
| **R-590** | the data-folder card's backup sentence — **a promise about the customer's files** | `consequenceFor` takes its lookup as a parameter; the test asserts the CONSEQUENCE in both languages, and that the two languages do not produce the same string |
| **R-573** | the agent-channel and endpoint-drift banners | keyed by the checker's own CLASSIFICATION, with the composed sentence kept as a **fail-open** fallback: an unmapped reason renders it verbatim rather than a blank banner or a raw key |
| **R-572** | **not what the row said** | the row claimed a template renders „vasárnap" on an English page. Measured: **no template and no Go file called `pruneLabel` or `nextPruneLabel`.** Dead func-map entries returning Hungarian — **deleted**, not translated |
## Evidence
`lifecycleBadge` was not in any row. It is the same builder one function from `updateBadge`, and
leaving a known identical defect there would have been a choice.
## Why deleting was the right direction for R-572, and how that was proven
Translating dead code would have added machinery with no reader and a test pinning a fiction.
Deletion is the **fail-loud** direction: `html/template` refuses to parse a template naming an
undefined function, and `loadTemplates` parses every template once per language at startup. Proven
rather than asserted — a template naming the removed function makes the parity test panic with
`function "pruneLabel" not defined`. `fmtDuration`, named in the same row, produces no Hungarian at
all ("< 1s", "5s", "2m 3s") and was left alone.
## Red-proofs — five, each seen to fail and then revert
| what was broken | what convicted |
| Check | Result |
|---|---|
| the `updateBadge` override removed from `localeFuncs` | English read „Naprakész" |
| `MessageKey` dropped from `SetAgentChannelAlert` | English read the Hungarian sentence |
| the folder sentence hard-coded back to Hungarian | the English arm convicted on three assertions |
| one Hungarian byte changed in a badge key | `TestLocaleFuncsHungarianBundleMatchesFuncMap` named both sides |
| a template naming a deleted helper | startup panic in the parity test |
| `go build ./... && go vet ./... && go test ./...` | green |
| `controller_gates.py --fast` (17 gates) | all OK |
| `scripts/i18n_go_parity.py` | OK — 718 keys, byte-for-byte against the frozen base |
| `scripts/i18n_missing_gate.py` | English missing **0** (ceiling 0); Hungarian formal 18 (ceiling 18) |
| `scripts/test_gate_decoys.py` | 23/23 |
**The fourth says something about the gate, not the code.** `i18n_go_parity.py` did NOT convict that
changed byte, because `localeFuncs` keys sit under `_preexisting` and are deliberately not measured
against the base capture. The citation to the test is what carries them — so the test had to be made
real, and it was extended to compare `hu.json` against the frozen funcmap forms word for word.
**Red-proofs, both seen failing:**
- One added full stop on `claim.msg.bad_code` in `hu.json` → go-parity named both sides.
- The wrong-code Hungarian literal restored in `claim.go` → the English test convicted twice (the
English sentence absent AND the Hungarian sentence present on the English page).
## Proven live, twice
## Two things the tests caught that review did not
- **demo-hp (0.258.0):** the English app page shows "Up to date" with an English tooltip while the
Hungarian page still says „Naprakész"; the folder sentence is English on one and unchanged on the
other; the free-space line reads "47.9 GB free" / „47.9 GB szabad". After the fixes, a scan of the
English app pages and the Apps list finds **zero** Felhom-authored Hungarian — the only hit is the
language picker naming itself „Magyar", which is correct.
- **A box installed from scratch that day**, walked by an English speaker: the update badge and the
time helpers were English there too, on hardware that had never run an older controller.
- **An apostrophe never renders.** `"The system backup's drive…"` is escaped to `&#39;` by
`html/template` and no `strings.Contains` on the page ever matched it. Reworded; all 23 English
values are now free of `' " < > &`.
- **"please" is not this product's voice.** The i18n gate convicted two of my English sentences for
pleading. Rewritten to plain second person.
## What the walk then found, which this release does not fix
## Open
The walk is `felhom.eu/documentation/audits/DRILL-first-hour-en-0258-2026-09-20.md`. Its verdict is
**not yet ready for an English-speaking tester**, and the blocker is in this repo:
- **R-596 (P1)** — `internal/web/claim.go` carries **sixteen** raw Hungarian literals, every one a
message the claim page shows. The page's chrome is English; its answers are not. Same shape as
R-573, one screen earlier in the journey, on the one screen between a household and their box.
- **R-598** — `backup_handlers.go` (12) and `backup_target_offer.go` (10): the Backup page's two
protection warnings and its two target names.
Both are the *composed-sentence-into-page-data* shape. That shape now has three instances on file
(R-573, R-596, R-598) and is worth naming as a class the next converter looks for.
## Needs the operator
**Raise the fleet floor to 0.258.0.** Nothing breaks if it waits — the four fixes simply do not reach
the other boxes. The drill box already ran 0.258.0, because the golden baked today is 0.258.0 and a
golden above the floor is served normally (`managed floor SERVED … floor 0.257.0 … golden 0.258.0`).
## Teardown
Nothing installed or removed on demo-hp; guest 9201 restarted onto the new image, which is the
deploy. The drill VM and its hub customer are torn down in three layers — see the drill document.
Every staged credential was shredded from both hosts and both guests.
Nothing from this release. The live proof and the floor are in the felhom.eu report.