The vocabulary file
“Cookie” is an HTTP state token in a web docset, a monster in one children’s canon, and a snack in a recipe corpus. An index does not settle which one a family means; a facts file mentions all three without noticing. The vocabulary file is the layer that pins the sense, lists the surface forms people actually type, and says what the term is not. It is neither index nor facts. It is what makes both findable and unambiguous.
What a vocabulary file is
llms-vocabulary.txt is one line per term of a family, each line carrying: the canonical
name, a definition taken from a kept unit, the neighbours it is easy to confuse it with
(not:) and how it differs, the words people say instead (aka:), and the URL of the unit
the definition came from. The file is shaped like a spec-v2 llms file — H1, blockquote,
generator banner, H2 sections — so any reader that opens an index can open this.
Three properties make it different from a glossary someone typed:
- Every definition is extractive. It comes from a
definitionunit or an “X is/are …” sentence in the pool, and the line ends with that unit’s anchor. A term the pool names but never defines goes to## Named, not yet definedwith its hit count, and never receives a model-invented definition. - Every alias appeared.
aka:entries are surface forms found in the pool — backticked tokens clustered by normalised spelling, with the most frequent surface as the canonical name and the rest as aliases. No synonym list is imported from outside. - Contrast is first-class.
not:comes from contrast cues in the units themselves — not, unlike, vs, rather than, instead of, not to be confused with — so the file says “small is not full” because a source said it, and can point at where.
Sources are ranked by trust, deterministic first and a model last:
- concept-tree node names and their existing
aliases(the subject and its children); - backticked tokens the pool keeps (file names, headers, paths, tools), seen at least twice, clustered by spelling;
definitionunits and “X is/are …” sentences → definitions; contrast cues →not:;--llm: the local model writes a missing definition or differentiator from at most six units that mention the term — and every name it returns must appear in those units, or the line is dropped. Lines that reach the file this way are markedorigin: llmwith a grounding score, and below the floor they carry verify before citing.
The line grammar
The full grammar, with every optional field shown:
# <Family> — vocabulary
> <n> terms of <family>; canonical name, definition, how it differs (not:), what people say instead (aka:). Each line anchored to the unit it came from.
<!-- generated by docset_refine vocabulary vN · <date> · sources: … -->
## Terms
- **<term>** [<sense-id>] (<pos>): <definition> — <url#anchor> · aka: a, b · not: <neighbour> — <how it differs> · ant: <antonym> · broader: <term> · narrower: <term>, <term> · related: <term> · measure: <unit> · field: <family-slug> · verified-as-of: <date>
## Homonyms
- **cookie** [web.cookie] · [folklore.cookie-monster] · [food.cookie]: <one line per sense with its family> — the sense picker's data
## Named, not yet defined
- <term> — seen in <n> units, no definition unit found (evidence rule)
Field by field:
| field | required | comes from | rule |
|---|---|---|---|
**term** | yes | tree node or canonical token | one line per term per sense |
[sense-id] | in a multi-family file | <family-slug>.<term-slug> | disambiguates the pair (term × family) |
(pos) | no | part of speech | noun unless stated |
definition | for a ## Terms line | a kept unit | must be extractive; its anchor is the line’s source |
— url#anchor | with a definition | the unit’s source | resolves to a heading on the page (P7) |
aka: | no | surface forms in the pool | never imported; the FTS5 layer expands through these |
not: … — how | no | contrast cues | the neighbour and one clause on the difference |
ant: | no | explicit antonyms | proposed extension |
broader: / narrower: / related: | no | the abstractor’s relation taxonomy | proposed extension |
measure: | no | the unit a quantity is stated in | proposed extension |
field: | no | the family slug | redundant with the sense id; kept for grep |
verified-as-of: | no | an actual re-fetch | a date bump without a fetch is not evidence |
Two honest notes on the grammar. The builder that exists today writes definition, aka:,
not: (with differs: for the how-clause), the source anchor, and the origin: llm marker;
ant:, broader:, narrower:, related: and measure: are proposed extensions that the
concept abstractor’s relation taxonomy is expected to supply. And contranyms — a word whose
senses oppose each other (sanction, cleave, oversight) — are two sense lines under
## Homonyms marked contranym, because a sense picker that offered only one would be
wrong half the time.
A line in the target grammar, with the proposed fields shown, for the term
llms-small.txt:
- **llms-small.txt** [llms.small] (noun): the budgeted variant of a full file — reference-class pages first, within about 50k tokens — /reference/formatting/#3-the-budgeted-file--llms-smalltxt · aka: small, llms-small · not: llms-full.txt — full is every page with no budget; small is a selection that fits a consumer's stable window · broader: llms-full.txt · related: manifest.json · measure: tokens (chars/4) · field: llms-txt
And the line the builder actually wrote for the same term in the llms.txt family’s own
llms-vocabulary.txt, abridged — no sense id, no broader:/related:/measure:, and the
how-clause under differs: rather than after a dash:
- **llms-small.txt** — llms-small.txt is a small variant of a tokenized text file used to enforce size budgets on the producer-side. · not: /_llms/, x-markdown-tokens, llms.txt, x-max-tokens · differs: not consumer-side truncation … — https://www.mintlify.com/docs/ai/llmstxt · evidence: hub estate · origin: llm (grounded 0.64)
The gap between the two is the honest state of the builder: the required fields ship, the relation fields do not yet. Either line tells an agent that “small” in a query is this file and that “full” is the neighbour it is contrasted with; only the first tells it that size is counted in tokens at four characters each.
Senses across fields
A sense id is <family-slug>.<term-slug>. A term is disambiguated by the pair (term ×
family): cookie in the web family is web.cookie, in a folklore family
folklore.cookie-monster, in a recipe family food.cookie. The three vocabularies are built
independently; the cross-family pass finds homonyms by matching term slugs across them and
writes a ## Homonyms line listing every sense with its family.
What a consumer does with that depends on its scope:
- scoped to a family — the query gets that family’s sense and the others are invisible; “cookie expiry” inside a web docset never sees the snack;
- unscoped — the query gets the sense picker: the homonym line, one sense per row, and the consumer (or the reader) chooses;
- crossing families on purpose — an agent that switches from the web docset to a recipe corpus reads the new family’s vocabulary first and stops guessing.
The model has a known seam. Keying senses by family may split a term that is really one
sense across two families — Link header in an HTTP family and in an llms-txt family are the
same header. A “same-as” link between senses is the obvious fix and is left open; until it
exists, the homonym line simply lists both.
Where it feeds
The vocabulary was built because three consumers were weak without it:
| consumer | what it takes | what changes |
|---|---|---|
| assignment — the topical builder’s keyword pass | aka: lists, merged into the concept-tree node’s aliases by --register (add-only) | a fact that says “session cookie” is filed under the node named “cookie” instead of falling to ## Shared |
| keyword — the FTS5 layer | aka: surfaces of a matched term, OR-ed into the query (designed: an expand flag on hub_query_docset, which today takes only docset, question, top, layer, mode) | an exact-token search for X-Markdown-Tokens would also find lines that wrote “the tokens header” |
| descriptions — the index exporter | the canonical definition | the one-liner after a link in llms.txt is the definition the pool agreed on, not a generated paraphrase |
A fourth consumer is the concept abstractor, which seeds its lexicon — synonyms, parts, sub-types, contrasts — from the family’s vocabulary before it harvests, and a fifth is the precedence ladder in the CLLMS essay, whose rung 4 is “agreement with the canonical definition” — which is a lookup in this file.
The acceptance bar for the keyword consumer is written down but not yet measured, because
server-side expansion is not shipped: when it lands, on the P12 question bank an expanded query
must raise exact-token recall by at least one hit per family without losing any. Until then a
client can do the expansion itself — read the term’s aka: list out of the vocabulary file and
send the surfaces as one mode="keyword" query.
Build one
The walkthrough below builds the llms.txt family’s own vocabulary — the terms are index, full, small, facts, twin, describedby, family, split root, unit, anchor and their neighbours. It is the same procedure for any field.
-
Assemble the pool. Any mix of
units.jsonl,llms-facts.txtand hub spokes. The pool is what the file may cite; nothing outside it can become a definition.PYTHONPATH=scripts .venv/bin/python -m docset_refine vocabulary \ --from outputs/exports/site.llms/llms-facts.txt \ --from skills/llms-deep-optimizer/references/attributes.md \ --subject "llms.txt" \ --out llms-topical/llms-txt.llms/ -
Read the candidates. The builder proposes terms from the tree, then from backticked tokens seen at least twice, then from definitions and contrast cues. A term with no definition lands in Named, not yet defined — that list is the research gap, not an error.
-
Decide about
--llm. Without it, every line is deterministic. With it, the local model writes the missing definitions from at most six evidence units each, and anything it names that the units do not contain is dropped. Lines it wrote are marked, and the floor (--floor) decides which ones say verify before citing. -
Lint.
llms_lint.py check llms-vocabulary.txt --kind vocabularyparses every line against the grammar and checks that every definition’s anchor resolves. The site’s CI runs this on its own file; 0 High is the bar. -
Register.
--registermerges each term’saka:into the matching concept-tree node’saliases. From then on the topical builder’s keyword pass matches the synonyms, and the FTS5 layer can expand through them. -
Serve. The file lands beside the family’s other files and is served at
/t/<slug>/llms-vocabulary.txtwith the same markdown headers as everything else.
The pilot bar for the llms.txt family is at least 40 terms, at least 5 not: contrasts, and
the cookie-style homonym demo across at least two families. The glossary
page is a hand page — the terms in the sense this site uses them — and
the site’s generated llms-vocabulary.txt is its machine twin; the two are checked against each
other, not derived one from the other.