v0.257.0: the app catalog can speak English (R-560, slice 5 Part A)
gates / gates (push) Successful in 25s

The READ PATH for a second language in `.felhom.yml`. An `i18n: {en: …}` sibling
block inside the same file; `Metadata.For(lang)` merges it FIELD BY FIELD over the
Hungarian, so a missing or blank English field shows the Hungarian one and a
half-translated app is a legal, shippable state.

`For("hu")` is the parsed struct with `I18n` cleared and nothing else — measured
against all 53 real catalog files, copied into `internal/stacks/testdata/catalog/`.
Lists replace whole; every other list is matched by its own key, never by position.
`For` never writes through the receiver: the metadata is the stack manager's, shared
by concurrent requests, and an in-place merge would leak one household's language
into another household's page.

Pages reach catalog copy only through `LocalizeStacks`/`LocalizeStackPtr`/`MetaFor`,
and `TestNoDirectMetaCopyReadOnPages` keeps a named, reasoned allow-list of every
direct `.Meta.<copy>` read in `internal/web` so the NEXT page to read one fails the
suite instead of quietly rendering Hungarian to an English household.

Eight red-proofs. Two of them convicted a hollow TEST rather than the code: a struct
copy shares its slices' backing arrays, so the obvious DeepEqual mutation check
passed a deliberately broken merge; and a one-entry fixture cannot tell key matching
from position matching. Both rewritten, both then seen to fail.

MinAgent: 0.131.0 (unchanged). Older controllers are unaffected — `LoadMetadata`
uses non-strict `yaml.Unmarshal`, so a pre-0.257.0 box drops the whole block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-20 14:31:52 +02:00
parent e81ad613ae
commit 60f0a86bd4
60 changed files with 5318 additions and 6 deletions
+49
View File
@@ -1,3 +1,52 @@
## v0.257.0 — the app catalog can speak English (2026-09-20, R-560 slice 5 Part A)
**MinAgent: 0.131.0** (unchanged).
The last Hungarian an English household meets on the dashboard is the text that comes from the app
catalog: each app's one-line description, its tagline, „mire jó" list, „első lépések", the labels and
help lines under every install setting, the prerequisites. 1 032 strings across 53 apps. This release
is the READ PATH for their English twins; the twins themselves arrive in catalog pushes.
**The format** (`10-localisation.md` §7, now measured rather than proposed). A sibling block inside
the SAME `.felhom.yml`, so a reviewer sees one app whole:
```yaml
description: "Titkosított jegyzet és szöveg megosztás"
i18n:
en:
description: "Encrypted note and text sharing"
```
- **`Metadata.For(lang)`** (`internal/stacks/metadata_i18n.go`) merges FIELD BY FIELD. An English
field that is absent — or blank — shows the Hungarian one, so a half-translated app is a legal,
shippable state. That is what makes the catalog batches safe to push one at a time.
- **`For("hu")` is the parsed struct with `I18n` cleared**, measured against all 53 real catalog
files in `internal/stacks/testdata/catalog/` (`TestMetaForHuIsIdentity`). The Hungarian product
cannot be changed by the presence of a translation.
- **Lists are replaced whole; everything else is matched by ITS OWN KEY** — `deploy_fields` by
`env_var`, options by `value`, `optional_config` groups by `match_group`, integrations by
`target`, data paths by `path`. Position matching would mistranslate silently the first time a
Hungarian field is inserted above another.
- **`For` never writes through.** Metadata lives in the stack manager's cache and is shared by
concurrent requests; an in-place merge would leak one household's language into another's page.
- **Pages reach copy only through `LocalizeStacks` / `LocalizeStackPtr` / `MetaFor`**, and
`TestNoDirectMetaCopyReadOnPages` keeps a named, reasoned allow-list of every direct `.Meta.<copy>`
read in `internal/web`, so a NEW page that reads catalog copy off the manager fails the suite
instead of quietly rendering Hungarian.
- Two copy producers sit in packages that have no request and therefore no language — the
integration rows and the initial-credentials note. Both are re-taken from the localised metadata
in the handler; identical bytes for Hungarian.
- **An older controller is unaffected**: `LoadMetadata` uses non-strict `yaml.Unmarshal` (this repo
constructs no `yaml.Decoder` at all, verified), so a pre-0.257.0 box drops the whole `i18n:` block.
That is what lets a catalog push reach the entire fleet ahead of the floor raise.
**Eight red-proofs**, each seen to fail and then revert: a mutated hu branch; deploy fields matched
by position; a blank English string winning over Hungarian; the defensive copy removed; optional
config, integrations and data paths matched by position; the untranslated half reaching the page.
**Two of them found a hollow test rather than a bug** — a struct copy shares its slices' backing
arrays, so the obvious `DeepEqual` mutation check passed a deliberately broken merge, and a one-entry
fixture cannot tell key matching from position matching. Both tests were rewritten.
## v0.256.1 — the push log says whether a household sentence was attached (2026-09-18, R-558)
**MinAgent: 0.131.0** (unchanged). One log line, no behaviour change.