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:
@@ -22,9 +22,9 @@ func TestR403_SkippedUnitLegIsNotRenderedAsFresh(t *testing.T) {
|
||||
const runDate = "2026-08-31T03:30:00Z"
|
||||
const pkgDate = "2026-08-25T03:30:00Z"
|
||||
|
||||
stale := r103Row(true, pkgDate, true)
|
||||
stale := r103Row(t, true, pkgDate, true)
|
||||
stale.Tier2LastRun, stale.Tier2LastSuccess = runDate, runDate
|
||||
stale.Tier2UnitStaleNotice = staleNoticeFor(pkgDate)
|
||||
stale.Tier2UnitStaleNotice = staleNoticeFor(t, pkgDate)
|
||||
|
||||
html := renderBackupPage(t, "backups_apps", baseBackupData([]AppBackupRow{stale}))
|
||||
|
||||
@@ -38,9 +38,9 @@ func TestR403_SkippedUnitLegIsNotRenderedAsFresh(t *testing.T) {
|
||||
|
||||
// NEGATIVE CONTROL: an ordinary row must NOT carry the notice, or D1 would pass on a page that
|
||||
// shows the warning to everybody.
|
||||
fresh := r103Row(true, runDate, true)
|
||||
fresh := r103Row(t, true, runDate, true)
|
||||
freshHTML := renderBackupPage(t, "backups_apps", baseBackupData([]AppBackupRow{fresh}))
|
||||
if strings.Contains(freshHTML, staleNoticeFor(pkgDate)) {
|
||||
if strings.Contains(freshHTML, staleNoticeFor(t, pkgDate)) {
|
||||
t.Error("an ordinary row carried the preserved-package notice")
|
||||
}
|
||||
if !strings.Contains(freshHTML, "/backup/tier2/unit-restore") {
|
||||
@@ -48,8 +48,8 @@ func TestR403_SkippedUnitLegIsNotRenderedAsFresh(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func staleNoticeFor(pkgDate string) string {
|
||||
return strings.Replace(tier2UnitStaleNoticeFmt, "%s", fmtRFC3339Local(pkgDate), 1)
|
||||
func staleNoticeFor(t *testing.T, pkgDate string) string {
|
||||
return noteServer(t).note(tier2UnitStaleNoticeFmtKey, fmtRFC3339Local(pkgDate))
|
||||
}
|
||||
|
||||
// D2 — TestR403_UnitRestoreOfferNamesTheOlderPackageDate.
|
||||
@@ -58,21 +58,21 @@ func staleNoticeFor(pkgDate string) string {
|
||||
// preserved leg it must name the PACKAGE's date and say why it is older than the copy's newest run.
|
||||
func TestR403_UnitRestoreOfferNamesTheOlderPackageDate(t *testing.T) {
|
||||
const pkgDate = "2026-08-25T03:30:00Z"
|
||||
staleConfirm := tier2UnitConfirmWithStaleness(pkgDate, true, true)
|
||||
freshConfirm := tier2UnitConfirmWithStaleness(pkgDate, true, false)
|
||||
staleConfirm := noteServer(t).tier2UnitConfirmWithStaleness(pkgDate, true, true)
|
||||
freshConfirm := noteServer(t).tier2UnitConfirmWithStaleness(pkgDate, true, false)
|
||||
|
||||
if !strings.Contains(staleConfirm, tier2UnitStaleClause) {
|
||||
if !strings.Contains(staleConfirm, noteHU(t, tier2UnitStaleClauseKey)) {
|
||||
t.Error("the confirm does not say the package is older than the newest run")
|
||||
}
|
||||
if !strings.Contains(staleConfirm, fmtRFC3339Local(pkgDate)) {
|
||||
t.Error("the confirm does not name the package's date")
|
||||
}
|
||||
// Everything the ordinary confirm promised is still promised.
|
||||
if !strings.Contains(staleConfirm, tier2UnitConfirmBase) || !strings.Contains(staleConfirm, tier2UnitConfirmContrast) {
|
||||
if !strings.Contains(staleConfirm, noteHU(t, tier2UnitConfirmBaseKey)) || !strings.Contains(staleConfirm, noteHU(t, tier2UnitConfirmContrastKey)) {
|
||||
t.Error("the stale confirm lost the overwrite warning or the additive contrast")
|
||||
}
|
||||
// NEGATIVE CONTROL: the ordinary confirm must NOT carry the clause.
|
||||
if strings.Contains(freshConfirm, tier2UnitStaleClause) {
|
||||
if strings.Contains(freshConfirm, noteHU(t, tier2UnitStaleClauseKey)) {
|
||||
t.Error("an ordinary confirm carried the preserved-package clause")
|
||||
}
|
||||
if staleConfirm == freshConfirm {
|
||||
@@ -80,14 +80,14 @@ func TestR403_UnitRestoreOfferNamesTheOlderPackageDate(t *testing.T) {
|
||||
}
|
||||
|
||||
// And it reaches the rendered markup, not only the constant.
|
||||
row := r103Row(true, pkgDate, true)
|
||||
row := r103Row(t, true, pkgDate, true)
|
||||
row.Tier2UnitConfirm = staleConfirm
|
||||
html := renderBackupPage(t, "backups_apps", baseBackupData([]AppBackupRow{row}))
|
||||
if !strings.Contains(html, "FIGYELEM") { // ASCII-only fragment, R-364
|
||||
t.Error("the stale clause never reached the page")
|
||||
}
|
||||
// The ASCII control: the same page WITHOUT the clause must not match.
|
||||
rowFresh := r103Row(true, pkgDate, true)
|
||||
rowFresh := r103Row(t, true, pkgDate, true)
|
||||
freshHTML := renderBackupPage(t, "backups_apps", baseBackupData([]AppBackupRow{rowFresh}))
|
||||
if strings.Contains(freshHTML, "FIGYELEM") {
|
||||
t.Error("the ASCII fragment matches a page that has no stale clause — the control fails")
|
||||
@@ -97,7 +97,7 @@ func TestR403_UnitRestoreOfferNamesTheOlderPackageDate(t *testing.T) {
|
||||
// The two-argument wrapper still produces the ordinary confirm — yesterday's callers are unchanged.
|
||||
func TestR403_TheOrdinaryConfirmIsUnchanged(t *testing.T) {
|
||||
const d = "2026-08-25T03:30:00Z"
|
||||
if tier2UnitConfirmMsg(d, true) != tier2UnitConfirmWithStaleness(d, true, false) {
|
||||
if noteServer(t).tier2UnitConfirmMsg(d, true) != noteServer(t).tier2UnitConfirmWithStaleness(d, true, false) {
|
||||
t.Error("the two-argument confirm is no longer the not-stale case")
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func TestR403_OutcomeNamesThePackageDateNotTheRunDate(t *testing.T) {
|
||||
UnitPackageDate: "2026-08-31T09:43:41Z", // the preserved package
|
||||
UnitLegPreserved: true,
|
||||
}
|
||||
msg := tier2UnitSourceMsg(cov)
|
||||
msg := noteServer(t).tier2UnitSourceMsg(cov)
|
||||
pkg := fmtRFC3339Local("2026-08-31T09:43:41Z")
|
||||
run := fmtRFC3339Local("2026-08-31T12:23:51Z")
|
||||
if !strings.Contains(msg, pkg) {
|
||||
@@ -124,14 +124,14 @@ func TestR403_OutcomeNamesThePackageDateNotTheRunDate(t *testing.T) {
|
||||
}
|
||||
// The confirm and the outcome must agree.
|
||||
date, stale := cov.UnitRestoreDate()
|
||||
confirm := tier2UnitConfirmWithStaleness(date, true, stale)
|
||||
confirm := noteServer(t).tier2UnitConfirmWithStaleness(date, true, stale)
|
||||
if !strings.Contains(confirm, pkg) {
|
||||
t.Errorf("the confirm does not name %q either: %q", pkg, confirm)
|
||||
}
|
||||
|
||||
// NEGATIVE CONTROL: with no preserved leg, the package date IS the fresh one and both agree on it.
|
||||
fresh := backup.Tier2Coverage{CopyLastSuccess: "2026-08-31T12:23:51Z", UnitPackageDate: "2026-08-31T12:23:00Z"}
|
||||
if !strings.Contains(tier2UnitSourceMsg(fresh), fmtRFC3339Local("2026-08-31T12:23:00Z")) {
|
||||
if !strings.Contains(noteServer(t).tier2UnitSourceMsg(fresh), fmtRFC3339Local("2026-08-31T12:23:00Z")) {
|
||||
t.Error("the ordinary outcome stopped naming its own package date")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user