Development record
abcd adds a missing site label to an existing ui.json and never rewrites one
Typed links: refines adr-47
(decision 2's closed allowlist is untouched: this adds a declared label to the
file, and adds no fallback at render time).
Context
site-src/ui.json is the closed allowlist of interface strings adr-47
decision 2 permits the site generator to add. LoadUI decodes it with unknown
keys refused and refuses a declared label the file leaves empty or absent,
naming it (no text for status.target), so a blank button never reads as a
rendering fault. abcd site setup seeds the file once and never rewrites it,
under the rule its chapter states: a file the repository owns once it exists
is kept.
Two intents of this release cycle each declared new required labels:
itd-2609212103568351 the six status.* labels of the Now / Next / Later
block, and itd-2609212103572513 status.target. Both carry
impact: additive, but a managed repository whose ui.json predates them
would see a green site build refuse on upgrade until it added the lines by
hand, which is breaking for that surface. The review of the second intent's
lane raised it as a ruling owed before the v0.12.0 cut.
Decision
The product thinker ruled on 2026-09-30 (ruling TG1), verbatim as relayed: "(b) ABCD ADDS THE MISSING LABELS: on the next site setup or site build, abcd adds only the missing required labels (with the default words); the project's own wording elsewhere in ui.json is never changed. No failure, no manual step; both intents stay impact: additive."
We therefore make one exception to "a file the repository owns once it exists
is kept": abcd site setup and abcd site build add to an existing
ui.json each label the allowlist declares and the file does not carry, with
the words abcd's own bundled ui.json gives it, and name each added label on
stderr, one line per label. Nothing else in the file changes:
- A label the file carries keeps its wording byte for byte. A label declared
blank is the project's declaration, so it is not rewritten and
LoadUIstill refuses it by name. - The added members go at the end of their block, in the block's own indentation and line style; a whole declared block the file lacks is added with every label in it. Every byte already in the file stays where it was.
- A file that does not decode against the allowlist, an unknown key included,
is not touched: the closed allowlist refuses it exactly as before, and
adding applies to declared keys only. The
forge_namesmap is not required, so nothing is added to it, and_purposeis never rendered, so it is never added. - The file is read as the site's other reads are, so a symlinked or
non-regular
ui.jsonis refused, and it is written atomically through the canonical writer, keeping its mode.
Alternatives Considered
- (a) A breaking impact. Keep the refusal and declare both intents
breaking, so the cut derives a major-shaped version and every adopter adds the lines by hand. Rejected by the ruling: a manual step for words abcd already knows. - (b) abcd adds the missing labels. Chosen: no failure and no manual step, both intents stay additive, and the project's wording is never touched.
- A fallback at render time. Render a missing label from the bundled words without writing the file. Rejected: adr-47 decision 2 keeps the allowlist closed and the file the one place the site's added words live; a silent fallback would render words the repository's file does not hold.
Consequences
- An older
ui.jsonkeeps building across a release that declares a new label, and the change it takes is visible: the verb says which labels it added, and the file is left modified in the working tree for the person to commit (site setupnames it in its commit step). site buildwrites one file outside its output directory, the repository'sui.json, and only when a declared label is absent.- A future label is additive for adopters by construction, provided the
bundled
ui.jsondeclares its words; a test holds the bundled file to declaring every label.
Frontmatter
| id | adr-2609301720596683 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-09-30 |
.abcd/development/decisions/adrs/2609301720596683-abcd-adds-a-missing-site-label-to-an.md
open on GitHub ↗ · commit history ↗
References3
- adr-47abcdev.app is rendered from this repository alonerelated
- itd-2609212103568351The status board shows Now, Next and Later, computed from the recordrelated
- itd-2609212103572513An intent names the release it must land by, and the cut says whether it didrelated
mentions2
- dcn-26093000000000212026-09-30 — An older site interface-string file keeps building: `abcd site setup` and `abcd site build` add to `site-src/ui.json` each label the allowlist declares and the file does not carry, with abcd's default words, name each on stderr, and change nothing else in it (the product thinker's ruling TG1 of 2026-09-30, relayed verbatim: "(b) ABCD ADDS THE MISSING LABELS: on the next site setup or site build, abcd adds only the missing required labels (with the default words); the project's own wording elsewhere in ui.json is never changed. No failure, no manual step; both intents stay impact: additive."). It is the one exception to "a file the repository owns once it exists is kept", recorded as adr-2609301720596683, which refines adr-47 and leaves decision 2's closed allowlist untouched: a blank declared label and an unknown key are still refused, and the site gate's own render never completes the file. itd-2609212103568351 and itd-2609212103572513 keep `impact: additive` (lane tgLabels of autonomous run A).
- iss-2610161831078335abcd land refuses when the checkout's local origin/HEAD is stale or missing and tells the person to run git fetch and git remote set-head themselves (land.go, the forge-default check), though abcd can settle the gap by asking origin which branch is its default. The product thinker has ruled twice against a refusal that hands a person a manual step abcd can resolve itself (adr-2609301720596683, adr-2610030720195401). The refusal for the branch being the default also appends a set-head hint that has nothing to do with that case. Found by a peer session and confirmed on main, 2026-10-16.