Skip to content

D276 — A KB glossary expands search and flags forbidden terms

Decision. A KB may declare its terminology in one KB-root file, glossary.yaml: a list of terms, each a required canonical with optional aliases and forbidden lists, compared through search.Fold. It is handled exactly like paths.yaml (D263) — written with artifact_write and validated strictly, read tolerantly, malformed entries reported as contract_malformed, never materialized on a client. search (and graph_context's seeding) runs a query once more for each variant obtained by replacing a whole-word canonical or alias with each other member of its group, at most 8 queries including the original, one level deep, merged by id at the best score and listed in expanded_to; a miss is recorded only when every variant is empty. lint reports a forbidden term used in prose outside code as forbidden_term (warning, suppressible).

Why. Keyword search (D135) has no notion of synonyms, so an acronym and its expansion, a product and its short name, or an old and a new name after a rename are unrelated queries: the first misses, and search_misses (D247) shows the miss with nothing an operator can do about it but rewrite pages. The write side has the mirror problem: an old name keeps reappearing because nothing flags it. One declared vocabulary fixes both. The cost is up to eight index passes for an expanded query, and one more KB-root format to validate.

Alternatives rejected. - Rewrite the query to its canonical form. It loses pages that only use the alias, and it makes the searched query differ from the typed one; running the original first and merging keeps today's result a subset of the new one. - Recursive or unbounded expansion. A group with many aliases, or a query naming several groups, would multiply index passes; one level and a cap of 8 bound the fan-out, and a group lists every member directly anyway. - Allow a string under two canonicals. An expansion would then be ambiguous and a forbidden term could be another group's alias; the validator refuses it and the tolerant read keeps the first term. - Inject the glossary into client instruction files. It would touch provisioning for a benefit the lint already delivers at write time. - Flag forbidden terms inside code. A command, a config key or a quoted old name in a fence is not prose; the code masking the link graph uses (D150) excludes it, and lint_ignore covers a migration note that quotes the old name in prose.

Consequences. A KB without glossary.yaml searches and lints exactly as before. Phrase matching is on word boundaries of the folded query, so an alias never matches inside a longer word. On the FTS5 backend a term shorter than three characters matches nothing alone (trigram tokenizer), so a two-letter alias finds pages only through its longer variants. A future change to search ranking must keep the merged result's order independent of which variant found a hit first.