D295 — Lint findings are executable on real KBs¶
Decision. concept_expand rebases the concept's own relative markdown links to <id>/index.md, splicing only the body so the frontmatter keeps its bytes. Links already broken by past expansions are repaired by kb_repair: broken_link in an expanded index carries rebase_link when the href resolves against the pre-expansion <id>.md. duplicate_link becomes one finding per repeated target and carries drop_link_item (the exact list line) when the item is a single link and nothing else. map_misfit names only a map whose contract admits the concept's type. missing_value_contract never proposes provenance, tags, resource, secrets_source, and always considers status whatever its cardinality. A top-level scalar followed by indented - lines is malformed_frontmatter (warning, not suppressible). kb_repair applies body fixes as well as frontmatter ones, with the same dry-run, one-commit, stale_write-skip contract.
Why. On a ~740-concept work KB the D289/D290 checks reported the right areas but almost nothing was actionable: kb_repair had 0 fixes, all 19 broken_link were links written for the pre-expansion file (ExpandConcept moved the file with os.Rename and never rebased), all 37 map_misfit proposed a map whose strict ontology refused the type, and the vocabulary proposal skipped status (10 values over the cap of 8) while proposing provenance. 61 concepts had a provenance silently truncated by the parser, with validate OK. After the change, on a copy of that KB: kb_repair broken_link fixed 19/19 in 8 concepts, drop_link_item removed 633 bare duplicate items in 286 concepts, map_misfit dropped to 0, and the 61 truncated values became visible.
While validating, the D294 enumeration was found to report every link written to an expanded concept's index file or to a map's own index.md as broken (19 → 668 on that KB): neither is a walked concept ID. Existence now falls back to ReadConcept on a miss, so the fast path stays and the verdict is the one before D294; a test pins it.
Alternatives rejected. Teaching readers two link bases, as the KB's own instructions had to: every reader, now and future, would need it. Rebasing in lint at read time: the graph and the files would still disagree. drop_link_item keyed on the target ID: a label or a second link on the same line would be removed with it; the exact line is unambiguous and idempotent. Fixing bare_link_list mechanically: the reason for a link is content, it stays with the doctor. Making malformed_frontmatter an error: it would turn a 61-concept KB's gate red on upgrade.
Consequences. lint.FixableChecks gains broken_link and duplicate_link; lint.DropLinkItem is shared by lint tests and kb_repair. duplicate_link counts rise (one finding per target, not per concept). MapContract carries OntologyMode and ConceptTypes for the misfit filter. ~/ allow prefixes were already accepted (D159), so that part of the plan needed no change. The bundled kb-doctor skill lists the new fixable checks (version 1.1).