Skip to content

D298 — The server builds the doctor's work list

Decision. A read-only kb_review tool returns ranked review items {kind, concepts, evidence, suggested_action} of five kinds — duplicate_candidate, zombie_work, promotion_candidate, glossary_gap, lint_judgement — computed on demand from the graph cache, the lint findings and the contracts. Creating a concept returns similar, the up-to-3 existing concepts whose title nearly matches. kb_status.review reports {total, by_kind}. Three contract keys say where the KB keeps what the server must not guess: promote_to, procedure_headings, glossary. An item is dismissed with lint_ignore: [<kind>] on a concept it names. This refines D290's split: the server still never judges (D14); it ranks candidates, the agent decides, the operator confirms.

Why. D290 gave the judgement half of repair no input except per-concept lint findings. A read-only audit of five real KBs found the rot is mostly relational and semantic: the same artifact documented twice in one map, procedures left in task journals, acronyms used in dozens of pages and defined nowhere, open tasks about archived services. Each has a cheap deterministic candidate signal even though deciding it needs an agent. Without the list every doctor session re-discovers the same things by reading, which does not scale past a few hundred concepts. The cost: a whole-KB walk per generation besides lint, borne by the D294 cache that kb_status already uses.

Alternatives rejected. A persisted review queue: it drifts from the KB the moment a write lands — D290's argument against a stored conformance level — and needs its own sync and conflicts. New dismissal state (a dismissed-items file): lint_ignore (D159) already is per-concept, versioned and visible in the history, and the agent writes the reason in the same commit. The server deciding duplicates or writing glossary entries: that is judgement (D14). A raw search-score threshold for similar: the score is corpus-relative (BM25 plus the centrality prior), so the search ranks the candidates and the qualification is the same title-token Jaccard ≥ 0.6 as duplicate_candidate, which a title sharing a single word never reaches unless both titles are that word. Guessing the glossary map from a type name or the promotion target from a map name: the KB says where they are.

Consequences. Every review kind is a lint_ignore name (perConceptChecks); TestReviewKindsDismissible fails for a kind that is not, or that a concept cannot dismiss. A glossary_gap names every concept using the term, so a concept's lint_ignore removes that concept from the count instead of the whole item. Visibility is applied outside the cache: an item naming a hidden concept is dropped, and the items derived from whole-graph checks need the whole KB. The tool joins the agent profile within the D285 budget, which took shortening map_update's description. Plan amendment (recorded in #486): doctor_suggested does not yet consider the review total — summarizeConformance is left unchanged so that #486 can rewrite it with the review total and a per-KB interval.