Orchestrata Ultimate › Gates › lexicon-collision
Gate: lexicon-collision
At a glance
| Purpose | Refuse a categorical intent-lexicon word (work-type or generic-verb registry) that also names a package or module in the tree. |
| Where it runs | The scripts/gates.py battery (registered as lexicon-collision). |
| Trigger | A battery run whose src/ or scripts/ fingerprint changed. |
| Exact command | python scripts/check_lexicon_collisions.py (--list, --json for inspection) |
| Blocks | Exit 1 on a collision, 2 when the check could not run, 0 clean. |
| Ruling / ADR | RUL-014, per the module docstring ("the mechanism required by RUL-014"). |
| Last recorded verdict | ok: true in the primary checkout's .cache/gates.json (file last written 2026-09-22 10:01 local). wt-MAP-PRECOMMIT has no .cache/gates.json. |
What it refuses
A categorical word in the intent lexicon (work-type or generic-verb registries) that also names a package or module in the tree — a collision that makes the intent classifier read a subsystem name as a work-type signal.
Why it exists
Module docstring — "THE DEFECT THIS EXISTS FOR, measured and shipped before it was caught":
"Five words were added to the intent lexicon as work-type signals:
memory,trace,integration,versionanddispatch. Each of them also names a subsystem in this repo. The result was that 'wire the memory engine into the gateway' classified as PERFORMANCE work, becausememorywas read as a performance signal rather than as the name of the subsystem being wired. All five had to be removed again, and the only thing that caught them was a human reading the held-out corpus results.dispatchrepeated the lesson late — mined with real evidence, accepted by review, applied, and then rejected becausedispatcherstems to it and 'refactor the dispatcher into smaller functions' started resolving to API work.A search of the published literature found no prior art for statically checking a controlled vocabulary against a live codebase symbol table. This gate is the mechanism required by RUL-014 (a ruling resolves to a mechanism, never prose)."
What counts as a subsystem name — measured, not assumed
The docstring records the calibration experiment against the five known-bad words:
| Strategy | Names | Collisions | Known-bad caught |
|---|---|---|---|
| directories, depth 1 | 30 | 1 | memory, integration |
| directories, depth 2 | 93 | 6 | + trace, dispatch |
| dirs + modules, depth 2 | 394 | 9 | + version (ALL FIVE) |
| dirs + modules, depth 3 | 577 | 16 | no additional catches |
"Depth 2 including module files is the shipped operating point: it is the only one that catches all five… Depth 3 is rejected: it doubles the exemption burden and catches nothing further. The cost is the nine documented exemptions, 9 of 144 categorical words."
Why not the full symbol table: "Almost any English word matches some function or
class name in a 122K-line tree — run, add, build and check all do — so
gating on every symbol would flag most of the lexicon and be switched off within a
day."
What is deliberately NOT a collision: "The target registry is deliberately exempt: a
target word naming a subsystem is not a bug, it is the entire point (session,
gateway and kernel are all legitimate targets AND packages)."
How it runs
- Commands:
uv run python scripts/check_lexicon_collisions.py # gate mode uv run python scripts/check_lexicon_collisions.py --list # show the sets uv run python scripts/check_lexicon_collisions.py --json # machine output - When it fires: the
scripts/gates.pybattery (registered aslexicon-collision). - Inputs: fingerprint roots
("src", "scripts")— "BOTH sides are inputs:srccovers the vocabulary and the tree layout,scriptscovers the gate's own exemption list. Renaming a module is enough to create a collision that no vocabulary edit would show" (registry comment). - Caching: the verdict is cached in
.cache/gates.jsonagainst_fingerprint(("src", "scripts")): a sha256 over the relative path and bytes of every file under those roots whose suffix is.py,.toml,.pl,.json,.mdor.sh(_interesting()inscripts/gates.py, read through the AST toolkit on 2026-10-23). There is no per-gate suffix mask: this page previously quoted one, andgit log --all -S GATE_MASKS -- scripts/gates.pyfinds no commit that ever contained aGATE_MASKStable.
Typical failures and how to fix them
- Exit codes: "0 clean, 1 collision found, 2 could not run the check."
- What a failure looks like (representative): the colliding word named with the package/module it collides against, per registry (work-type / generic-verb).
- State over time: on 2026-08-29 the cached battery verdict was
ok: false(a live collision); the primary checkout's.cache/gates.json, last written 2026-09-22 10:01, recordsok: true. - A pass that checks the wrong vocabulary. If the generated
intent_lexicon.gen.plhas drifted fromIntentVocabulary, this gate reads the Python side and can pass while the parser's lexicon holds a colliding word (thelexicon-driftregistry comment). Run both gates together. - Un-blocking: remove/rename the lexicon word, or add a documented exemption in the script — the exemption list is inside the gate on purpose so an edit to it invalidates the cache.
Wiring
- Invokers:
scripts/gates.py(battery). - Tests:
tests/scripts/test_check_lexicon_collisions.py.
Related
- Gates master index
- Development workflow
- RUL-014 — A ruling that exists only in prose is incomplete work
- lexicon-drift — the paired gate; drift makes THIS gate check "a vocabulary the parser does not use"