diff --git a/CHANGELOG.md b/CHANGELOG.md index a72ec69..9a4ec69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +## v0.255.0 — The globe on the sign-in-flow pages: styled, and inside the card (2026-09-18, fixes v0.254.0) + +**MinAgent: 0.131.0** (unchanged). **No hub release needed.** Nothing on the wire moved. One Hungarian +block changes, on the pages outside the dashboard chrome only — **zero dashboard fixtures were +re-captured**. + +Two defects in v0.254.0's globe, both visible on a real browser and neither catchable by any test that +existed: + +**1. The stylesheet was requested with no cache-buster, so the globe rendered UNSTYLED.** `layout.html` +has always used `/static/style.css?v={{.Version}}`; the shells did not. A browser that had the file +cached from before v0.254.0 kept serving a copy with no `.lang-globe` rules in it, and the globe came +out as a bare `
` — a stray triangle and two plain words, floating at the edge of the window. +**It was not three shells but FIVE**: `login`, `claim`, `recovery`, **and both guest share pages**. The +two share pages have no globe, but they have the same stale-stylesheet problem for any CSS change, and +their visitor is the likeliest of all to be holding an old copy. All five fixed. + +**And `.Version` was not in the data of three of them** — which is exactly how the next shell would +have been missed. It is now set in `executeTemplateLang`, the single choke point every shell renders +through, so a new shell cannot forget it. + +**2. The globe floated outside the card.** `.shell-lang` was pinned to the corner of the VIEWPORT, so +it read as part of the browser rather than part of the page. It now sits **inside the card, centred +under the footer**, with the menu opening upward — which is the shared `.lang-globe-menu` rule, so the +block adds no positioning of its own and the dashboard and the shells cannot drift apart. + +**A third defect, found by an existing test while fixing the first two.** Putting `?v={{.Version}}` +on the guest share pages would have printed the controller version onto a page **a stranger holding a +capability URL can open** — `TestShareGuest_HeadersTilesNoAdminChrome` refuses exactly that, and +refused it. Those two pages now take an **opaque per-build tag** (`AssetTag`, four bytes of a hash of +the version): it busts a cache just as well and tells a visitor nothing about which build is running. +The fill lives in one function, `addShellAssetData`, called by `executeTemplateLang` **and** by the +parity harness — because a fixture rendered through a different data path is a picture of a page +nobody serves, which this release's predecessor got wrong twice. + +`TestGlobeOnAnonymousShells` gained both halves — the stylesheet carries a non-empty `?v=`, and the +globe is inside the card and below the footer. Each was red-proofed by restoring the defect's exact +shape. The test server gained a version, because an empty `?v=` busts a cache exactly once and never +again, and baking that into a fixture would have hidden it. + ## v0.254.0 — The saved notes follow the language, and the switch becomes a globe (2026-09-18, R-557 slice 2 release C — SLICE 2 CLOSED) **MinAgent: 0.131.0** (unchanged). **No hub release needed.** Nothing on the wire moved. diff --git a/CONTEXT.md b/CONTEXT.md index 7159edc..32b41d6 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,7 +7,9 @@ > > Ask Claude Code: "Please update CONTEXT.md with what we did today" -Last updated: 2026-09-18 (v0.254.0 — localisation slice 2 CLOSED: saved notes follow the language, the switch is a globe) +Last updated: 2026-09-18 (v0.255.0 — the globe on the sign-in-flow pages: styled, and inside the card) + +> **2026-09-18 — v0.255.0 (fixes the v0.254.0 globe).** The sign-in-flow pages requested `style.css` with **no `?v=`**, so a browser holding a pre-0.254.0 copy served CSS with no `.lang-globe` rules and the globe rendered as a bare unstyled `
` — **invisible to every test, because they all read markup and the fault was in which CSS file the browser fetched**. It was FIVE shells, not three (both guest share pages too), and `.Version` was missing from three of their data maps — now set once in `executeTemplateLang`. The globe also moved INSIDE the card, centred under the footer, menu opening upward via the shared rule. 15 shell fixtures re-captured, **zero dashboard ones**. R-579. > **2026-09-18 — FLEET FLOOR RAISED to 0.254.0** (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 … from declared`; demo-felhom went 0.253.0 -> 0.254.0 **by itself in ~40 s**, healthy, four other containers up, own log `settle-gate: GO — at/above floor 0.254.0`, and its sign-in page shows **one globe, zero old text links**. **Peti Proxmox (DOWN 65 d, 0.115.0) and Tester 1 (DOWN 1 d, 0.245.0) did NOT get it** and take it unattended when they return — untested on this version. Evidence: `felhom.eu/documentation/audits/i18n-slice2-2026-09-18/floor-raise-0.254.0.md`. diff --git a/REPORT.md b/REPORT.md index a9c6a3a..207a12c 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,274 +1,70 @@ -# REPORT — localisation slice 2, COMPLETE (controller v0.252.0 + v0.253.0 + v0.254.0, R-557) +# REPORT — the globe on the sign-in-flow pages, fixed (controller v0.255.0) -**2026-09-18 · base commit `736f54b49610` (v0.251.0) · MinAgent 0.131.0, unchanged · no hub release.** -**Three releases: A (the sentences shown now), B (the 179 error messages), C (the saved notes + the globe).** -Architecture read first and named: `felhom.eu/documentation/architecture/10-localisation.md` §1 -(parity), §2 (mechanism), §5 (gates), §9 (the R-553 signals), §10 (this slice); `07-backup-architecture.md` -for what the hub reads from a box; `05-hub-architecture.md` for what the hub composes itself. - ---- - -## 1. Claims in the task that live source disproved — named first - -| the task said | source says | -|---|---| -| "`cloudflare/countries.go` is used by `report/types.go` (on the wire)" | **No.** `report/builder.go` puts country **CODES** on the wire (`AllowedCountries []string`); no country NAME leaves the box. `CountryName()` has no caller at all. So the names are display-only and were free to translate — which is what this release did. | -| "the hub's dispatcher composes its own customer mail from event kinds (grep of `dispatcher.go` for `.Message`: nothing)" | **Wrong in substance.** `.Message` finds nothing because the message arrives as a PARAMETER, not a field. `FormatCustomerEmail` (hub `notify/templates.go` L167-210) uses `customerMessages[eventType]` when it has one and **falls back to the controller's message** when it does not — and appends it as „- Üzenet: %s" whenever the two differ. Several event types deliberately carry no entry so the controller's sentence IS the mail (hub `api/handler.go`, the notes at L2034/L2045/L2066). The conclusion (do not translate them) was right; the reason was not. | -| "5 flash readers / 8 writers" | 8 writers, **8 read sites** — the three named plus `auth.go:100` (login), `server.go:498` (claim) and the `flash_error` twins. All eight converted. | -| per-file counts (`handlers.go` 121, `offbox_handlers.go` 72, `storage_handlers.go` 48, `handler_debug.go` 40, `offbox_reconstitute.go` 41, `notify/notifier.go` 30, `api/router.go` 19) | measured at `736f54b49610`: **120 / 70 / 45 / 39 / 38 / 31 / 18**. Totals: **1 120** Hungarian literals, not 1 141. | -| "`scripts/i18n_inventory.py`" (as a controller script) | it lives in **`felhom.eu/scripts/`**. Its Go-literal walker was copied into the new gate rather than imported — a controller clone need not sit beside felhom.eu, and a gate that can fail to import its scanner can silently skip. | - -## 2. What shipped - -**226 Go literals converted**, plus 237 country names now available in English. 426 keys, each -recorded in `scripts/i18n_go_keys.json` against the literal it replaced. - -| file | before | after | converted | of the rest, `fmt.Errorf` (release B) | -|---|---|---|---|---| -| `internal/web/handlers.go` | 120 | 44 | 76 | 0 | -| `internal/web/offbox_handlers.go` | 70 | 22 | 48 | 0 | -| `internal/web/storage_handlers.go` | 45 | 19 | 26 | 16 | -| `internal/api/router.go` | 18 | 0 | 18 | 0 | -| `internal/web/share_handlers.go` | 22 | 5 | 17 | 0 | -| `internal/web/sharing_handlers.go` | 18 | 3 | 15 | 1 | -| `internal/web/alerts.go` | 13 | 0 | 13 | 0 | -| `internal/web/tier2_config_handler.go` | 8 | 3 | 5 | 0 | -| `internal/api/geo.go` | 5 | 0 | 5 | 0 | -| `internal/web/backup_handlers.go` | 12 | 9 | 3 | 0 | -| `internal/cloudflare/countries.go` | 113 | 113 | 0 (the TABLE stays — it validates codes) | 0 | - -Mechanism: `i18n.Bundle.Msgf`; `web.(*Server).msg/msgLang/msgN`; `api.(*Router).msg/msgLang/langFor/countryName`; -`flashQuery`/`flashText`/`flashFrom`; `Alert.MessageKey`/`MessageArgs`/`LinkTextKey` + `Alert.rendered(lang)`; -`TitleArgs` for the four app-named titles (R-566, closed). - -**Word order without a second syntax.** The task asked for a named-parameter (`{{.Name}}`) form for -multi-parameter messages. It is not here, deliberately: English reorders with Go's own explicit -argument indexes (`%[2]s`), which `fmt` already understands. The Hungarian value then stays the format -string the code always had, byte for byte — which is exactly what the parity gate compares, so the -measurement needs no exception. `TestBundleParametersMatchAcrossLanguages` counts an indexed verb as -one verb, so a reordered English still has to use the same verbs on the same values. - -## 3. The wire — surveyed, frozen, not translated (A.1) - -Everything a HUB reads keeps its bytes. Filed against **R-558** (slice 3 owns it): - -| producer | where it lands | why it stays | -|---|---|---| -| `internal/monitor/healthcheck.go` — 5 storage sentences | report `health.warnings` / `health.issues` | the hub stores and shows them to the operator | -| `internal/notify/notifier.go` — 31 event messages | event `message` → `FormatCustomerEmail` | **the household reads these in an e-mail**; several types have no `customerMessages` entry precisely so this sentence is the mail | -| `internal/cloudflare/countries.go` — the table | nothing (codes only) | it validates codes; the DISPLAY name is what follows the language | - -Pinned by `TestStorageWarningWireTextIsFrozen`, `TestStorageWarningFormatsAreFrozen`, -`TestEventMessageWireTextIsFrozen`, `TestEventMessagesCarryNoBundleKey`. Both goldens were captured -from the producers at the base commit, before any change, and both were seen to fail. - -The **persisted** text found by the same survey is listed in §6 for release C. - -## 4. Tests and their red-proofs - -Every one was run against a planted break and watched to convict; the planted change was then -reverted and the suite re-run green. Full log: `audits/i18n-slice2-2026-09-18/redproofs.txt`. - -| test / gate | what was planted | what convicted | -|---|---|---| -| `i18n_go_parity.py` | one byte added to a hu.json value | `CHANGED`, naming the key and the base literal | -| `i18n_go_parity.py` | a key citing text no literal has | `INVENTED`, naming the text | -| `i18n_go_parity.py` | a joined key with reworded fragments | `INVENTED`, naming the fragment | -| `test_gate_decoys.py` go-parity ×3 | reworded / invented / unlisted | all three rejected | -| `TestEventMessageWireTextIsFrozen` | „telepítve" → „telepitve" | got/want printed | -| `TestStorageWarning*IsFrozen` | a wire warning translated | both tests | -| `TestFlashOnAServerWithNoBundleField` | `s.bundle()` without the `i18n.Shared` fallback | the page showed `flash.share.enabled` | -| `TestFlashKeyRoundTrip` | — | legacy prose, unknown key, `