A glossed word declares which door is open by default, and a marker on the occurrence inverts it — automatic sense-matching is refused because it cannot be verified
Accepted
Context
MEASURED 2026-08-12, on the running site and on the glossary itself.
· THE TOOLTIP MATCHES TEXT AND NOTHING ELSE. `site/public/js/glossary-tooltip.js:172` walks the DOM with a TreeWalker over text nodes and applies, per term, a word-boundary regex (`(^|[^\w-])(term)([^\w-]|$)`). There is no sense disambiguation of any kind. A `seen` map annotates only the FIRST occurrence per page — which is worse than annotating none, because it CHOOSES silently, and it may choose the wrong sense.
· THE COLLISION IS NOT HYPOTHETICAL AND IT IS NOT RARE. Of the governed concepts, `gate`, `door`, `bond`, `sensor`, `guide` and `warrant` are ordinary words with a common non-ontoref sense in both languages. "El nodo del cluster" and "el nodo del grafo" are the same string and different concepts.
· THE COST IS ALREADY BEING PAID, BY THE PROSE. In a published post the author avoided a false tooltip by writing «servidor» where the sentence wanted «nodo». They are not the same thing; the text is worse; and the workaround holds only while somebody remembers. That is the mechanism taxing the product — the exact inversion this project refuses elsewhere.
· THE PROJECT ALREADY PATCHED THIS ONCE, BY NAMING. Two term ids are qualified with the project's own name — `ontoref-node`, `ontoref-plane` — which is somebody meeting this collision and solving it in the identifier. It does not reach the tooltip: the tooltip matches the RENDERED WORD, not the id.
· AND THE WORST CASE IS SCHEDULED, NOT PAST. The cellular vocabulary — `cell`, `tissue`, `nucleus`, `membrane`, `differentiation`, `lineage`, `apoptosis` — carries 10 terms and ZERO `policy` blocks, so it is invisible to the tooltip TODAY. Writing those policies (already on the work list) would activate this defect at its maximum: every text touching biology, Kubernetes, or a cell of anything would fill with ontoref tooltips. The decision therefore comes BEFORE that data, not after it.
Decision
A TERM DECLARES WHICH DOOR IS OPEN BY DEFAULT; A MARKER ON THE OCCURRENCE INVERTS IT.
reflection/schemas/term.ncl `auto_gloss | Bool | default = true` ontology/glossary.ncl `auto_gloss = false` on the ambiguous terms (projection, deferred) the occurrence marker and the tooltip that honours it
auto_gloss = true — the OPEN door. Unmarked occurrences are glossed; a marker suppresses. For words whose collision risk is low and where the affordance is worth more than the risk: `witness`, `drift`, `ondaod`, `on+re`.
auto_gloss = false — the CLOSED door. Unmarked occurrences are NOT glossed; a marker enables. For words that are ambiguous in every text: `gate`, `door`, `bond`, `sensor`, `guide`, and the whole cellular vocabulary when it lands.
THIS IS `gate_source` ONE LAYER OVER, AND DELIBERATELY SO. That field already says, in schemas/rendering.ncl: "a regex cannot tell a compound from a bare loan, so the policy says so instead of guessing." Same shape, same reason, different mechanism: a per-term switch declaring whether the automatic match may fire. No new vocabulary is invented; an existing one is extended to the surface that needed it.
WHY BOTH LEVELS AND NOT ONE. A pure per-term switch resolves the false positive by destroying the affordance — the tooltip exists precisely FOR words a reader can recognise and misread, so switching those off removes it exactly where it earns its keep. A pure per-occurrence marker puts the cost on every legitimate use of every term, forever. Keeping both puts the cost only where the collision is, and lets the default be chosen per word rather than per project.
THE ASYMMETRY THAT MAKES THIS VERIFIABLE, and it is the reason the design is shaped this way rather than the reverse. In the OPEN regime nothing is checkable: no mechanism can detect that an occurrence carried the wrong sense without guessing the sense. In the CLOSED regime everything is determined — the marker is present or it is not — so a checker can speak. Which means: AMBIGUITY IS PAID BY MOVING THE TERM INTO THE REGIME WHERE THE MACHINE CAN SPEAK, instead of leaving a mechanism to guess. That is adr-071's rule ("the gate declares what it cannot check") applied to a projection rather than to a gate.
WHAT LANDS NOW AND WHAT DOES NOT. The DECLARATION lands: the schema field and the per-term values, which is the half that decides and the half the cellular policies are blocked on. The PROJECTION does not: the occurrence marker's syntax and the tooltip that honours it are deferred, and until they exist `auto_gloss = false` is a declared intent the JS does not read. That is stated as a negative consequence rather than hidden, and it is the reason the projection-side constraint below is 'Soft and reports a count.
Constraints
- Hard Whether a term may be glossed automatically is declared on the term, in the schema, never inferred by the projection.
- Hard Terms whose word has a common non-ontoref sense declare the closed door.
- Hard `auto_gloss` defaults to true, so adopting this decision costs an existing consumer nothing until they choose to close a term.
- Soft Until the occurrence marker and the tooltip that honours it exist, the gap between declared intent and served behaviour is stated, not assumed closed.
Alternatives considered
- Disambiguate by context at render time — rejected: It is word-sense disambiguation, and doing it needs a model whose answer is non-deterministic and unauditable. adr-071 forbids the shape directly: a mechanism must declare what it cannot establish rather than assert it. Here the assertion would be made to the reader, in the product, with no signal that a guess occurred.
- Per-occurrence marker only, with no per-term default — rejected: It taxes every legitimate use of every term forever, including the terms that never collide. That is the unbounded authoring friction which makes mechanisms get skipped — and the skipping would be invisible, because a missing tooltip looks like a decision.
- Per-term switch only, with no occurrence marker — rejected: It resolves the false positive by destroying the affordance on exactly the words that need it. `gate` and `cell` are precisely the terms a reader can recognise and misread; switching them off globally means the glossary is silent where it is most useful.
- Qualify the ambiguous words in the id, as `ontoref-node` and `ontoref-plane` already do — rejected: It was tried and it does not reach the surface: the tooltip matches the RENDERED WORD, not the id. It also cannot work for the rendered form — nobody writes «el ontoref-nodo del grafo» in a sentence, and demanding it is the prose paying for the mechanism again.
- Keep the first-occurrence heuristic and rewrite prose when it misfires — rejected: This is the measured status quo. It already cost a published post its accurate word, it holds only while remembered, and it leaves no record. And it is not neutral: the heuristic picks a sense by document order, which correlates with nothing.
Anti-patterns
- Rewriting the text to avoid a tool's false positive — An author changes an accurate word for a less accurate one because a mechanism would misfire on the accurate one — «servidor» where the sentence wanted «nodo». The product degrades, the cost is invisible, the fix survives only while remembered, and nothing records that a mechanism caused it. The tell is a word choice that the author can explain by naming a tool.
- A projection that chooses a sense by document order — A mechanism matches a term by text and annotates the first occurrence, so it does pick a sense — by position, which correlates with nothing. It reads as 'no policy' while being a policy, and it cannot be argued with because nobody wrote it down. The tell is a `seen`/`first` guard in a matcher with no declaration behind it.