v0.256.0: the box sends its own sentence in the household's language (R-558 Part B)
gates / gates (push) Successful in 24s
gates / gates (push) Successful in 24s
MinAgent: 0.131.0 (unchanged). Needs hub v0.118.0+, which shipped first and tolerates a box that sends none of this - every box in the fleet is that box until this release reaches it. The hub writes a household's e-mails in their language now, but about a third of those mails carry a sentence the BOX composed, naming a drive, an app or a number. The hub cannot translate one. So the box sends it twice. - message_customer on POST /api/v1/event, omitempty. A HUNGARIAN household sends nothing extra at all, so its payload stays byte-for-byte what every box sends today and the hub's fallback path keeps being the one production exercises rather than a branch nobody takes. - 19 producers render both sentences from ONE bundle key. `message` stays Hungarian always: it is what the operator is mailed and what the hub logs. - customer.language bootstraps a new box - stored choice, then config, then Hungarian. The config value is NEVER written into settings.json: that would record a choice the household never made. The Hungarian did not move, measured twice: the wire golden from the slice-2 base commit, and the Go parity gate over all 19 new keys. Three guards had to learn the change and one caught me: the test seam now carries the new field; the R-329 severity register reported two dynamic sites as no longer existing the moment they moved off PushEvent (the walk now checks 36 severity literals, up from 20); and TestConfigLanguageIsWiredInMain reads main.go, because cmd/ is gitignored and ripgrep does not. A mistake, named: the first pass dropped displayName from three producers, which would have mailed customers "Alkalmazás telepítve: %!s(MISSING)". Caught reading the diff; now pinned by a test that refuses %!/MISSING/%s/%d in either language. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -27,6 +27,13 @@ type Settings struct {
|
||||
// surfaced to the dashboard as a persistent banner. Not persisted.
|
||||
LoadWarning string `json:"-"`
|
||||
|
||||
// configLanguage (v0.256.0, R-558) is `customer.language` from controller.yaml — the language
|
||||
// the hub says this box should START in. It is consulted ONLY when the household has never
|
||||
// chosen: GetLanguage prefers the stored choice, always. Not persisted, and deliberately not a
|
||||
// field of the JSON: it belongs to the config, which is re-pulled, and writing it into
|
||||
// settings.json would turn a default into a choice the household never made.
|
||||
configLanguage string `json:"-"`
|
||||
|
||||
// Auth
|
||||
PasswordHash string `json:"password_hash,omitempty"` // bcrypt hash, overrides controller.yaml
|
||||
|
||||
@@ -819,12 +826,40 @@ func (s *Settings) SetBackupWindowStart(start string) error {
|
||||
return s.save()
|
||||
}
|
||||
|
||||
// GetLanguage returns the household's dashboard language, normalised: anything unset or unknown is
|
||||
// Hungarian (i18n.Default).
|
||||
// SetConfigLanguage records the language controller.yaml says this box should start in.
|
||||
//
|
||||
// Called once at startup and again after each config pull. It never touches s.Language: a
|
||||
// re-delivered config must not be able to change what a household chose.
|
||||
func (s *Settings) SetConfigLanguage(lang string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.configLanguage = lang
|
||||
}
|
||||
|
||||
// GetLanguage returns the household's dashboard language, normalised.
|
||||
//
|
||||
// THE ORDER, and it is the whole of the bootstrap rule (R-558):
|
||||
//
|
||||
// 1. What the HOUSEHOLD chose, from settings.json. An explicit choice always wins, and it wins
|
||||
// forever — a later config pull cannot unseat it.
|
||||
// 2. What the hub said to start in (`customer.language`), for a box nobody has told yet. This is
|
||||
// what makes "create the customer as English" produce an English box on first boot.
|
||||
// 3. Hungarian.
|
||||
//
|
||||
// Steps 1 and 2 are DIFFERENT KINDS OF FACT and the difference is the reason this is not one field:
|
||||
// an empty s.Language means "never chosen", which is not the same as "chose Hungarian". Collapsing
|
||||
// them would make an English household's box silently revert to Hungarian the first time anyone
|
||||
// looked at a config default.
|
||||
func (s *Settings) GetLanguage() string {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return i18n.Normalize(s.Language)
|
||||
if i18n.IsSupported(s.Language) {
|
||||
return s.Language
|
||||
}
|
||||
if i18n.IsSupported(s.configLanguage) {
|
||||
return s.configLanguage
|
||||
}
|
||||
return i18n.Default
|
||||
}
|
||||
|
||||
// SetLanguage stores the household's dashboard language and saves. Only a supported language is
|
||||
|
||||
Reference in New Issue
Block a user