A concept's rendering is declared per language, and the gate declares what it cannot check

Accepted

ontoref
Writing about software in Spanish has no settled convention, and the project had been resolving the

Context

Writing about software in Spanish has no settled convention, and the project had been resolving the question one file at a time. The first attempt, `anglicisms.ncl`, was a flat deny-list: ~50 English words, a Spanish replacement, a severity, and a gate that failed on a Hard hit.

It was the right instinct with the wrong shape, and it was wrong in a way that MEASURES.

WHAT WAS MEASURED (2026-07-12, across the glossary and the whole positioning layer, code spans stripped, word boundaries honoured):

2 concepts DIVERGED BETWEEN THE TWO SURFACES `reflection` glossary kept the English (4×) · positioning wrote "reflexión" (3×) `gate` glossary kept the English (3×) · positioning wrote "compuertas"

5 concepts were INCOHERENT INSIDE A SINGLE FILE the glossary — the document whose ONLY job is to fix the vocabulary — wrote both `schema` and «esquema», both `drift` and «deriva»; the positioning layer wrote both `witness` and «testigo», both `runtime` and «tiempo de ejecución».

The headline is not that two documents disagreed. It is that EACH DOCUMENT DISAGREED WITH ITSELF. The arbiter did not agree with itself — not through carelessness, but because the decision had NOWHERE TO LIVE. A rendering was a fact about a string in a file, not a property of the concept.

Six vocabularies were live: the glossary, the positioning layer, two sets of `.ftl` (one of them DEAD — `content/i18n/build-tools/`, which nothing reads), a hand-rolled Nushell VOCAB table (`expediente-vocab.nu`), and seven generators with Spanish strings baked in. None owned, none witnessing the others. This is the ADR-070 pathology — declared twice, no witness — one layer down, and it is the third clone of the same pathogen after `deriva-hardcoded` and `espejo-que-revertía`.

AND THE COLLISION NOBODY COULD SEE. The glossary renders `door` → "puerta" (the funnel entry, ADR-064). Its OWN Spanish definition of `testigo-verificado` was rendering `gate` → "puerta" as well. Two distinct concepts had already collapsed into one Spanish word, inside the document written to prevent exactly that. Each decision is defensible ALONE, which is why reading never catches it.

THE FIRST ATTEMPT AT A FIX REPRODUCED THE DISEASE, LIVE. The surface-typed lexicon that preceded this ADR (a four-class deny-list with a per-surface axis) was a SEVENTH vocabulary, built parallel to the six without seeing them. And it misfiled `reflection` as untranslatable — because it collided with the `reflection/` directory — while correctly rendering `schema` as «esquema» in the same file, on the same day, under a rule its own author had just written. Nothing could contradict it, so it convinced itself. The agent had the full ontology loaded. That is what a vocabulary without a witness produces, and it is the most useful evidence this ADR has.

Decision

A concept's rendering is DECLARED PER LANGUAGE, in one registry, under contracts that run across every surface that speaks. And the gate DECLARES WHAT IT CANNOT CHECK.

STRUCTURE

reflection/schemas/rendering.ncl how a concept renders in a language + the seven cross-contracts reflection/schemas/term.ncl gains an OPTIONAL `policy` — a concept carries its own decision reflection/schemas/lexicon.ncl LANGUAGES, vocabulary that is not a concept, coverage ontology/glossary.ncl concepts WITH a definition (protocol surface; ADR-063 gates it) ontology/lexicon.ncl vocabulary WITHOUT one (`drift`, `flag` — nothing to define) ontology/registry.ncl THE UNION — the only place the contracts run, and the only thing a consumer may read

The two registries are not a compromise: forcing a definition onto `flag` would be ceremony, and deleting `glossary.ncl` would break the protocol seed and ADR-063's Hard constraints. What makes the split SAFE is that the union is contracted as ONE namespace. A consumer reading half a registry believes half a policy.

TWO ROADS TO KEEPING A SOURCE FORM. CONFLATING THEM IS THE BUG.

'Identifier it appears verbatim in a command, an API, a `kind:`, an error message. Translating breaks the REFERENCE — the reader cannot get from the text to the tool. Mechanical, true in EVERY language. (`commit`, `ADR`, `NCL`)

'NoFaithfulRendering no word in THIS language carries the concept without displacing it. Translating breaks the CONCEPT. A fact about ONE language, possibly false in the next. (`gate`)

Same verdict, incompatible reasons, and the contract enforces the difference: 'Keep justified by 'FaithfulRendering is REFUSED — if a faithful word exists, use it. That single rule is what corrects `reflection` → «reflexión», and it is the rule the previous design could not express.

For `gate`, the note must say what each candidate LOSES, because each looks reasonable alone: «compuerta» sends it to ELECTRONICS · «puerta» is already `door`'s · «umbral» loses the typed policy · «punto de control» loses that it BLOCKS · «criterio de paso» is honest and is a periphrasis.

A REJECTED RENDERING IS NOT ONLY A CALQUE.

