feat(wiki): kb.wiki_lint - orphan/dead-link/stale/uncited page checks - #729
Open
joaovictor91123 wants to merge 3 commits into
Open
feat(wiki): kb.wiki_lint - orphan/dead-link/stale/uncited page checks#729joaovictor91123 wants to merge 3 commits into
joaovictor91123 wants to merge 3 commits into
Conversation
health.lint() is thorough for claim/proposal health (broken citations, stale claims, dangling refs) but pages have no sibling sweep - a [[wikilink]] whose target is later archived, a page nothing links to, a page untouched in a year, or a hand-edited page whose citation coverage eroded after approval all surface nowhere. wikilink validation and citation coverage were previously checked exactly once, at compile.py's draft-approval gate, never again after a page is live. add a new wiki_lint module, registered the same way kb.lint is on all four surfaces (mcp kb_wiki_lint, jsonl kb.wiki_lint, capabilities.py, cli vouch wiki-lint), reusing wiki_render's link index/backlinks and compile's citation-coverage sentence 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 (deduplicated per page+target) - stale_page: not updated in --stale-days (default 180) - uncited_section: citation coverage below --min-coverage (default 0.5, same threshold compile.py enforces on drafts) archived pages are excluded entirely from what's checked, and treated as unresolvable link targets - matching render-wiki's own vouchdev#695 exclusion policy (a link to an archived page is exactly as dead as a link to nothing). purely read-only, like wiki_render's existing derived views - never proposes, writes, or mutates, so it doesn't touch proposals.py, lifecycle.py, or the review gate. none of the four checks are data-integrity errors the way health.lint's broken_citation is, so `ok` stays true even with findings. Closes vouchdev#726
test_hot_memory_universal_coverage asserts every kb.* method is either in HOT_MEMORY_COVERED or explicitly excluded in HOT_MEMORY_EXCLUDED - a new method with neither fails the suite. kb.wiki_lint landed with neither, breaking CI. excluded with the same reason kb.lint/kb.doctor already carry: diagnostic findings, no claim payload to decorate.
…path diff-coverage flagged three gaps: server.py's kb_wiki_lint body (only exercised indirectly via jsonl, never called directly), cli.py's wiki-lint command (had zero test coverage at all), and wiki_lint.py's naive-datetime normalization branch in the stale_page check (every existing test used tz-aware datetime.now(UTC) timestamps). add a direct server.kb_wiki_lint() test diffed against a direct wiki_lint() call, CliRunner tests for both a findings case and a clean-kb case, and a stale_page case with a timezone-naive updated_at (hand-written yaml or an older on-disk page predating a tz-aware write path both produce this shape).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
New
kb.wiki_lintmethod — a page-level sibling tokb.lint's claim-healthsweep, registered on all four surfaces (MCP
kb_wiki_lint, JSONLkb.wiki_lint,capabilities.py, CLIvouch wiki-lint). Four checks overthe live, already-approved page set:
orphan_page— zero inbound[[wikilinks]](viawiki_render.backlinks).dead_wikilink— a[[link]]that doesn't resolve against the live,non-archived page set (via
wiki_render.resolve_link; deduplicated perpage+target so a repeated dead link isn't reported twice).
stale_page— not updated in--stale-days(default 180, mirroringvouch lint --stale-days).uncited_section— citation coverage below--min-coverage(default0.5, reusing
compile.py's own_citation_coveragesentence walk andMIN_CITATION_COVERAGEthreshold).Why
ROADMAP.md's 1.4 milestone: "wiki lint: orphan pages, stale pages, dead
links, uncited sections." None of the four existed. Wikilink resolution and
citation coverage were previously checked exactly once — at
compile.py'sdraft-approval gate, before a page is ever proposed — and never again once
it's live. A link whose target is later archived, a hand-edited page whose
coverage erodes after approval, or a page nothing else links to all
surfaced nowhere.
New module rather than folding into
health.lint():lint()'s docstringscopes it explicitly to claim/proposal health, and its own
stale_after_daysalready means something different (claim confirmationvs. page edit recency) — a shared flag with two meanings under one method
would be confusing.
kb.lint/kb.doctoralready establish the patternof separate entry points for separate sweep scopes.
Closes #726
What might break
Nothing for users with an existing
.vouch/directory — purely additive:a new method, a new module, no object model or on-disk shape change. Fully
read-only, like
wiki_render's existing index/MOC/backlinks — neverproposes, writes, or mutates, so it doesn't touch
proposals.py,lifecycle.py,storage.py, or the review gate. None of the four findingsare
errorseverity (none represent a data-integrity violation the wayhealth.lint'sbroken_citation/dangling_relationdo), sookstaystrueeven with findings — consistent withhealth.lint's own framing of"user-actionable, not necessarily broken."
Archived pages are excluded entirely from what gets checked, and treated
as unresolvable link targets — this matches
render-wiki's existing (#695)exclusion policy exactly (a link to an archived page is exactly as dead as
a link to nothing), so it's not a new precedent.
VEP
Not applicable — no object model, on-disk layout, bundle format, or
audit-log shape change. A new read-only aggregation method, same shape as
kb.explain_ranking/kb.effectiveness/themes.detect_themes.Tests
make check-equivalent: ruff clean (src+tests); mypyclean on
wiki_lint.py;tests/test_capabilities.py(four-surfaceparity),
tests/test_health.py,tests/test_wiki_render.py,tests/test_wiki_lint.py,tests/test_cli.pyall passtests/test_wiki_lint.py(14 cases: each check's positive/negativecase, archived-page exclusion from both the checked set and as a
link target, dedup of repeated dead links,
oksemantics, JSONLenvelope success and param handling)
CHANGELOG.mdupdated under## [Unreleased]