v0.254.0 — the saved notes follow the language, and the switch becomes a globe (R-557 slice 2 release C; SLICE 2 CLOSED)
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
The notes a background run SAVES — last night's backup line, the last error, the proof result, the restore outcome — are written in the BOX's language at the moment they are written. A household that switches sees the previous run's note in the old language until the next run rewrites it: the operator's §16 option 1, stated rather than hidden. EndRestoreOp no longer receives a Hungarian literal from anywhere. The language switch is a globe. Two text links wrapped in the sidebar footer and asked the reader to recognise "Magyar"/"English" as links; a globe is the one symbol every web user already reads as "language", so nobody has to read Hungarian to escape Hungarian. It is <details>/<summary> — a menu with no script, drawn inline because the icon sprite lives only in layout.html and the visitor pages have their own shell. Those visitor pages get the same globe, and a visitor's choice stays theirs: a display-only felhom_lang cookie that langFor reads ONLY when there is no session. A signed-in household can never inherit a language a previous visitor picked in the same browser. POST /lang is CSRF-exempt for a narrow reason written at the exemption — its only achievable effect is the language of the page the victim's own browser shows them — and safeBackPath refuses //evil.example as well as https://, because "starts with /" alone is not the test. §16 taken: a successful claim carries the cookie into the household's setting. TWO PARITY EXCEPTIONS, MEASURED: 106 fixtures compared with a real diff — exactly two change shapes (the dashboard footer, the globe in the shells) and 5 byte-identical, which are the three pages that must not change. I INTRODUCED A DEADLOCK AND THE SUITE CAUGHT IT BY HANGING. UpdateOffboxStatus holds the settings write lock while running its callback; boxLang() wants the read lock; sync.RWMutex is not reentrant. On a real box an off-site run would have hung forever HOLDING the settings lock. Fixed by resolving the language before the callback, and guarded by a test that names the file and line in a second instead of hanging for 25 minutes. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# REPORT — localisation slice 2, releases A and B (controller v0.252.0 + v0.253.0, R-557)
|
||||
# REPORT — localisation slice 2, COMPLETE (controller v0.252.0 + v0.253.0 + v0.254.0, R-557)
|
||||
|
||||
**2026-09-18 · base commit `736f54b49610` (v0.251.0) · MinAgent 0.131.0, unchanged · no hub release.**
|
||||
**Two releases this session: A (the sentences shown now) and B (the 179 error messages).**
|
||||
**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.
|
||||
@@ -193,3 +193,82 @@ are not red-proofs — nobody planted them.
|
||||
translated on purpose), `handler_debug.go` (R-574), the notifier's 27 wire messages (R-558),
|
||||
`funcmap.go`'s two un-overridden helpers (R-572), the text a background run PERSISTS (release C), and
|
||||
the R-570 producer. Release C's default stands: written in the box's language at write time.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Release C — v0.254.0: the saved notes, and a globe for the switch. SLICE 2 CLOSED.
|
||||
|
||||
## 1. Claims in the prompt that live source disproved — named first
|
||||
|
||||
| the prompt said | source says |
|
||||
|---|---|
|
||||
| "Release A's survey (persisted list, wire list)" is in `audits/i18n-slice2-2026-09-18/A/` | That folder holds only `live/`. The persisted list is in this REPORT's §6 for release A. Derived again from source here, which is the stronger reading — and it found the largest persisted group (`EndRestoreOp`'s outcome family, ~40 literals in two files) that a list would have had to remember. |
|
||||
| "the three shells share no layout" | **True, and confirmed by reading**: `login.html`, `claim.html`, `recovery.html` each open their own `<html lang="{{T "layout.html_lang"}}">`. But the consequence was missed: the icon SPRITE lives only in `layout.html`, so a `<use href="#i-globe">` would render nothing on those three. The globe is drawn INLINE in the partial instead — one definition, four places, no sprite coupling. No dead symbol was added. |
|
||||
| "no cookie is read anywhere for language" | Confirmed by grep: no language cookie existed. |
|
||||
| the `<details>` menu "working without script in the browsers the guide names" | **Cannot be confirmed here.** `claude-in-chrome` is not available on DooPlex, so no browser rendered this page. `<details>`/`<summary>` is a plain HTML element with no script, and the markup is asserted; whether it LOOKS right is an operator click-through. Said plainly rather than implied. |
|
||||
|
||||
## 2. What shipped
|
||||
|
||||
**The saved notes.** ~70 producers across `internal/backup` (off-site classes and quota, tier-2
|
||||
reasons and warnings, undo phrases, the reconstitute hold, the restore record) and `internal/web`
|
||||
(the whole `EndRestoreOp` outcome family). Each renders through `util.Text(boxLang(), key, …)`.
|
||||
**`EndRestoreOp` no longer receives a Hungarian literal from anywhere** (grep, with the negative
|
||||
control). `s.noteErr`/`m.noteErr` render release B's errors into a note in the same language.
|
||||
|
||||
**The globe.** One partial (`lang_globe.html`), used by the dashboard footer and the three visitor
|
||||
shells; drawn inline in the same stroke style as the sprite. `langFor` gained one step, and the order
|
||||
is now fixed: `?lang=` → **household setting when a session exists** → visitor cookie when none →
|
||||
setting → `hu`. A signed-in household never reads the cookie, which is the row that protects them.
|
||||
|
||||
**`POST /lang`** — CSRF-exempt for a narrow, checkable reason written at the exemption: the only
|
||||
achievable effect is changing the language of the page the victim's own browser shows them. It writes
|
||||
one cookie, reads nothing, and `safeBackPath` refuses `//evil.example` as well as `https://…` —
|
||||
"starts with /" alone is not the test, and that is the mistake the function exists to not make.
|
||||
|
||||
**§16 taken (the operator's stated default): a claim carries the visitor's language**, on success
|
||||
only, at the one moment an anonymous visitor becomes the household.
|
||||
|
||||
## 3. The two parity exceptions, measured
|
||||
|
||||
106 fixtures rendered and compared with a **real (LCS) diff**: exactly **two change shapes** — the
|
||||
dashboard footer (89 fixtures) and the globe in the shells (12) — and **5 byte-identical**, which are
|
||||
precisely the two guest share pages and the catch-all, the three that must not change. Full diff:
|
||||
`audits/i18n-slice2-2026-09-18/C/parity-exception-diff.txt`.
|
||||
|
||||
**Deviation, named:** the prompt asked for one commit per re-capture. This is one commit, because
|
||||
splitting them leaves an intermediate commit whose suite is red — and a commit that does not pass is
|
||||
worse than a commit whose EVIDENCE separates the two blocks, which this one does explicitly.
|
||||
|
||||
## 4. Two defects release C found in its own work
|
||||
|
||||
**(a) A DEADLOCK I introduced, caught by the suite hanging.** `UpdateOffboxStatus` holds the settings
|
||||
WRITE lock while it runs its callback; `boxLang()` reads the language through the settings READ lock.
|
||||
`sync.RWMutex` is not reentrant. The first draft rendered a note inside that callback — on a real box
|
||||
an off-site run would have hung **forever, holding the settings lock**, wedging everything else that
|
||||
touches `settings.json`. The only symptom was `go test` timing out at 25 minutes instead of 8.
|
||||
Fixed by resolving the language before the callback; `TestNoteHelpersAreNotCalledUnderTheSettingsLock`
|
||||
now names the file and line in a second instead. **A hang is the worst kind of failure to diagnose,
|
||||
and the lesson is: a helper that takes a lock must never be called from inside a callback that holds
|
||||
one.**
|
||||
|
||||
**(b) The first "only two blocks changed?" measurement compared LINE BY INDEX.** An insertion shifts
|
||||
every line below it, so it reported 60 520 changed lines and measured nothing. **A line-index compare
|
||||
is not a diff.** Redone with difflib.
|
||||
|
||||
Also caught before it shipped: the parity HARNESS rendered the three shells through the DASHBOARD
|
||||
path, which would have baked a form the real page never serves. Found by reading the diff before
|
||||
re-capturing, and independently by `TestI18nDirectRenderPagesFollowLanguage`.
|
||||
|
||||
## 5. Green gate
|
||||
|
||||
`go build ./... && go vet ./... && go test ./...` green; all controller gates OK. Red-proofs 15–19 in
|
||||
`audits/i18n-slice2-2026-09-18/redproofs.txt`, each seen to convict.
|
||||
|
||||
## 6. What is left after slice 2
|
||||
|
||||
**629 Hungarian literals**, none of them errors and none of them saved notes: the country TABLE (113,
|
||||
not on the wire, not translated on purpose), the page-title literals slice 1 handles through
|
||||
`TitleKey`, `handler_debug.go` (R-574), the notifier's 27 wire messages (R-558), `funcmap.go`'s two
|
||||
un-overridden helpers (R-572), the R-570 producer, and the soft memory warning (R-575).
|
||||
**R-557 is CLOSED.** New row: **R-577**, the guest-share visitor's language.
|
||||
|
||||
Reference in New Issue
Block a user