'Calque a source verb forced into the target's conjugation ("mergear"). It grates, so it is found. The defect is the MORPHOLOGY, not the loanword: a borrowed noun is inert, a borrowed verb must pick a conjugation and drags its register in. Hence the repair — LIGHT VERB + KEPT NOUN, "hacer un commit" — carried on the denial itself, because a noun cannot repair a verb. 'FalseFriend library → «librería» (a SHOP). Reads perfectly. Is wrong. Has largely won. THESE ARE THE DANGEROUS ONES, because nobody hunts for a mistake that sounds right. 'Displaces a true cognate borrowed from ANOTHER DISCIPLINE («compuerta», from electronics). 'Regional correct elsewhere, rejected here for REGISTER («corrida»). A choice, and it says so.

Each denial carries its reason IN THE DATA, or someone reverts it in good faith in six months.

RENDERINGS ARE INJECTIVE, PER LANGUAGE. Two concepts may not render to the same word. This is the contract a human found and no validator would have, and it fires on the real data today.

THE LANGUAGE AXIS IS A GUARANTEE. `languages` is declared ONCE — a fact about the project, not the website; rustelo's `site.ncl` becomes a consumer, not a second truth. Adding one FAILS THE BUILD until every concept answers for it and every surface declares its content. A half-added language is not a pending translation; it is a site quietly serving the wrong one — which four `domains/es/` door pages did for months, carrying Spanish frontmatter over English bodies, because a surface had no language and nothing could ask.

THE GATE DECLARES WHAT IT CANNOT CHECK — and this is half the decision, not a footnote.

On its first run the gate reported 52 Hard findings. FORTY-THREE WERE FALSE, every one of them the checker asserting what a regex cannot establish:

· "puerta" is denied for `gate` and is the RIGHT rendering of `door`. Injectivity guarantees a form is the PREFERRED rendering of at most ONE concept; it says nothing about a form preferred by one and denied by another — and that case is legal and common. It is what an AVOIDED collision looks like from the other side. → `gateable = false` · "build system", "un fork", "el deploy" are BORROWED COMPOUNDS, entering the language as units. Demanding «compilar» inside «cada build system usa DAGs» is nonsense. → `gate_source = false` · `tags = ["witness", "adr-070"]` is a routing key, not prose. Renaming it breaks the filter. · a verbatim quotation in the source language is a CITATION. You do not translate a quote, and no checker can tell one from the author's prose — on the page they are identical. → `<!-- lang:en -->`

The rule is still EMITTED — the glossary and the public note print it, because a human writing a sentence HAS the context a regex lacks — but no surface fails on it. This is not weakening the policy. It is the policy refusing to claim knowledge it does not have. A gate that cries wolf is a gate people learn to skip, and a skipped gate is the failure this entire ADR is about.

THE POLICY EXPLAINS ITSELF WHERE IT IS READ. The Spanish glossary and a public "Nota sobre el lenguaje" are GENERATED from the same NCL the gate reads; `--check` fails on drift. A rule enforced by a machine and justified nowhere is a rule people comply with and resent. This is not a legal disclaimer: the right analogy is the licence or the code of conduct — normative, published, versioned — and unlike either, executed by CI.

Constraints

  • Hard The rendering policy MUST live in `ontology/registry.ncl` (the union of glossary + lexicon) under the contracts in `reflection/schemas/rendering.ncl`. `anglicisms.ncl` MUST NOT exist. Consumers MUST read the union, never a registry half.
  • Hard A 'Keep MUST declare WHICH road: 'Identifier (translating breaks the reference) or 'NoFaithfulRendering (translating breaks the concept), and MUST carry the evidence for it. 'Keep justified by 'FaithfulRendering is forbidden — if a faithful word exists, use it.
  • Hard Within a language, two concepts MUST NOT render to the same form. `gate` → «puerta» and `door` → «puerta» is refused by contract.
  • Hard `languages` is declared ONCE. Adding one MUST fail the build until every governed concept declares its rendering and every surface declares its content in that language.
  • Hard A rule the checker cannot falsify MUST be marked uncheckable IN THE DATA (`gateable`, `gate_source`) or handled by a declared marker (`<!-- lang:xx -->`, the key whitelists) — never by weakening the policy, and never by editing correct prose to appease the tool.
  • Hard Coverage MUST declare every (surface × target language). A surface with no content is `sources = []` — inactive by absence of input. Omission is forbidden. A glob MUST NOT be both source and projection, and a BILINGUAL file MUST declare `mode = 'I18nKeys`.
  • Hard The gate and both oracles MUST be reachable from a CI recipe (`just ci-lexicon`). A gate runnable only by hand is drift by construction.
  • Hard The reader-facing surfaces (`glosario-es.md`, the public language note) MUST be generated from the registry the gate reads, and their `--check` MUST fail on drift.
  • Soft A `web` document leaning on KEPT terms SHOULD hand the reader the glossary. The check REPORTS the count and MUST NOT fail while the site serves no glossary route; when `gloss_markers` names a real affordance it becomes enforceable with no code change.

