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

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:
2026-09-18 14:19:31 +02:00
parent eb6aa58aa7
commit 48f3336956
142 changed files with 2199 additions and 318 deletions
+61
View File
@@ -1,3 +1,64 @@
## 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.
**Hungarian changes in exactly TWO places, both declared and both measured** — the sidebar footer of
every dashboard page, and the three pages a visitor meets. Everything else is byte-identical.
**1. The notes a background run SAVES now follow the language.** The line under last night's backup,
the last error, the proof result, the restore outcome — written in the BOX's language at the moment
they are written (`util.Text(lang, key, …)`, `m.note`/`s.note`). The consequence, stated rather than
hidden: **a household that switches sees the note from the run before in the old language until the
next run rewrites it.** That is the operator's §16 option 1; storing a code and rendering live would
need a dozen new persisted fields and a legacy path for each — the R-570 shape a dozen times over.
Converted: the off-site failure classes and quota notes, the tier-2 reasons and warnings, the undo
phrases, the reconstitute hold, the restore record, and the whole restore-outcome family.
**`EndRestoreOp` no longer receives a Hungarian literal from anywhere.**
**2. 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 find their way out of Hungarian. It is
`<details>`/`<summary>` — a menu with no script, which opens on click and on Enter and which a screen
reader announces. Language names inside are shown in their own language and are never translated.
**3. The sign-in, claim and recovery pages get the same globe — and a visitor's choice stays theirs.**
Those pages are met by someone who has not signed in. They have no setting to read, and they must not
write the household's: a box's sign-in page is reachable by anyone who can reach the box. So the
choice lives in the visitor's own browser (`felhom_lang`, display-only, one of two values), and
`langFor` reads it **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 exempt from CSRF for a narrow, checkable reason, written down where it is made: the
only thing a forged request can achieve is to change the language of the page the victim's own browser
shows them. It writes one cookie, reads nothing, touches no setting, and `safeBackPath` keeps its
redirect on this box (a protocol-relative `//evil.example` is refused — "starts with /" alone is not
the test). **If that handler ever gains a second effect, it needs CSRF that day.**
**§16, the operator's default, taken: a claim CARRIES the visitor's language.** Someone who switched
the claim page to English and then claimed the box chose English deliberately. Only on success, and
only there — the one moment an anonymous visitor becomes the household.
**No globe on the guest share pages or the not-found page.** A share visitor is a stranger and the
household's setting is the wrong default for them; that is a promise about the share feature and an
operator decision (**R-577**). Pinned by `TestGuestSharePagesHaveNoGlobe` so it stays a decision.
**The two parity exceptions, measured rather than asserted.** 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), with 5 byte-identical, which are precisely the three pages that must not change. The
whole diff is in `felhom.eu/documentation/audits/i18n-slice2-2026-09-18/C/parity-exception-diff.txt`.
**Two defects this release found in its own measurement, recorded rather than tidied away:**
- The parity HARNESS rendered the three visitor shells through the DASHBOARD path, so the fixture
would have baked a globe posting to `/settings/language` with a CSRF field — a form the real page
never serves. Caught by reading the diff before re-capturing. The harness now branches on
`i18nDirectTemplates`.
- The first "is the change only the two blocks?" 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.**
`R-575` folded in where it was one call (`tier2NoTargetReason` now returns a note in the box's
language); the soft memory-overcommit warning stays as its row says.
## v0.253.0 — Errors carry the key of the sentence they are (2026-09-18, R-557 slice 2 release B)
**MinAgent: 0.131.0** (unchanged). **No hub release needed.** Every Hungarian sentence is byte-identical