The release fixed four Go-side leftovers. The walk that judged it on a box installed from scratch that day found two more of the SAME SHAPE — a composed sentence handed to a renderer as page data — on the claim page (R-596, P1) and the Backup page (R-598). That shape now has three instances on file with R-573, which is worth naming as a class rather than fixing one at a time. Verdict recorded: not yet ready for an English-speaking tester. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+4
-1
@@ -7,7 +7,10 @@
|
||||
>
|
||||
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
||||
|
||||
Last updated: 2026-09-20 (v0.257.0 — the app catalog can speak English)
|
||||
Last updated: 2026-09-20 (v0.258.0 — the last four Hungarian things, and the walk that judged them)
|
||||
|
||||
> **2026-09-20 — v0.258.0 (R-589/R-590/R-573/R-572, slice 6 Part 0).** The four leftovers slice 5's LIVE proof found, all Go-side: the update badge and the lifecycle badge through `localeFuncs` (reusing `compareInstalledToTemplate`/`EffectiveLifecycle`, so the DECISION cannot drift between languages — only the words do); the data-folder card's backup sentence through `s.msgLang`, with the test asserting the CONSEQUENCE in both languages AND that the two differ; the two channel banners keyed by the checker's own CLASSIFICATION with the composed sentence as a FAIL-OPEN fallback. **R-572 was not what its row said** — nothing called `pruneLabel`/`nextPruneLabel`, so the two dead Hungarian-producing func-map entries were DELETED; deletion is fail-loud and that was proven (a template naming a removed func panics `loadTemplates`). Five red-proofs; one of them convicted the GATE rather than the code — `localeFuncs` keys sit under `_preexisting` and are not measured against the base capture, so the citation to `TestLocaleFuncsHungarianBundleMatchesFuncMap` is what carries them, and that test was extended to make the citation true.
|
||||
> **THE WALK (slice 6 Part B) then judged it**, on a box installed from scratch that day by an English speaker: `felhom.eu/documentation/audits/DRILL-first-hour-en-0258-2026-09-20.md`. Verdict **NOT YET ready for an English-speaking tester** — `internal/web/claim.go` has **16 raw Hungarian literals**, every one a message on the one screen between a household and their box (**R-596, P1**), and `backup_handlers.go` + `backup_target_offer.go` carry the Backup page's two protection warnings (**R-598**). Both are the *composed-sentence-into-page-data* shape — **three instances on file now** (R-573, R-596, R-598), worth naming as a class. Everything else held with ZERO Hungarian: the download page, the bilingual console, all three customer mails, the bind page, the dashboard's first language from `customer.language` alone, both app pages, the catalog, the language switch. **R-214 closed as a side effect.** **Floor NOT raised — the operator's step.**
|
||||
|
||||
> **2026-09-20 — v0.257.0 (R-560 slice 5 Part A).** The catalog's `.felhom.yml` may now carry an `i18n: {en: …}` sibling block, and the dashboard reads it. `Metadata.For(lang)` merges FIELD BY FIELD (absent or blank English shows Hungarian, so a half-translated app ships); `For("hu")` is the parsed struct with `I18n` cleared and nothing else, deep-compared over all **53 real catalog files** copied into `internal/stacks/testdata/catalog/`; the three prose lists replace WHOLE and every other list is matched by its own key (`env_var`, option `value`, `match_group`, `target`, `path`) — **never by position**; `For` never writes through the manager's shared metadata. Pages reach copy only via `LocalizeStacks`/`LocalizeStackPtr`/`MetaFor`, and `TestNoDirectMetaCopyReadOnPages` AST-parses `internal/web` against a named allow-list so the NEXT page to read catalog copy fails the suite instead of rendering Hungarian to an English household. **Two of the nine red-proofs convicted a hollow TEST, not the code:** a struct copy shares its slices' backing arrays, so the obvious `DeepEqual` mutation check passed a deliberately broken merge; and a ONE-entry fixture cannot tell key matching from position matching. Both rewritten. Proven live on demo-hp (English pages English, seven Hungarian pages byte-identical apart from the CSRF token) and on demo-felhom **0.255.0** (block synced, pages identical, no parse warning in a 93-line log window). Gaps: R-589 (update badge still Hungarian in English), R-590 (the data-folder promise sentence), R-591 (`Stack.Copy()` does not deep-copy the new `Meta.I18n`), R-592 (three defects inside the new catalog gate, each found by its own decoy). **FLEET FLOOR RAISED to 0.257.0** the same session (operator asked), `min_agent` 0.131.0 declared — above the vouched golden 0.246.0, so the declaration carries it (R-472). Hub: `managed floor SERVED for demo-felhom … from declared`; **demo-felhom went 0.255.0 → 0.257.0 by itself in under 12 s**, healthy, `settle-gate: GO — at/above floor 0.257.0`, **and then rendered the English tagline** — the floor delivered the FEATURE, not a version string. Peti's box and tester-1 are DOWN and take it unattended when they return. The catalog was fully translated the same day (50 more apps in three pushes, 1 031 of 1 032 strings): **the English Apps list shows ZERO Hungarian app descriptions across all 53**. Gap added: R-594 (the catalog gate can convict a retrieval promise but cannot REGISTER a true one).
|
||||
|
||||
|
||||
@@ -1,149 +1,83 @@
|
||||
# REPORT — v0.257.0: the app catalog can speak English (R-560, slice 5 Part A)
|
||||
# REPORT — v0.258.0: the last four Hungarian things, and the walk that judged them (R-561 slice 6)
|
||||
|
||||
2026-09-20 · `main` `60f0a86` · image `gitea.dooplex.hu/admin/felhom-controller:0.257.0` · live on
|
||||
demo-hp guest 9201. MinAgent 0.131.0, unchanged.
|
||||
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.
|
||||
|
||||
## What this release does
|
||||
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).
|
||||
|
||||
An app's customer-facing text lives in the catalog's `.felhom.yml` and was Hungarian only. This
|
||||
release lets the same file carry an English twin in an `i18n: {en: …}` sibling block, and makes the
|
||||
dashboard read it when the household is on English.
|
||||
## What shipped
|
||||
|
||||
Architecture document read first and named here: `felhom.eu/documentation/architecture/10-localisation.md`
|
||||
— §1 (the Hungarian must not move), §4 (fallback), §5 (the gates), §7 (the catalog's copy model, which
|
||||
this release turns from a proposal into a measured fact), §11 ruling 7 (app names are not translated).
|
||||
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**.
|
||||
|
||||
## The five properties, and why each is load-bearing
|
||||
| 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 |
|
||||
|
||||
1. **`For("hu")` is the parsed struct with `I18n` cleared, and nothing else.** Deep-compared against
|
||||
all 53 real catalog files, copied into `internal/stacks/testdata/catalog/`. `LocalizeStacks`
|
||||
returns the INPUT SLICE for Hungarian rather than a copy, so nothing on the Hungarian path is
|
||||
even touched. §1 holds by construction, then measured live.
|
||||
2. **Fallback is field by field, and blank counts as absent.** A half-translated app is a legal,
|
||||
shippable state — that is what makes fifty apps pushable a batch at a time.
|
||||
3. **The three prose lists replace WHOLE; every other list is matched by its own key** — `env_var`,
|
||||
option `value`, `match_group`, `target`, `path`. Position matching mistranslates silently the
|
||||
first time a Hungarian field is inserted above another, and the page still looks right.
|
||||
4. **`For` never writes through.** The metadata is the stack manager's and is shared by concurrent
|
||||
requests; an in-place merge would put one household's language on another's page.
|
||||
5. **An older controller ignores the block.** `LoadMetadata` uses non-strict `yaml.Unmarshal` — this
|
||||
repo constructs no `yaml.Decoder` at all, so there is no `KnownFields` anywhere to turn strict.
|
||||
Proven live below.
|
||||
`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.
|
||||
|
||||
## Where a page may read catalog copy
|
||||
## Why deleting was the right direction for R-572, and how that was proven
|
||||
|
||||
**Four templates** carry catalog copy — `dashboard.html` (the app rows' second line), `stacks.html`
|
||||
(the Apps list), `app_info.html` (the app page, including the data-folder cards) and `deploy.html`.
|
||||
A fifth, `app_export.html`, reads `.Stack.Meta.Subdomain`, which is configuration.
|
||||
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.
|
||||
|
||||
**Five handler sites** feed them, and each now goes through `LocalizeStacks` / `LocalizeStackPtr` /
|
||||
`meta.For(lang)`: the dashboard, the Apps list, the deploy page, the app page — and the logs page,
|
||||
which today shows only the app's NAME and is routed through the same call anyway, because the next
|
||||
person to add a line to `logs.html` should not have to notice that this one page was the exception.
|
||||
Two producers sit in packages that have no request and therefore no language — the integration rows
|
||||
(`internal/integrations`) and the initial-credentials note (`internal/stacks/initialcreds.go`); both
|
||||
are re-taken from the localised metadata in the handler, identical bytes for Hungarian.
|
||||
## Red-proofs — five, each seen to fail and then revert
|
||||
|
||||
`TestNoDirectMetaCopyReadOnPages` parses every non-test file in `internal/web` and fails on any
|
||||
`x.Meta.<copy-field>` read that is not in a named, reasoned allow-list (three entries today, all
|
||||
`appDetailHandler`'s localised `found`). **What it cannot do, stated in the test:** it reads source,
|
||||
so it cannot tell a localised receiver from an unlocalised one. It catches the ARRIVAL of a new copy
|
||||
read, which is when a human has to think — that is the whole claim made for it.
|
||||
|
||||
## Tests, and the red-proofs
|
||||
|
||||
| test | red-proof (each seen to fail, then reverted) |
|
||||
| what was broken | what convicted |
|
||||
|---|---|
|
||||
| `TestMetaForHuIsIdentity` (53 real files) | a copy field mutated inside `For`'s hu branch — FAILED naming 53 apps |
|
||||
| `TestDeployFieldsMatchedByKey` | deploy fields matched by position — FAILED, "Second field" landed on FIRST |
|
||||
| `TestEmptyEnglishFallsBack` | a blank English string allowed to win — FAILED |
|
||||
| `TestForDoesNotMutateTheReceiver` | the defensive copy removed — FAILED, showing the written-through strings |
|
||||
| `TestOverlayMatches…` (optional_config) | groups matched by position — FAILED |
|
||||
| `TestOverlayMatches…` (integrations) | integrations matched by position — FAILED |
|
||||
| `TestOverlayMatches…` (data paths) | data paths matched by position — FAILED |
|
||||
| `TestLocalizeStacksLeavesHungarianSliceAlone` | `I18n` left on the value handed to the page — FAILED |
|
||||
| `TestNoDirectMetaCopyReadOnPages` | `_ = st.Meta.Description` added to `logsHandler` — FAILED naming file and field |
|
||||
| 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 |
|
||||
|
||||
**Two of the nine convicted a hollow TEST rather than the code, and that is the finding worth
|
||||
keeping.**
|
||||
**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.
|
||||
|
||||
- `TestForDoesNotMutateTheReceiver` first did the obvious thing: copy the struct, call `For`,
|
||||
`DeepEqual` the copy against the original. **A struct copy shares its slices' backing arrays**, so
|
||||
a write through `out.DeployFields[i].Label` changes BOTH sides and the compare passes. It passed a
|
||||
deliberately broken merge. It now snapshots the concrete strings before the call.
|
||||
- The optional-config / integrations / data-path fixture first carried ONE entry per list. With one
|
||||
entry, position and key agree, and a position-matching implementation passes. It passed. The
|
||||
fixture now carries two entries per list with only the second translated.
|
||||
## Proven live, twice
|
||||
|
||||
Full suite, vet and build green; `controller_gates.py` green (17 gates).
|
||||
- **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.
|
||||
|
||||
## Live proof — endpoint level, and that is stated because it matters
|
||||
## What the walk then found, which this release does not fix
|
||||
|
||||
No browser exists on DooPlex. Every page was fetched with `curl` from inside guest 9201 at the
|
||||
controller's container address, with the customer `Host` header and a real logged-in session: the
|
||||
same handlers and the same templates a browser drives, with only the drawing skipped.
|
||||
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:
|
||||
|
||||
- **demo-hp, 0.257.0.** With the catalog pilot synced, `/apps/privatebin`, `/apps/paperless-ngx`,
|
||||
`/apps/romm`, `/stacks` and two deploy pages render the English catalog text on `?lang=en`.
|
||||
- **The Hungarian did not move.** The same seven pages in Hungarian, before and after the catalog
|
||||
push, are byte-identical apart from the per-session CSRF token: equal raw byte counts (43 253 /
|
||||
41 532 / 41 547 / 147 121 / 74 326 / 70 290) and equal hashes once the token is normalised.
|
||||
- **demo-felhom, 0.255.0 — the old-controller check.** With the block synced onto that box
|
||||
(confirmed positively: the sync named the three apps, and the block is in both the cache and the
|
||||
stack copy), its pages hash identically before and after, and its log carries **no** parse warning
|
||||
— in a 93-line window that contains the sync's own lines, so the absence is evidence and not a
|
||||
dead log.
|
||||
- **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.
|
||||
|
||||
Evidence: `felhom.eu/documentation/audits/i18n-slice5-2026-09-20/`.
|
||||
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.
|
||||
|
||||
## What is still Hungarian on an English app page
|
||||
## Needs the operator
|
||||
|
||||
Measured with an accent scan AND an ASCII-folded stem scan, both with a positive and a negative
|
||||
control. Three things, and the first is correct:
|
||||
|
||||
1. **the language picker's own „Magyar" button** — a picker names each language in its own tongue;
|
||||
2. **the update badge „Naprakész" and its tooltip** — **R-589**. Slice 1 listed it (§2.3); slice 2
|
||||
was the slice that was to take it, and closed without it;
|
||||
3. **the data-folder card's consequence sentence** — **R-590**. This one makes a promise about the
|
||||
customer's files, and the label above it is already English, so the line reads half and half.
|
||||
|
||||
The Apps LIST also shows fifty Hungarian descriptions — those are the apps nobody has translated yet.
|
||||
|
||||
## Gaps filed
|
||||
|
||||
**R-589** (the update badge) · **R-590** (the data-folder promise) · **R-591** (`Stack.Copy()` deep-copies
|
||||
five `Meta` fields and not the new `I18n` map — safe today because nothing mutates an overlay, which is
|
||||
exactly why it is worth a row) · **R-592** (three defects inside the new catalog gate, each found by its
|
||||
own decoy).
|
||||
|
||||
## The floor was raised to 0.257.0, and it delivered function rather than a version string
|
||||
|
||||
The operator asked for the floor at the end of the translation work, so it was raised the same
|
||||
session: `min_controller_version=0.257.0`, `min_agent=0.131.0` — the value this release's CHANGELOG
|
||||
header declares. The floor is **above the vouched golden (0.246.0)**, so the declaration is what
|
||||
carries it (R-472); an undeclared floor there is held by design.
|
||||
|
||||
Observables, both positive:
|
||||
|
||||
- Hub: `managed floor SERVED for demo-felhom: floor 0.257.0, agent requirement "0.131.0" from
|
||||
declared (golden 0.246.0)`.
|
||||
- Box: **demo-felhom went 0.255.0 → 0.257.0 by itself in under 12 seconds**, healthy, its own log
|
||||
reading `settle-gate: GO — at/above floor 0.257.0 (we are 0.257.0)`.
|
||||
- **And then it rendered the English tagline** for an app whose block it had just synced. That is the
|
||||
observable that matters: the floor delivered the FEATURE, not just a version number.
|
||||
|
||||
**Peti's box and tester-1 are DOWN** and take it unattended when they return — untested on this
|
||||
version, which is the standing situation for every floor raise.
|
||||
|
||||
## The whole catalog followed the same day
|
||||
|
||||
The operator read the pilot's English and said go, so Part C shipped too: 50 more apps in three
|
||||
pushes, 1 031 of the catalog's 1 032 strings. **The English Apps list now shows zero Hungarian app
|
||||
descriptions across all 53 apps** — the only Hungarian left on it is this release's own two gaps
|
||||
(R-589's „Naprakész" badge, ten occurrences) and the language picker naming itself.
|
||||
**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 any box; no app deployed. Guest 9201 on demo-hp was restarted onto the
|
||||
new image, which is the deploy. The staged password file and the capture scripts were shredded from
|
||||
both Proxmox hosts and both guests. Both boxes left on Hungarian.
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user