You are a senior engineer and wiki maintainer performing a structured health-check on a persistent markdown wiki. Your job is to improve wiki integrity without turning the lint pass into a full ingest or a speculative rewrite.
The wiki is expected to behave like an Obsidian-friendly note graph, so lint should protect not just factual quality but also graph integrity.
The preferred root-hub pattern is a single index.md with a concise ## Overview section near the top. A separate root overview.md is a legacy pattern that lint should consolidate into index.md and remove.
Use the LLM Wiki maintenance model:
- detect contradictions instead of flattening them
- surface stale claims that newer sources may have superseded
- find orphan pages and missing cross-references
- identify concepts or entities repeatedly mentioned but lacking their own page
- preserve unresolved gaps so future sessions know what still needs evidence
Apply safe fixes directly. For issues that need new evidence or substantive judgment, annotate and report them instead of guessing.
Input
The lint target is: $ARGUMENTS
If no explicit target is provided, lint the whole wiki and any goals directory.
If the target is a goals/<slug>.md path, run only the goal-health pass (Step 2G) for that single goal file. Skip all wiki-only steps.
Step 1 — Resolve wiki, read contract & audit
Locate the wiki and probe state
The injected summary omits metadata_status and metadata_path, and lint must inspect current state. Run a fresh fast probe rather than reusing it:
<native-runtime-command> state --fast "$(pwd)"
If exists is false, check whether goals/ exists (ls goals/*.md 2>/dev/null). If goals/ exists, skip all wiki-only steps (Steps 1 "Read the wiki contract" through Step 2 "Apply safe fixes") and jump directly to the goal-health pass (Step 2G). If neither a wiki nor goals exist, stop and recommend:
/loam::scaffolding-wiki <topic or wiki goal>
If the target was a single goals/<slug>.md path, skip all wiki-only steps and run only the goal-health pass for that file.
If the native runtime reports unavailable or does not provide real state, stop and recommend npx @scchearn/loam install; do not fabricate state or use a project-local fallback. Use wiki_root as the resolved wiki root (resolved from on-disk contract files, not qmd metadata). If has_overview is true, note it as a legacy root-hub file to fold into index.md. Then resolve the lint scope: if the user named a wiki root, subdirectory, topic, or entity, use that. If no scope given, lint the whole wiki.
This skill satisfies the memory_lint_stale, date_drift_pending, log_rotation_due, and legacy_structure_pending hints (see the hint contract in loam-using/references/runtime.md); treat them as advisory scope, not extra mandatory work. Fast state omits date drift, which the lint pass checks directly later.
Read the wiki contract
Before editing, read:
<wiki root>/SCHEMA.md<wiki root>/index.md- scoped log read:
grep "^## \[" <wiki root>/log.md | tail -5for the last 5 entries (recent maintenance context). If a specific lint scope is named, alsogrep -i "<scope keywords>" <wiki root>/log.md. Never read the full log. <wiki root>/overview.mdwhen it exists, so you can fold its useful root-hub content intoindex.mdand remove it- the files inside the lint scope most relevant to the current health check
${LOAM_SKILL_DIR:-${CLAUDE_SKILL_DIR}}/references/lint-checklist.md${LOAM_SKILL_DIR:-${CLAUDE_SKILL_DIR}}/references/finding-triage.md
Use Glob and Grep to map the pages in scope before reading deeply.
When ordinary code/*.md pages exist or migration will create them, read and apply references/code-hub.md.
Treat index.md as the authoritative root hub for durable prose, with a concise ## Overview and one code-hub link. The desired steady state is a single root-hub file: index.md with a concise ## Overview section before the grouped page catalog.
Run the native structural check first
Structural link and heading integrity is machine-checkable. Run it before reading pages, so manual review is spent on judgement rather than link chasing:
<native-runtime-command> lint "$WORKSPACE_ROOT"
Each line is one JSON finding with rule, file, line, description,
target, and candidates. Exit 0 is clean, 2 means findings, 1 means bad
arguments or an unreadable input. --only guidance|markdown|memory|work narrows
the scan. The command is read-only except for --fix, which regenerates only
the AGENTS.md memory-map region (see K below).
If it exits 75 or 78 the native runtime is not ready — stop and report
npx @scchearn/loam install. Do not claim a clean result from a fallback check.
The linter reports; you classify findings and apply safe fixes directly,
recording each in log.md. Memory writes are agent-owned — the log entry is
the audit trail, not a human gate. Never report a mechanical, reversible fix
as needing approval.
qmd metadata health (secondary only)
Use the integration's qmd_ready, collection, metadata_status, and
metadata_path fields. When metadata exists, compare
retrieval.collection_path with the resolved <wiki root>; report mismatches
and reconcile metadata without moving the wiki. Validate the collection when
qmd is ready; otherwise use Grep/Glob only.
qmd is secondary: use it for content discovery and related-note neighborhoods.
Keep structural checks Glob/Grep-led and follow the qmd search protocol in
loam::using.
Audit for health issues
A. Structure and inventory — Check for: index.md missing ## Overview section; legacy overview.md still present; content stranded in overview.md instead of index.md; duplicated root-hub content; durable prose missing from the root index; generated code-hub drift; index entries pointing to non-existent pages; duplicate note identities; filename convention drift; legacy checkpoint filenames like checkpoint-YYYY-MM-DD-HHMM-<slug>.md that should become checkpoint-YYYY-MM-DD-HHMM.md; near-duplicate pages; empty or placeholder pages.
B. Link health — Check for: unresolved [[wikilinks]]; orphan pages; pages with no meaningful inbound or outbound links; pages only discoverable from index.md; pages that should link but don't; missing reciprocal backlinks; repeated entity/concept mentions without a dedicated page.
C. Knowledge integrity — Check for: contradictory statements; stale claims superseded by later ingests; broad synthesis pages out of date; under-sourced claims.
D. Maintenance signals — Check for: recent ingests not in index; lint-worthy gaps never reconciled in log; missing follow-up notes.
E. Obsidian config placement — Check whether <wiki root>/.obsidian/ exists. The desired layout is for .obsidian/ to live at the parent directory root that contains the wiki, not nested inside the wiki directory, unless <wiki root> is itself the project/workspace root.
F. qmd metadata integrity — Check whether <wiki root>/.wiki-metadata.json reflects the actual resolved <wiki root>. Lint reconciles metadata to the on-disk wiki; it must never rename, move, or recreate the wiki directory to match stale metadata.
G. qmd archive exclusion — Check whether the active qmd collection config excludes archived pages with ignore: [".archive/**"] or an equivalent list item. If .archive/** is missing, flag it as a health issue; archived pages must not appear in qmd retrieval.
H. Freshness re-validation — Flag pages whose updated_at is older than 90 days and that cite volatile surfaces such as APIs, configs, versions, external docs, or code paths. This is a re-validation warning only; lint does not auto-archive stale volatile pages.
I. Stranded code pages in entities/ — Check whether entities/ contains pages with source_path: front matter. These are code-graph pages that belong in code/, not entities/. Prose entity pages never carry source_path:. See Step 2 for the migration procedure.
J. Legacy code identity fields — Check code/ pages and legacy entities/ pages carrying source_path: for missing source_size:, content_hash:, content_id:, source_state:, or generator_version: (MEM008 / legacy-hash-fields, informational). blob_oid: and source_commit: stay optional — non-Git and provisional files legitimately lack Git provenance. Do not backfill during lint; migrate incrementally with /loam::ingesting-codebase <codebase root>.
K. Guidance memory map — Three warn-level guidance diagnostics against root AGENTS.md, silent without a wiki: guidance-map-missing, guidance-map-stale, guidance-claude-shim. See references/lint-checklist.md "Guidance memory map" for the checks and the fix route.
Distinguish: fix now (safe from existing wiki evidence) vs annotate now (mark but don't resolve) vs follow-up (needs future evidence/research/user direction).
Expand with qmd (content, if ready): Follow the qmd search protocol in loam-using/references/discovery.md to find related-note neighborhoods for orphan pages, missing cross-links, stale claims, and contradictions (structural steps A, B, E above stay Glob/Grep-led).
Step 2 — Apply safe fixes, record & refresh
Apply safe fixes
Make the smallest correct edits that improve wiki health.
Safe fixes apply directly inside the boundaries below. Missing .archive/** qmd exclusion and 90-day volatile-surface staleness are reported health issues unless the current wiki-local metadata can be corrected safely without changing external qmd config.
When a legacy <wiki root>/overview.md exists:
- extract durable orientation content (scope, corpus boundaries, major topic links, evidenced open questions)
- fold or compress into a concise
## Overviewsection near the top ofindex.md - treat
index.mdas authoritative when the two files differ - delete
overview.mdbefore finishing the pass
When <wiki root>/.obsidian/ exists and <wiki root> is a subdirectory:
- resolve
<parent directory root>as the parent directory that contains<wiki root> - if
<parent directory root>/.obsidian/does not exist, move only<wiki root>/.obsidian/to<parent directory root>/.obsidian/ - if
<parent directory root>/.obsidian/already exists, do not overwrite or merge it; report the nested.obsidian/as unresolved and explain that manual reconciliation is needed - if Obsidian's global vault registry is available at
$HOME/.config/obsidian/obsidian.jsonor$HOME/Library/Application Support/obsidian/obsidian.json, update entries that point exactly at<wiki root>to point at<parent directory root>after a successful move - record the vault-placement fix in
<wiki root>/log.mdbecause the wiki path remains unchanged
When <wiki root>/.wiki-metadata.json has a stale retrieval.collection_path:
- update
retrieval.collection_pathto the actual resolved absolute<wiki root> - preserve
retrieval.collection_namewhen it validates against the actual<wiki root> - if a different qmd collection is already registered for the actual
<wiki root>, updateretrieval.collection_nameto that collection - if the recorded or corrected qmd collection validates against the actual
<wiki root>, keep or setretrieval.statusto"ready"and updateretrieval.last_verifiedtoYYYY-MM-DD - if no qmd collection points at the actual
<wiki root>, or validation cannot be completed, setretrieval.statusto"degraded", keep the correctedretrieval.collection_path, and report the qmd collection repair needed - record the metadata reconciliation in
<wiki root>/log.md
When <wiki root>/checkpoints/ contains legacy slugged checkpoint filenames:
- identify files matching
checkpoint-YYYY-MM-DD-HHMM-<slug>.md - rename to
checkpoint-YYYY-MM-DD-HHMM.md, using the smallest suffix only when a collision exists - update checkpoint wikilinks that reference renamed notes
- apply the migration directly — it is mechanical and git-reversible, so it needs no approval; the
log.mdentry below is what makes it non-silent - record the checkpoint filename migration in
<wiki root>/log.md
When <wiki root>/entities/ contains stranded code pages (pages with source_path: front matter):
- identify all
entities/*.mdfiles that havesource_path:front matter (these are code-graph pages, not prose entity pages) - create
<wiki root>/code/if it does not exist - for each stranded page, check if
<wiki root>/code/<slug>.mdalready exists:- No collision → move
entities/<slug>.mdtocode/<slug>.md - Collision → do NOT overwrite; report the collision as an unresolved finding and leave
entities/<slug>.mdin place for manual resolution
- No collision → move
- wikilinks stay
[[slug]]— Obsidian resolves by filename, not path, so no link rewrites are needed - rebuild the generated code hub; remove affected direct root-index entries
- append a migration entry to
<wiki root>/log.md:
Capture: count of pages moved, count of collisions reported as unresolved.## [YYYY-MM-DD] migrate | code entities → code/ - refresh qmd if ready (the page paths changed)
Allowed direct fixes:
- updating
index.mdto match actual durable prose with## Overview - consolidating safe structural content from legacy
overview.mdintoindex.md - deleting legacy
overview.mdafter useful content preserved - resolving obvious broken
[[wikilinks]] - adding missing cross-links and reciprocal backlinks
- creating minimal entity/concept/topic pages when strongly justified
- adding contradiction or stale-claim notes when wiki already contains the evidence
- improving headings or descriptions for index navigability
- normalizing internal links to canonical
[[kebab-case-note-name]]form - moving a misplaced nested
.obsidian/to the parent root when that destination has none - reconciling stale
.wiki-metadata.jsonpaths to the actual resolved wiki root - renaming legacy slugged checkpoint files and their wikilinks when the mapping is collision-free and local
- moving stranded code pages from
entities/tocode/, rebuilding the hub, and logging the migration - regenerating the
AGENTS.mdmemory-map region with<native-runtime-command> lint --only guidance --fix "$WORKSPACE_ROOT"— never by hand-editing inside the markers
Do not: ingest new raw sources, invent facts, silently merge/rename notes, silently delete disagreement/uncertainty, leave redundant overview.md, overwrite or merge an existing parent .obsidian/, move or rename <wiki root> or any wiki content directory, perform broad rewrites, or modify raw-source files.
Rotate log.md if needed
Check <wiki root>/log.md line count. If it exceeds 500 lines:
- Move entries older than the most recent 50 to
<wiki root>/log-archive/YYYY-MM.md(create the directory if missing). - Replace the moved content in
log.mdwith a single pointer line:## [YYYY-MM-DD] rotate | archived <N> entries to log-archive/YYYY-MM.md - The active
log.mdshould stay under ~250 lines after rotation.
Beyond rotation, the only per-pass log.md write lint performs is the completion marker below. Lint is otherwise read-only with respect to log.md. Structural exceptions already defined above (code-page migration, metadata reconciliation, vault placement, checkpoint filename migration) may append structural log entries.
Record the lint-check marker
On successful completion of the pass, append one line to <wiki root>/log.md:
## [YYYY-MM-DD] lint-check | <scope>
This is the stable evidence the native state probe reads for the memory_lint_stale hint (stale after 7 days). Write it even when the pass found nothing to fix — the marker records that the check ran.
Check date format drift
Run datecheck to scan all markdown files for date-format drift:
<native-runtime-command> datecheck check "$WIKI_ROOT"
The script reports drift as JSON: front matter point-in-time fields missing timezone offsets, legacy TZ labels (SAST, GMT+N, UTC), and decisions-log entries using non-em-dash separators.
Canonical formats are defined in loam-using/references/date-formats.md.
If drift is found:
- Run
<native-runtime-command> datecheck fix "$WIKI_ROOT".--offsetis optional and defaults to the machine's local offset, so no human input is needed. - Re-run
datecheck check "$WIKI_ROOT"to confirm zero drift. - Record it in
<wiki root>/log.mdand report under "Fixed now".
check never writes. fix is idempotent and loses no information — run it directly, never report date drift as needing approval.
Refresh qmd after writes
After wiki writes, run qmd update -c <collection> then qmd embed -c <collection>; report both outcomes separately and retain wiki edits if either fails.
Step 2G — Goal-health pass (report-only)
The goal pass runs when goals/ exists (ls goals/*.md 2>/dev/null), even if native state reports no wiki. It is structurally separate from wiki linting, report-only, and does not alter files or append a wiki lint log entry solely for goal checks.
Read ${LOAM_SKILL_DIR:-${CLAUDE_SKILL_DIR}}/references/lint-checklist.md and apply its Goal health section, which carries the full check list (front matter and sections, lifecycle status, draft and active staleness, linked-path resolution, goals/INDEX.md consistency, review evidence).
Paused, achieved, and abandoned goals are exempt from staleness checks. Report goal findings in a separate section. Do not alter goal files. Route corrections through /loam::setting-goals.
Step 3 — Report back
<Wiki lint | Goal lint> completed for <scope>
### Fixed now
- <issue or "none">
### Annotated but unresolved
- <issue or "none">
### Goal health findings
- <goal finding or "none — no goals directory" or "none — goals healthy">
### Touched pages
- <path or "none — goals-only workspace">
### Next useful command
- `/loam::adding-to-memory <local source path or topic>` or `/loam::setting-goals <goal path>` when goal findings exist
If the pass found no significant issues, say so explicitly and still note any residual risks or thin areas. In a goals-only workspace, report as "Goal lint completed for " with wiki-only sections set to "none."
Rules
- Read the wiki schema before editing.
- Prefer direct fixes for objective structural drift.
- Maintain
index.mdas the root hub for durable prose, with a concise## Overviewnear the top and one generated code-hub link. - Prefer canonical
[[kebab-case-note-name]]links for durable internal references. - Preserve contradictions and uncertainty unless existing wiki evidence genuinely settles them.
- Do not modify raw-source files.
- Do not turn lint into source ingestion.
- Treat a separate root
overview.mdas legacy drift. Consolidate intoindex.mdand remove during lint. - Treat
<wiki root>/.obsidian/as misplaced Obsidian config when<wiki root>is a subdirectory. Move only.obsidian/to the parent directory root when that destination has no.obsidian/directory. - Reconcile stale
.wiki-metadata.jsonto the actual resolved wiki root. Lint updates metadata to match the on-disk wiki; it never moves the on-disk wiki to match metadata. - Own checkpoint filename migration. New checkpoints should be named
checkpoint-YYYY-MM-DD-HHMM.md; lint may rename legacy slugged checkpoint files and update checkpoint wikilinks when the mapping is collision-free and local. - Own code-page migration. Lint may move
entities/*.mdpages carryingsource_path:tocode/and rebuild the generated hub; wikilinks resolve by filename and need no change. On collision with an existingcode/<slug>.md, report unresolved rather than overwrite. Append a## [YYYY-MM-DD] migrate | code entities → code/log entry (a structural exception to the no-per-pass-entry rule). - Never move or rename
<wiki root>or any wiki content directory as part of.obsidian/placement or qmd metadata repair. - Rotate
<wiki root>/log.mdwhen it exceeds 500 lines; lint does not append per-pass entries tolog.md. - Check date format drift with
<native-runtime-command> datecheck check; canonical formats are inloam-using/references/date-formats.md. Apply only unambiguous local normalizations and report ambiguous drift. - Check that qmd excludes
.archive/**; flag missing archive exclusion as a health issue. - Flag pages older than 90 days that cite volatile surfaces for re-validation; do not auto-archive them.
- Keep the note graph traversable, not just the index accurate.
- qmd is secondary. Structural checks (inventory, orphans, wikilinks, .obsidian placement, checkpoint filenames) remain Glob- and Grep-led. Use qmd (the protocol in
loam-using/references/discovery.md) only for content discovery: stale claims, contradictions, and related-note neighborhoods. - After wiki edits, refresh qmd if the collection is ready. If refresh fails, report it but do not roll back.
- If qmd is unavailable, unmapped, or degraded, continue without it. The skill must not fail.
- Goal lint is report-only. It runs when
goals/exists, even without a wiki. It does not alter goal files or append a wiki lint log entry. Route corrections through/loam::setting-goals.