Alternatives considered

  • Keep the four-class surface-typed lexicon (the design this supersedes) and just fix `reflection` by hand.rejected: It would fix the symptom and leave the cause. A single 'rigid' class CANNOT express why `commit` and `gate` are both kept, so the next term would be misfiled by the same reasoning — and the misfiling is invisible, because the class name reads as a decision. The bug was in the schema's expressive power, not in the entry.
  • One merged registry: fold the glossary into the lexicon, delete `glossary.ncl`.rejected: The literal merge the design called for, and the evidence refused it: `glossary.ncl` inherits the protocol-shipped seed and ADR-063 carries accepted Hard constraints with `Grep` checks against it. Deleting it breaks an accepted ADR and forces a migration across every consumer. Merging the POLICY (onto the term) achieves the goal without any of that; merging the FILES achieves nothing extra.
  • Define the vocabulary in Fluent `.ftl` terms, since rustelo already speaks FTL.rejected: FTL is a runtime MESSAGE catalogue and is excellent at that. It has no types, no contracts, and no way to declare that a term is untranslatable or WHY — so pushing the vocabulary into it gains reach and loses governance. The right direction is the inverse: the NCL registry is the source, and `.ftl` terms become one of its PROJECTIONS. Then FTL reaches everywhere without changing FTL, and Weblate becomes a possible consumer instead of the owner of the policy.
  • Make every finding Hard: no `gateable`, no `gate_source`, no citation marker.rejected: This is the run that produced 52 findings of which 43 were false. It fails a slide for «cada build system usa DAGs», fails a page for the word «puerta» in a text about the four funnel doors, and demands a translated tag that would break the routing. Nobody would keep such a gate for a week, and an ignored gate is a green tick over unread files — the failure this ADR exists to end, reintroduced by the opposite route.
  • A style guide in prose, no gate.rejected: This is precisely what the project had, and the record shows what it produced: the arbiter contradicting itself in seven places, four Spanish pages serving English for months, and a false friend in a published post. Advisory prose does not bind a human under deadline and binds a probabilistic agent not at all — `testigo-verificado` names the reason: what is not verified is not held. The agent that wrote the FIRST version of this policy misfiled a term the same day, under its own rule.

Anti-patterns

  • One Lexical Class for Two Incompatible Reasons — A vocabulary marks a term 'do not translate' without recording WHY. Two incompatible facts hide under one label: 'translating breaks the reference' (it is an identifier — mechanical, universal) and 'translating breaks the concept' (this language has no word for it — a claim about ONE language). Because the label reads as a decision, the misfiling is invisible: a term with a perfectly faithful cognate gets filed beside one with no rendering at all, and nobody re-examines it. The tell is a term kept because it COLLIDES WITH A DIRECTORY NAME — an identifier collision, which blanking code spans already handles, dressed up as a lexical fact.
  • Trusting a Cognate Because It Looks Like Its Source — A translation policy hunts for the ugly borrowings ('mergear', 'deployar') and accepts anything that looks native. But languages evolve their cognates APART, and the resulting displacement is invisible precisely because the word still resembles its origin: «librería» for library is a SHOP; «compuerta» for gate is a real cognate borrowed from ELECTRONICS and drags a logic-circuit reading into a sentence about state transitions. The dangerous rendering is the one that does NOT grate — it reads fluently, it often WINS in usage, and nobody hunts for a mistake that sounds right.
  • Two Concepts, One Word, Each Decision Defensible Alone — Concept A renders to a word; concept B renders to the same word; each choice is correct in isolation and the two live in different files. The translation MERGES two concepts the source kept apart — inventing an ambiguity the original never had. This is the classic failure of technical translation and it is nearly invisible to reading, because there is no single line where the mistake appears. Ours had already happened: the glossary rendered `door` → «puerta» while its own Spanish definition of the witness rendered `gate` → «puerta» too.
  • A Gate That Asserts What a Regex Cannot Establish — A checker reports findings that are TRUE ABOUT THE STRING and FALSE ABOUT THE LANGUAGE: a word that is another concept's legitimate rendering, a loanword inside a borrowed compound ('build system'), a routing key in a tag list, a verbatim citation in the source language. Each finding is unfixable as reported — the prose is correct — so the author learns the tool is unreliable and starts skipping it. This is WORSE than a false positive that is obviously wrong, because it takes real thought to dismiss. 43 of our first 52 findings were this. The wrong repairs are equally available and both destroy the policy: weaken the rule until the tool goes quiet, or edit correct prose to appease it.
  • A Checker Whose Coverage Is a Glob Nobody Audits — A gate hardcodes the files it scans, the content tree grows past that glob, and it goes on printing a green tick over families it never opens. The green is not a lie about the files it read — it is a lie about the files it did not, and silence about coverage reads to every human as full coverage. The extreme case: a gate that CANNOT read the file format where its own violation was measured (a bilingual .ncl) and therefore reports clean over the very surface that is broken.

Related ADRs

ADR-070 · ADR-066 · ADR-064 · ADR-063 · ADR-035

Was this useful? Rate it
Got something to add? Tell me what you think, what you'd suggest, or whether we should keep exploring this topic.
· reads

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.