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:
@@ -18,14 +18,16 @@ import (
|
||||
// Shape: one JSON file in the controller's state directory (DataDir, beside settings.json), written
|
||||
// atomically (atomicWrite: tmp + rename) at BOTH ends of an op. At startup a record still marked
|
||||
// running is, by construction, a restore nothing is running any more: it becomes a terminal failure
|
||||
// (Interrupted, RestoreInterruptedMessage) and a per-app notice that stays until that app's next
|
||||
// (Interrupted, RestoreInterruptedKey) and a per-app notice that stays until that app's next
|
||||
// restore. LoadRestoreRecord reports the conversion ONCE, so the caller raises restore_interrupted once.
|
||||
//
|
||||
// No path set (tests that build a bare Manager, a box with backup disabled) = the old in-memory
|
||||
// behaviour, silently: persistence is a property of the wired controller, not of every Manager.
|
||||
|
||||
// RestoreInterruptedMessage is what the household reads when the box stopped mid-restore.
|
||||
const RestoreInterruptedMessage = "A visszaállítás megszakadt (a doboz újraindult) — indítsd el újra."
|
||||
// RestoreInterruptedKey names what the household reads when the box stopped mid-restore. It is
|
||||
// SAVED in the restore record and read on the page afterwards, so it is rendered in the box's
|
||||
// language at the moment it is written (release C, R-557).
|
||||
const RestoreInterruptedKey = "note.restore.interrupted"
|
||||
|
||||
// restoreRecordFile is the on-disk shape.
|
||||
type restoreRecordFile struct {
|
||||
@@ -76,7 +78,7 @@ func (m *Manager) LoadRestoreRecord() *RestoreOpResult {
|
||||
}
|
||||
res := RestoreOpResult{
|
||||
Op: rec.Op, Stack: rec.Stack, OK: false,
|
||||
Message: RestoreInterruptedMessage, FinishedAt: time.Now(), Interrupted: true,
|
||||
Message: m.note(RestoreInterruptedKey), FinishedAt: time.Now(), Interrupted: true,
|
||||
}
|
||||
m.opLast = &res
|
||||
if m.opInterrupted == nil {
|
||||
|
||||
Reference in New Issue
Block a user