You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ROADMAP.md's 1.4 milestone calls for "wiki lint: orphan pages, stale pages,
dead links, uncited sections" — none of the four exist today. Once a page
is approved, nothing re-checks it: a [[wikilink]] whose target is later
archived, a page nothing else links to, a page not touched in a year, or a
hand-edited page whose citation coverage eroded after approval all surface
nowhere. health.lint()/vouch lint is the equivalent sweep for claims
(broken citations, stale claims, dangling refs) — pages have no sibling.
What you've tried
wiki_render.backlinks() already computes the inbound-link map (used
internally by render_moc), but nothing flags a page with zero entries.
wiki_render.resolve_link() link resolution is only exercised at render
time (silently drops anything unresolved) and at compile.py's
draft-approval gate (_first_dangling_link rejects a new draft with a
dangling link) — never as a standing check against the live, approved
page set.
compile.py's _citation_coverage() / MIN_CITATION_COVERAGE guardrail
runs once, on a draft, before it's ever proposed — a page's body can
drift after approval without re-triggering it.
kb.wiki_lint — a page-level sibling to kb.lint, registered the same
way (MCP kb_wiki_lint, JSONL kb.wiki_lint, CLI vouch wiki-lint),
reusing wiki_render's link index/backlinks and compile's
citation-coverage walk rather than reimplementing either:
orphan_page — zero inbound backlinks.
dead_wikilink — a [[link]] that doesn't resolve against the live
(non-archived) page set.
stale_page — not updated in --stale-days (default 180, mirroring vouch lint --stale-days).
Suggested severities: orphan_page as info (a root/index page is
naturally unlinked, not inherently wrong), the other three as warning
(actionable, matching stale_claim's existing severity) — none as error,
since none represent a data-integrity violation the way broken_citation
or dangling_relation do.
Compatibility considerations
Purely additive, read-only, no VEP needed — no object model, on-disk
layout, or existing kb.* method signature changes. Like wiki_render's index/MOC/backlinks, this is a derived view over already-
approved pages, so it doesn't touch proposals.py, lifecycle.py, or the
review gate at all.
Alternatives
Folding these checks into health.lint()/kb.lint directly instead of a
new method — rejected because lint()'s docstring scopes it explicitly to
claim/proposal health, and its stale_after_days parameter already means
something different (claim confirmation, not page edit recency); a shared stale_days flag with two different semantics under one method would be
confusing. A dedicated method mirrors how kb.lint and kb.doctor are
already two separate entry points for two different sweep scopes.
What you're trying to do
ROADMAP.md's 1.4 milestone calls for "wiki lint: orphan pages, stale pages,
dead links, uncited sections" — none of the four exist today. Once a page
is approved, nothing re-checks it: a
[[wikilink]]whose target is laterarchived, a page nothing else links to, a page not touched in a year, or a
hand-edited page whose citation coverage eroded after approval all surface
nowhere.
health.lint()/vouch lintis the equivalent sweep for claims(broken citations, stale claims, dangling refs) — pages have no sibling.
What you've tried
wiki_render.backlinks()already computes the inbound-link map (usedinternally by
render_moc), but nothing flags a page with zero entries.wiki_render.resolve_link()link resolution is only exercised at rendertime (silently drops anything unresolved) and at
compile.py'sdraft-approval gate (
_first_dangling_linkrejects a new draft with adangling link) — never as a standing check against the live, approved
page set.
compile.py's_citation_coverage()/MIN_CITATION_COVERAGEguardrailruns once, on a draft, before it's ever proposed — a page's body can
drift after approval without re-triggering it.
render-wikialready excludes archived pages from the front door (fix(wiki): render-wiki includes archived pages in index and MOC #695);nothing extends that policy to say a link into an archived page is
dead.
Suggested shape
kb.wiki_lint— a page-level sibling tokb.lint, registered the sameway (MCP
kb_wiki_lint, JSONLkb.wiki_lint, CLIvouch wiki-lint),reusing
wiki_render's link index/backlinks andcompile'scitation-coverage walk rather than reimplementing either:
orphan_page— zero inbound backlinks.dead_wikilink— a[[link]]that doesn't resolve against the live(non-archived) page set.
stale_page— not updated in--stale-days(default 180, mirroringvouch lint --stale-days).uncited_section— citation coverage below--min-coverage(default0.5, mirroring
compile.py's ownMIN_CITATION_COVERAGE).Suggested severities:
orphan_pageasinfo(a root/index page isnaturally unlinked, not inherently wrong), the other three as
warning(actionable, matching
stale_claim's existing severity) — none aserror,since none represent a data-integrity violation the way
broken_citationor
dangling_relationdo.Compatibility considerations
Purely additive, read-only, no VEP needed — no object model, on-disk
layout, or existing
kb.*method signature changes. Likewiki_render's index/MOC/backlinks, this is a derived view over already-approved pages, so it doesn't touch
proposals.py,lifecycle.py, or thereview gate at all.
Alternatives
Folding these checks into
health.lint()/kb.lintdirectly instead of anew method — rejected because
lint()'s docstring scopes it explicitly toclaim/proposal health, and its
stale_after_daysparameter already meanssomething different (claim confirmation, not page edit recency); a shared
stale_daysflag with two different semantics under one method would beconfusing. A dedicated method mirrors how
kb.lintandkb.doctorarealready two separate entry points for two different sweep scopes.