v0.258.0: the last four Hungarian things an English household met
gates / gates (push) Successful in 24s
gates / gates (push) Successful in 24s
R-589 the update badge, R-590 the data-folder backup sentence, R-573 the two channel banners, R-572 two dead helpers. All four were built in Go, which is why neither the template parity fixtures nor TestI18nEnglishPages could see them; slice 5's LIVE proof is what found them. R-590 is the one that matters most: it is a promise about the customer's files. It said, in Hungarian and under an already-English folder label, that a drop-zone is temporary and unbacked. The test now asserts the CONSEQUENCE in both languages — and that the two languages do not produce the same string, which would mean the English fell back. R-572 is not what its row said. The row claimed a template renders "vasárnap" on an English page; measured, NO template and no Go file called pruneLabel or nextPruneLabel. They were dead func-map entries returning Hungarian, so they are deleted rather than translated — translating dead code would add machinery with no reader and a test pinning a fiction. Deletion is fail-loud and that was proven: a template naming the removed function panics loadTemplates at startup. Five red-proofs. One of them says something about the gate rather than the code: the Go-parity gate does not measure localeFuncs keys against the base capture, so the citation to TestLocaleFuncsHungarianBundleMatchesFuncMap is what carries them — the test was extended to make that citation true. MinAgent: 0.131.0 (unchanged). Hungarian byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -170,6 +170,37 @@ func TestLocaleFuncsHungarianBundleMatchesFuncMap(t *testing.T) {
|
||||
t.Errorf("infra %s display: hu.json %q, inframeta.go %q", name, b.Msg("hu", k), m.DisplayName)
|
||||
}
|
||||
}
|
||||
// The two *MetaBadge builders (R-589, v0.258.0). i18n_go_keys.json lists their keys under
|
||||
// `_preexisting` CITING THIS TEST, so the citation has to be true: hu.json must say exactly
|
||||
// what the frozen Hungarian funcmap forms say, word for word.
|
||||
huBadge := fm["updateBadge"].(func(stacks.Stack) *MetaBadge)
|
||||
cur := stacks.Stack{Name: "a", Deployed: true,
|
||||
Meta: stacks.Metadata{Slug: "a", CatalogSince: time.Now().UTC().AddDate(0, 0, -2).Format("2006-01-02")},
|
||||
AppConfig: &stacks.AppConfig{InstalledImages: map[string]stacks.InstalledImage{"x": {Ref: "same"}}},
|
||||
CatalogImages: map[string]string{"x": "same"}}
|
||||
behind := cur
|
||||
behind.CatalogImages = map[string]string{"x": "moved"}
|
||||
if got, want := b.Msg("hu", "badge.update.current"), huBadge(cur).Label; got != want {
|
||||
t.Errorf("badge.update.current: hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
if got, want := b.Msg("hu", "badge.update.current.title"), huBadge(cur).Title; got != want {
|
||||
t.Errorf("badge.update.current.title: hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
if got, want := b.Msg("hu", "badge.update.behind")+fmt.Sprintf(b.Msg("hu", "badge.update.behind.days"), 2), huBadge(behind).Label; got != want {
|
||||
t.Errorf("badge.update.behind(+age): hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
if got, want := b.Msg("hu", "badge.update.behind.title"), huBadge(behind).Title; got != want {
|
||||
t.Errorf("badge.update.behind.title: hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
huLifecycle := fm["lifecycleBadge"].(func(stacks.Metadata) *MetaBadge)
|
||||
ab := huLifecycle(stacks.Metadata{Lifecycle: stacks.LifecycleAbandoned})
|
||||
if got, want := b.Msg("hu", "badge.lifecycle.abandoned"), ab.Label; got != want {
|
||||
t.Errorf("badge.lifecycle.abandoned: hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
if got, want := b.Msg("hu", "badge.lifecycle.abandoned.title"), ab.Title; got != want {
|
||||
t.Errorf("badge.lifecycle.abandoned.title: hu.json %q, funcmap %q", got, want)
|
||||
}
|
||||
|
||||
// And the English funcs really are English.
|
||||
en := s.localeFuncs("en")
|
||||
if got := en["timeAgo"].(func(time.Time) string)(now.Add(-time.Minute - time.Second)); got != "1 minute ago" {
|
||||
@@ -564,3 +595,139 @@ func TestParameterisedPageTitles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── R-589 / R-573 (v0.258.0) — the badges and the two channel banners follow the language ──────
|
||||
//
|
||||
// These were the last two Felhom-authored Hungarian strings an English household met on an app
|
||||
// page, found LIVE on 2026-09-20 by localisation slice 5's proof, not by reading. Both are built in
|
||||
// Go, so neither the template parity fixtures nor TestI18nEnglishPages could see them.
|
||||
|
||||
// TestUpdateBadgeFollowsTheLanguage — the Hungarian funcmap form and the bundle form must AGREE in
|
||||
// Hungarian (that is the parity guarantee) and DIFFER in English (that is the fix).
|
||||
//
|
||||
// RED-PROOF (2026-09-2X): deleting the "updateBadge" entry from localeFuncs made the English arm
|
||||
// return „Naprakész" and the test failed naming it; restoring it went green.
|
||||
func TestUpdateBadgeFollowsTheLanguage(t *testing.T) {
|
||||
s := i18nTestServer(t)
|
||||
behind := stacks.Stack{
|
||||
Name: "app", Deployed: true,
|
||||
Meta: stacks.Metadata{Slug: "app", CatalogSince: time.Now().UTC().AddDate(0, 0, -3).Format("2006-01-02")},
|
||||
AppConfig: &stacks.AppConfig{InstalledImages: map[string]stacks.InstalledImage{"a": {Ref: "old"}}},
|
||||
CatalogImages: map[string]string{"a": "new"},
|
||||
}
|
||||
current := behind
|
||||
current.CatalogImages = map[string]string{"a": "old"}
|
||||
unknown := stacks.Stack{Name: "app", Deployed: false}
|
||||
|
||||
huFn := s.templateFuncMap()["updateBadge"].(func(stacks.Stack) *MetaBadge)
|
||||
enFn := s.localeFuncs("en")["updateBadge"].(func(stacks.Stack) *MetaBadge)
|
||||
|
||||
// 1. UNKNOWN renders nothing, in both languages — a badge on an app nobody can judge is a
|
||||
// question the customer cannot answer.
|
||||
if huFn(unknown) != nil || enFn(unknown) != nil {
|
||||
t.Error("an unjudgeable app must carry no badge in either language")
|
||||
}
|
||||
|
||||
// 2. Hungarian is unchanged: the bundle form equals the frozen funcmap form.
|
||||
for _, st := range []stacks.Stack{current, behind} {
|
||||
hu := huFn(st)
|
||||
viaBundle := s.localeFuncs("hu")
|
||||
if viaBundle != nil {
|
||||
t.Fatal("localeFuncs must return nothing for Hungarian — that IS the parity guarantee")
|
||||
}
|
||||
if hu == nil {
|
||||
t.Fatal("a judgeable app must carry a badge")
|
||||
}
|
||||
}
|
||||
if got, want := huFn(current).Label, "Naprakész"; got != want {
|
||||
t.Errorf("hu current label = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
// 3. English really is English, and carries the SAME class (the decision, not the words).
|
||||
enCur, enBehind := enFn(current), enFn(behind)
|
||||
if enCur.Label != "Up to date" {
|
||||
t.Errorf("en current label = %q", enCur.Label)
|
||||
}
|
||||
if enCur.Class != huFn(current).Class || enBehind.Class != huFn(behind).Class {
|
||||
t.Error("the badge CLASS must not depend on the language — only the words may")
|
||||
}
|
||||
if !strings.HasPrefix(enBehind.Label, "Update available") || !strings.Contains(enBehind.Label, "3 days ago") {
|
||||
t.Errorf("en behind label = %q, want the English age suffix", enBehind.Label)
|
||||
}
|
||||
for _, b := range []*MetaBadge{enCur, enBehind} {
|
||||
if strings.ContainsAny(b.Label+b.Title, "áéíóöőúüűÁÉÍÓÖŐÚÜŰ") {
|
||||
t.Errorf("an English badge still carries Hungarian: %q / %q", b.Label, b.Title)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. The abandoned lifecycle badge, same shape.
|
||||
huLife := s.templateFuncMap()["lifecycleBadge"].(func(stacks.Metadata) *MetaBadge)
|
||||
enLife := s.localeFuncs("en")["lifecycleBadge"].(func(stacks.Metadata) *MetaBadge)
|
||||
ab := stacks.Metadata{Lifecycle: stacks.LifecycleAbandoned}
|
||||
if huLife(stacks.Metadata{}) != nil || enLife(stacks.Metadata{}) != nil {
|
||||
t.Error("an available app must carry no lifecycle badge")
|
||||
}
|
||||
if got := huLife(ab).Label; got != "Nem karbantartott" {
|
||||
t.Errorf("hu lifecycle label = %q", got)
|
||||
}
|
||||
if got := enLife(ab).Label; got != "No longer maintained" {
|
||||
t.Errorf("en lifecycle label = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAgentChannelBannersFollowTheLanguage — R-573. The checker classifies the fault; the banner is
|
||||
// keyed by that classification. The CONSEQUENCE asserted here is what the household reads, not
|
||||
// whether a key was set.
|
||||
//
|
||||
// RED-PROOF (2026-09-2X): dropping `MessageKey` from SetAgentChannelAlert left the English banner
|
||||
// reading the Hungarian sentence and this failed naming it.
|
||||
func TestAgentChannelBannersFollowTheLanguage(t *testing.T) {
|
||||
s := i18nTestServer(t)
|
||||
b := s.i18n
|
||||
// Every reason the checker can classify must have a key, or the banner silently falls back.
|
||||
for _, reason := range []string{"construction_error", "pin_mismatch", "unauthorized",
|
||||
"misconfigured", "unreachable", "timeout", "unknown"} {
|
||||
key := "alert.agent_channel." + reason
|
||||
if !b.Has("hu", key) {
|
||||
t.Errorf("reason %q has no Hungarian banner key %q", reason, key)
|
||||
continue
|
||||
}
|
||||
hu, en := b.Msg("hu", key), b.Msg("en", key)
|
||||
if hu == en {
|
||||
t.Errorf("%s: the English banner fell back to Hungarian: %q", key, en)
|
||||
}
|
||||
if strings.ContainsAny(en, "áéíóöőúüűÁÉÍÓÖŐÚÜŰ") {
|
||||
t.Errorf("%s: the English banner carries Hungarian letters: %q", key, en)
|
||||
}
|
||||
}
|
||||
if b.Msg("en", "alert.endpoint_drift") == b.Msg("hu", "alert.endpoint_drift") {
|
||||
t.Error("the endpoint-drift banner fell back to Hungarian")
|
||||
}
|
||||
|
||||
// The AlertManager renders the key, and an UNMAPPED reason still shows the checker's sentence
|
||||
// rather than a blank banner or a raw key — the fail-open half of the fix.
|
||||
am := NewAlertManager(nil)
|
||||
am.SetAgentChannelAlert(true, "alert.agent_channel.timeout", "A tárolókezelő ügynök nem válaszol.")
|
||||
if got := findAlert(am.GetAlerts("en"), "agent-channel-down"); got == nil {
|
||||
t.Fatal("the banner did not render")
|
||||
} else if got.Message != "The storage agent is not answering." {
|
||||
t.Errorf("en banner = %q", got.Message)
|
||||
}
|
||||
am.SetAgentChannelAlert(true, "", "A tárolókezelő ügynök nem válaszol.")
|
||||
if got := findAlert(am.GetAlerts("en"), "agent-channel-down"); got == nil || got.Message != "A tárolókezelő ügynök nem válaszol." {
|
||||
t.Errorf("an unmapped reason must fall back to the composed sentence, got %+v", got)
|
||||
}
|
||||
am.SetAgentChannelAlert(false, "", "")
|
||||
if findAlert(am.GetAlerts("en"), "agent-channel-down") != nil {
|
||||
t.Error("clearing the banner must remove it")
|
||||
}
|
||||
}
|
||||
|
||||
func findAlert(alerts []Alert, id string) *Alert {
|
||||
for i := range alerts {
|
||||
if alerts[i].ID == id {
|
||||
return &alerts[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user