SuperAPI AI Directory

wiki-lint

Writing & research

View repository

Updated 10-15 23:46

About this skill

Wiki Lint

Audit the wiki as a four-phase pipeline that keeps page bodies out of the main context: a deterministic script (Phase 1) and per-cluster subagents (Phase 2) do the heavy reading; the main thread only assembles findings. Produce a categorized report, offer concrete fixes, log the operation.

Pre-condition

Find SCHEMA.md (search from cwd upward, or ~/wikis/). If not found, tell the user to run wiki-init first. Read it to get wiki root path and conventions.

Link style. Read link_style: from SCHEMA.md. If the field is missing or <wiki-root>/config/link-style.md does not exist, default to obsidian ([[slug]]). Otherwise, read <wiki-root>/config/link-style.md. All link scanning below uses the ## Parse rule from that file (which may match more than one form). The lint report you write is itself a wiki page, so its cross-references use the ## Emit rule.

Why the pipeline

The old whole-wiki pass loaded every page body into one context — it nuked the context window as the wiki grew. Instead:

  • Phase 1 runs bin/lint-mechanical.py, which computes the deterministic checks (graph and string properties) with no LLM and no bodies in your context, and emits the tag-cluster list for Phase 2.
  • Phase 2 dispatches one subagent per tag cluster. Each reads only its cluster's pages (bodies live in the subagent's context, discarded on return) and reports the cross-page and body-level findings. You aggregate compact findings, never bodies.

