Files
felhom.eu/documentation/audits/i18n-slice2-2026-09-18/C/live/README.md
T
admin 95c10954ae
gates / gates (push) Successful in 23s
docs: localisation slice 2 CLOSED (controller v0.254.0) — R-577, R-578, and a probe rule
10-localisation.md §10.3: the saved notes follow the box language at write time, with the
one-night consequence stated rather than hidden; the globe, and the table of WHO reads which
page and where its globe posts — getting that wrong makes the button do nothing, which it did
on /recovery until the live probe found it. Decision 6 superseded a second time; decision 8
(a claim carries the visitor's language) recorded. Decision 5 of §11's anonymous-surface line:
changing what a VISITOR reads is within what an anonymous request may do; changing anything the
household owns is not, and POST /lang can do only the first.

R-578 — the deadlock, and why it is a row rather than a fixed bug: 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 that lock. The
symptom was a test suite going from 8 minutes to a 25-minute timeout. Fixed and guarded, but the
guard covers one package and three helper names; the class needs a gate.

R-577 — a guest share visitor still has no way to pick a language, and the household's setting
is the wrong default for a stranger. Deliberately left, pinned by a test, and the operator's to
decide because it is a promise the share feature makes.

.claude/rules/live-probes.md, unconditional: never send a deploy request for an app that is not
installed, not even expecting a refusal — the endpoint accepts first and validates later. Two
sessions made that mistake in two days, the second WITH a prompt line forbidding it. A prompt is
read once; a rule file is loaded every session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-18 14:31:49 +02:00

4.2 KiB

Live validation — controller v0.254.0 on demo-hp guest 9201 (2026-09-18, release C)

Method: endpoint-level, stated as such. No browser on DooPlex. Nothing was installed, created, formatted or deleted, and the deploy endpoint was not touched at all — the new rule (.claude/rules/live-probes.md) written after yesterday's mistake. The password was passed as a file and deleted from host and guest afterwards.

1. A visitor's language is their own (no session) — before and after

probe 0.253.0 0.254.0
globe on /login 0 1
POST /lang lang=en back=/login 302 (no such route → the login redirect) 303, felhom_lang=en set
/login with that cookie <html lang="hu"> <html lang="en">, and "Forgot password" present
settings.json language after it "hu" "hu" — unchanged. An anonymous request cannot write what the household owns
POST /lang lang=xx 302 400, no cookie
POST /lang back=//evil.example/x the login redirect / — a protocol-relative URL is another origin
globe on /claim 0 1

/launcher with a session and the felhom_lang=en cookie → <html lang="hu">. The cookie is not read once there is a session; that is the row that keeps a household from inheriting a language a previous visitor picked in the same browser.

3. The dashboard globe, end to end

POST /settings/language lang=en (session CSRF) → 302 → settings.json "language": "en" → /launcher, /backups, /backups/remote all <html lang="en"> → switched back → "hu".

The footer renders in order — version, globe, sign-out — and the old two-text-link switch is gone (lang-switch-btn count 1 → 0):

<div class="sidebar-footer">
    <span class="version">0.254.0</span><details class="lang-globe">
    <summary class="lang-globe-btn" aria-label="Nyelv" title="Nyelv"><svg class="ico" …globe…/></summary>
    <ul class="lang-globe-menu">
        <li><form method="POST" action="/settings/language">…<button … class="lang-globe-item current" aria-current="true">Magyar</button></form></li>
        <li><form method="POST" action="/settings/language">…<button … class="lang-globe-item">English</button></form></li>
    </ul>
</details>
    <a href="/logout" class="logout-link">Kijelentkezés ↗</a>

4. The saved note, and the §16 consequence, seen live

With the box switched to English, the stored tier-2 note stayed as it was written: "last_warning": "A belső SSD-n csak a konfiguráció, adatbázis és a köte…". That is the operator's choice working, not a bug: a saved note is written in the box's language at write time and shown verbatim afterwards, so a household that switches sees the previous run's note in the old language until the next run rewrites it.

5. What could NOT be proven live, and why

The /recovery globe. recoveryPageHandler redirects to /backups/remote unless recoveryOffer() is true — the page exists only while a box is in a lost-machine situation, and demo-hp is not. It 302s with or without a session. So its globe is proven by the render tests (TestGlobeOnAnonymousShells, TestI18nDirectRenderPagesFollowLanguage) and by its 7 parity fixtures, not live. Said plainly rather than left to be assumed from the other rows.

That page is also where release C's own defect was: it is an AUTHENTICATED route, so its globe must write the household's SETTING; the first draft gave it the anonymous form, which sets a cookie langFor ignores once there is a session — the button would have done nothing. Found by this probe reporting no globe on /recovery and then reading the route table.

The <details> menu opening in a browser. claude-in-chrome is not available on DooPlex. The markup is asserted; whether it looks and behaves right on screen is a manual click-through.

6. State left behind

Controller 0.254.0; saved language hu; the visitor cookie was set only inside the probe's own curl jar and is gone with it. 23 standing containers up, unchanged. Nothing installed, nothing removed, no drive touched, no floor raised, no golden baked. Provisioned nothing.