Phase 1 checks are whole-wiki exhaustive. Phase 2 checks are cluster-scoped: a page that shares no tag with any other page is not body-checked (approach A's accepted recall limit — see Phase 2). Run lint periodically (every 5–10 ingests) so the scoped passes accumulate coverage.

Process

Phase 0 — regenerate the index

python bin/generate-index.py so the index reflects current page frontmatter.

Phase 1 — deterministic checks (script, no LLM)

Run python bin/lint-mechanical.py and parse its JSON. It returns:

{ "findings": { "broken_links": [...], "missing_frontmatter": [...], "orphans": [...],
                "slug_collisions": [...], "stale_date": [...], "missing_concept": [...] },
  "clusters": [ {"pages": ["slug-a", "slug-b"], "split": false}, ... ] }

These map to report sections directly:

  • 🔴 Broken links — broken_links ({page, link}).
  • 🔴 Missing frontmatter — missing_frontmatter ({page, missing:[...]}). overview.md lives in wiki/, not wiki/pages/, and is exempt.
  • 🟡 Orphan pages — orphans ({page}): zero inbound [[slug]] links.
  • 🟡 Colliding slugs — slug_collisions ({token, pages:[...]}): a bare slug clashing with qualified ones (mercury vs mercury-element). Flag the pair; never auto-rename — renaming is a wiki-merge split.
  • 🟡 Stale claims — stale_date ({page}): not updated in 90 days and the body still says current/latest/recent/state-of-the-art or names a year ≥2 years old.
  • 🔵 Missing concept pages — missing_concept ({slug, count}): a [[slug]] referenced 3+ times with no page.

Do not re-derive these by reading pages yourself — the script is the source of truth.

Phase 2 — cluster subagents (contradictions + body-level checks)

For each cluster in the Phase 1 clusters list, dispatch a subagent (parallel fan-out, batched to bound concurrency). Give it the cluster's page paths and this instruction:

Read these pages. Report, within these pages only, as a compact list — each finding as {type, pages:[slug,...], detail}, or clean if none:

  • contradiction — two pages describe the same entity with conflicting dates, counts, names, or relationships.
  • missing-xref — two pages clearly discuss the same entity but don't [[link]] each other.
  • merge-candidate — two pages describe the same concept under different slugs (near-duplicate titles / near-identical Description sections).
  • chronological-section — a page body carries date-stamped journal headers (## [Month], **[Month] N update) that should be integrated in-place.
  • addable-line-range — a footnote citing a text-addressable raw file (markdown / plaintext / code / cached HTML) with no L<start>-<end> token. Exempt: PDFs, transcripts, live URLs — never flag those. Return findings only — no page bodies.

Aggregate all subagent findings in the main thread and dedup on (type, sorted(pages), detail-entity) — a page in K tags sits in K clusters, so the same finding can surface more than once.

Coverage note: because subagents only see their cluster, body-level findings are cluster-scoped. Pages sharing no tag with any other page are not body-checked. This is the accepted limit of tag-cluster scoping; Phase 1 (graph/string checks) still covers every page.

Phase 3 — coverage gaps, then assemble the report

First, the one body-level check that needs no page bodies: read wiki/overview.md and flag 🔵 Coverage gaps — open questions that a web search or a new ingest could answer.

Then write wiki/pages/lint-<today>.md (do not ask permission — always write this), merging Phase 1 findings, the deduped Phase 2 findings, and the coverage gaps. The template below shows slug references in obsidian form for readability; in the actual report you write, every slug reference must follow the ## Emit rule from config/link-style.md:

---
title: Lint Report <today>
category: Maintenance
summary: <N> errors, <N> warnings, <N> info
tags: [lint, maintenance]
sources: []
created: <today>
updated: <today>
---

# Lint Report — <today>

## Summary
- 🔴 Errors: N
- 🟡 Warnings: N
- 🔵 Info: N
<!-- note here if any cluster was split (Phase 1 `split: true`): contradiction recall is
     reduced for those pages — they were too large a tag group to check together -->

## 🔴 Broken Links
- [[source-page]] references [[missing-slug]] — does not exist
  Fix: create the page or remove the reference

## 🔴 Missing Frontmatter
- [[page]] is missing: title, updated

## 🟡 Orphan Pages
- [[slug]] — no inbound links
  Fix: add link from [[related-page]], or delete if no longer relevant

## 🟡 Contradictions
- [[page-a]] says: "<claim>"
- [[page-b]] says: "<conflicting claim>"
  Recommendation: <which to trust, or "investigate further">

## 🟡 Stale Claims
- [[page]] last updated <date>, contains "latest" — may be outdated
  Fix: re-verify claims or add a "as of <date>" qualifier

## 🟡 Colliding Slugs
- [[mercury]] and [[mercury-element]] share base token "mercury" — possible undisambiguated homonym
  Fix: qualify the bare slug via wiki-merge split (e.g. mercury → mercury-planet)

## 🟡 Chronological Update Sections
- [[page]] has date-stamped journal headers — integrate the updates in-place

## 🔵 Missing Concept Pages
- [[slug]] referenced N times but no page exists
  Fix: run wiki-ingest or create a stub

## 🔵 Coverage Gaps
- Open question from overview.md: "<question>"
  Suggestion: search for <X> or ingest <source type>

## 🔵 Missing Cross-References
- [[page-a]] and [[page-b]] both discuss <entity> but don't link to each other

## 🔵 Merge Candidates
- [[page-a]] and [[page-b]] appear to describe the same concept under two slugs
  Fix: run wiki-merge to consolidate into one page (rewrites inbound links)

## 🔵 Addable Line-Ranges
- [[page]] [^3] cites text source [[markdown-source]] with no line-range
  Fix: add an `L<start>-<end>` token so wiki-audit can verify it deterministically

The lint report carries category: Maintenance in its frontmatter, so it lands in the index automatically — do not hand-edit index.md. Run python bin/generate-index.py.

Offer concrete fixes

For each fixable category, offer:

  • Broken links: "Remove the broken slug references? (I'll show each change before writing)"
  • Missing cross-references: "Add the missing links between these page pairs?" (emit in the wiki's link_style)
  • Colliding slugs / merge candidates: "These look like the same concept (or a homonym needing disambiguation) — want to run wiki-merge to consolidate or split them?" Do not fix these in wiki-lint; hand off to wiki-merge.
  • Orphan page tags: "Add status: orphan to frontmatter of orphan pages?"
  • Missing frontmatter: "Add missing frontmatter fields with placeholder values?"

Show the exact diff for each change before writing. Apply only after confirmation.

Record the operation

Per SCHEMA's Operation Log & Commit Convention:

  • Git wiki: suggest a commit (default Conventional Commits fix: when fixes were applied, chore: for a report-only run, plus the trailer); commit on confirmation.
    fix: resolve <what lint fixed>
    
    Wiki-Op: lint
    
  • Non-git wiki: append to wiki/log.md:
    ## [<today>] lint | <N errors> errors, <N warnings> warnings, <N info> info
    Report: <slug reference to lint-<today>, in the wiki's link_style>
    Fixed: <list what was auto-fixed, or "none">