wiki-lint — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited wiki-lint (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are performing a health check on an Obsidian wiki. Your goal is to find and fix structural issues that degrade the wiki's value over time.
Before scanning anything: follow the Retrieval Primitives table in llm-wiki/SKILL.md. Prefer frontmatter-scoped greps and section-anchored reads over full-page reads. On a large vault, blindly reading every page to lint it is exactly what this framework is built to avoid.
llm-wiki/SKILL.md (walk up CWD for .env → ~/.obsidian-wiki/config → prompt setup). This gives OBSIDIAN_VAULT_PATHindex.md for the full page inventorylog.md for recent activity contextRun these checks in order. Report findings as you go.
Find pages with zero incoming wikilinks. These are knowledge islands that nothing connects to.
How to check:
.md files in the vault[[page-name]] referencesindex.md and log.md) are orphansHow to fix:
Find [[wikilinks]] that point to pages that don't exist.
How to check:
\[\[.*?\]\] across all pages.md file existsHow to fix:
Every page should have: title, category, tags, sources, created, updated.
How to check:
^--- at file heads) instead of reading every page in fullHow to fix:
Every page should have a summary: frontmatter field — 1–2 sentences, ≤200 chars. This is what cheap retrieval (e.g. wiki-query's index-only mode) reads to avoid opening page bodies.
How to check:
^summary: across the vaultHow to fix:
Pages whose updated timestamp is old relative to their sources.
How to check:
updated timestamps to source file modification timesClaims that conflict across pages.
How to check:
How to fix:
Verify index.md matches the actual page inventory.
How to check:
index.md to actual files on diskindex.md still match page contentCheck whether pages are being honest about how much of their content is inferred vs extracted. See the Provenance Markers section in llm-wiki for the convention.
How to check:
provenance: block or any ^[inferred]/^[ambiguous] markers, count sentences/bullets and how many end with each markerextracted, inferred, ambiguous)synthesis/provenance: frontmatter block, flag it when any field is more than 0.20 off from the recomputed valueprovenance: frontmatter and no markers — treated as fully extracted by conventionHow to fix:
synthesis/ pagesources: to frontmatter or clearly label the page as synthesisprovenance: frontmatter to match the recomputed valuesChecks whether pages that share a tag are actually linked to each other. Tags imply a topic cluster; if those pages don't reference each other, the cluster is fragmented — knowledge islands that should be woven together.
How to check:
n = count of pages with this tagactual_links = count of wikilinks between any two pages in this tag group (check both directions)cohesion = actual_links / (n × (n−1) / 2)How to fix:
cross-linker skill targeted at the fragmented tag — it will surface and insert the missing linksChecks that visibility/ tags are applied correctly and aren't silently missing where they matter.
How to check:
password, api_key, secret, token, ssn, email:, phone: followed by an actual value (not a field description). If a page matches and lacks visibility/pii or visibility/internal, flag it as a likely mis-classification.visibility/pii should always have a sources: frontmatter field — if there's no provenance, there's no way to verify the classification. Flag any visibility/pii page missing sources:.visibility/ tags are system tags and must not appear in _meta/taxonomy.md. If found there, flag as misconfigured — they'd be counted toward the 5-tag limit on pages that include them.How to fix:
visibility/pii (or visibility/internal if it's team-context rather than personal data) to the page's frontmatter tagssources:: add provenance or escalate to the user — don't auto-fillvisibility/ entries from _meta/taxonomy.mdFind pages in misc/ that have accumulated enough project affinity to be promoted.
How to check:
$OBSIDIAN_VAULT_PATH/misc/*.mdaffinity frontmatter fieldHow to fix:
cross-linker skill first if affinity scores look stale (e.g., affinity: {} on a page with many wikilinks)projects/<project-name>/references/ (or another appropriate category), update its category frontmatter, remove promotion_status, and grep the vault for backlinks to update themEnforces the confidence + lifecycle frontmatter schema (see llm-wiki/SKILL.md, Confidence and Lifecycle section).
Two modes:
base_confidence only when drift is detected (Rule 12e); never rewrites lifecycle#### Rule 12a — lifecycle enum validation
How to check: Grep frontmatter for ^lifecycle: across all pages. Flag any value not in {draft, reviewed, verified, disputed, archived}.
How to fix: n/a (only a human should set lifecycle state)
#### Rule 12b — base_confidence range
How to check: Grep frontmatter for ^base_confidence: across all pages. Flag any value outside [0.0, 1.0] or any page missing the field entirely.
How to fix: n/a (wrong value means the skill computed it wrong — surface for manual correction)
#### Rule 12c — Stale page report (computed overlay)
Staleness is never stored — it is computed at read time: is_stale = (today − updated) > 90 days.
How to check: For each page, read updated: from frontmatter and compute is_stale. If stale, also check lifecycle:. Report:
lifecycle: verified with a louder annotation (these are the most dangerous — high-trust pages that may be wrong)How to fix: --fix does not rewrite lifecycle. Staleness clears automatically when a re-ingest bumps updated.
#### Rule 12d — Supersession integrity
How to check: For each page with superseded_by: "[[target]]":
archived (no circular or chained supersession)lifecycle != archived while superseded_by is set (inconsistent state)How to fix: n/a — flag for human resolution
#### Rule 12e — Confidence drift
How to check: For pages that have both base_confidence: and sources: in frontmatter, recompute base_confidence using the formula in llm-wiki/SKILL.md. If the stored value differs from the recomputed value by more than 0.05, flag it as drift.
How to fix (`--fix` only): Rewrite the base_confidence field to the recomputed value. This is the only rule that mutates frontmatter automatically.
#### Migration timeline
| Phase | When | Behavior on missing fields |
|---|---|---|
| Phase 1: Soft launch | Initial PR | Warning only — missing base_confidence or lifecycle on any page |
| Phase 2: New pages enforced | +2 weeks | Error for newly created pages missing the fields; existing pages still warn even if updated is bumped during routine maintenance |
| Phase 3: Full enforcement | +6 weeks, gated on a backfill script shipping in a separate PR | Error for all pages |
#### Output additions
Add to the Wiki Health Report:
### Confidence/Lifecycle Issues (N found)
- `concepts/foo.md` — missing `lifecycle` field (warning: Phase 1)
- `entities/bar.md` — `lifecycle: stalestate` is not a valid enum value
- `concepts/scaling.md` — `base_confidence: 1.4` is out of range [0.0, 1.0]
- `synthesis/old-analysis.md` — STALE (last updated 2025-10-01, 182 days ago) lifecycle=verified ⚠️ HIGH PRIORITY
- `concepts/outdated.md` — STALE (last updated 2025-11-15, 137 days ago) lifecycle=draft
- `entities/tool-v1.md` — `superseded_by: [[entities/tool-v2]]` but lifecycle=draft (expected archived)
- `concepts/drift-example.md` — base_confidence drift: stored=0.80, recomputed=0.59 (delta=0.21)Append to the LINT log entry:
- [TIMESTAMP] LINT ... lifecycle_issues=NValidate relationships: frontmatter blocks. Skip pages that have no relationships: block — the field is optional.
Allowed types: extends, implements, contradicts, derived_from, uses, replaces, related_to
How to check:
^relationships: across all vault pagesrelationships: block, read its frontmatter (not the full page body)type: value not in the allowed set above[[ and ]] from the target: string, normalize (lowercase, spaces→hyphens, strip .md), and check whether a .md file at that path exists in the vault. Flag unresolved targets.How to fix:
related_to when the type is ambiguousOutput additions:
### Typed Relationship Issues (N found)
- `concepts/foo.md` — relationships[1]: type "contradication" is not an allowed type (did you mean "contradicts"?)
- `concepts/bar.md` — relationships[0]: target "[[skills/nonexistent-skill]]" resolves to no page in vault
- `entities/baz.md` — relationships[2]: self-reference (target resolves to this page's own id)Append to the LINT log entry:
... relationship_issues=NIdentify high-value synthesis opportunities the wiki is missing — concept pairs that co-occur across many pages but have no synthesis/ page connecting them.
How to check:
synthesis/ — collect the concept pairs each one already covers (from its [[wikilinks]] or title)concepts/ and entities/ grep -rl "\[\[ConceptA\]\]" "$OBSIDIAN_VAULT_PATH" --include="*.md" > /tmp/a.txt
grep -rl "\[\[ConceptB\]\]" "$OBSIDIAN_VAULT_PATH" --include="*.md" > /tmp/b.txt
comm -12 <(sort /tmp/a.txt) <(sort /tmp/b.txt) | wc -lHow to fix:
/wiki-synthesize to automatically discover and fill the top gapsReport findings as a structured list:
## Wiki Health Report
### Orphaned Pages (N found)
- `concepts/foo.md` — no incoming links
### Broken Wikilinks (N found)
- `entities/bar.md:15` — links to [[nonexistent-page]]
### Missing Frontmatter (N found)
- `skills/baz.md` — missing: tags, sources
### Stale Content (N found)
- `references/paper-x.md` — source modified 2024-03-10, page last updated 2024-01-05
### Contradictions (N found)
- `concepts/scaling.md` claims "X" but `synthesis/efficiency.md` claims "not X"
### Index Issues (N found)
- `concepts/new-page.md` exists on disk but not in index.md
### Missing Summary (N found — soft)
- `concepts/foo.md` — no `summary:` field
- `entities/bar.md` — summary exceeds 200 chars
### Provenance Issues (N found)
- `concepts/scaling.md` — AMBIGUOUS > 15%: 22% of claims are ambiguous (re-source or move to synthesis/)
- `entities/some-tool.md` — drift: frontmatter says inferred=0.10, recomputed=0.45
- `concepts/transformers.md` — hub page (31 incoming links) with INFERRED=28%: errors here propagate widely
- `synthesis/speculation.md` — unsourced synthesis: no `sources:` field, 55% inferred
### Fragmented Tag Clusters (N found)
- **#systems** — 7 pages, cohesion=0.06 ⚠️ — run cross-linker on this tag
- **#databases** — 5 pages, cohesion=0.10 ⚠️
### Visibility Issues (N found)
- `entities/user-records.md` — contains `email:` value pattern but no `visibility/pii` tag
- `concepts/auth-flow.md` — tagged `visibility/pii` but missing `sources:` frontmatter
- `_meta/taxonomy.md` — contains `visibility/internal` entry (system tag must not be in taxonomy)
### Misc Promotion Candidates (N found)
Pages in misc/ that have ≥ 3 connections to a single project and are ready to be promoted:
| Page | Top Project | Affinity Score |
|---|---|---|
| `misc/web-martinfowler-articles-microservices.md` | `obsidian-wiki` | 4 |
### Typed Relationship Issues (N found)
- `concepts/foo.md` — relationships[1]: type "contradication" is not an allowed type
- `concepts/bar.md` — relationships[0]: target "[[skills/nonexistent]]" resolves to no page
### Synthesis Gaps (N found)
Concept pairs that co-occur frequently but have no synthesis page:
| Pair | Co-occurrence | Suggested Action |
|---|---|---|
| [[Caching]] × [[Consistency]] | 5 pages | Run `/wiki-synthesize` |
| [[Testing]] × [[Observability]] | 3 pages | Run `/wiki-synthesize` |Append to log.md:
- [TIMESTAMP] LINT issues_found=N orphans=X broken_links=Y stale=Z contradictions=W prov_issues=P missing_summary=S fragmented_clusters=F visibility_issues=V promotion_candidates=C synthesis_gaps=G relationship_issues=ROffer to fix issues automatically or let the user decide which to address.
--consolidate)Triggered by wiki-lint --consolidate. Switches from report-only to act-and-report — the "dream cycle" that runs periodically so the wiki self-heals.
Always run in dry-run first. Before writing anything:
"Apply these N changes? [yes / no / select]".wiki-dedup for that. Only link, promote, demote, and flag.#### Action 1: Fix broken wikilinks
For each broken [[Target]] found in Check 2:
Grep across index.md titles)[[Oringal]] → [[corrected-page]].~~[[Target]]~~ → Target) and add a comment <!-- broken link: no match found -->.#### Action 2: Add missing cross-references for orphans
For each orphan page found in Check 1 (zero incoming links):
[[wikilink]] replacing the plain-text mention.cross-linker which runs broadly).#### Action 3: Correct lifecycle states
Apply these rules automatically (they don't require human judgment — they enforce the documented state machine):
lifecycle: draft AND created > 30 days ago AND base_confidence > 0.7. Set lifecycle: reviewed, lifecycle_changed: <today>, lifecycle_reason: "auto-promoted by wiki-lint --consolidate: age>30d, confidence>0.7".stale is a computed overlay, not a lifecycle value. Instead: for verified pages where is_stale = (today − updated) > 180 days, add a callout at the top of the page body: > ⚠️ **Stale**: This page was last updated <date>. Verify before relying on it. Only add if the callout isn't already present.#### Action 4: Tier demotion
For pages with tier: supporting (or unset) that have 0 incoming links AND haven't been updated in 90+ days:
tier: peripheral.tier: core pages automatically — those were manually set.#### Action 5: Tag normalization
Read _meta/taxonomy.md for the alias mapping (e.g., ml → machine-learning). For each page, replace known alias tags with their canonical form in the tags: frontmatter field. This is a subset of tag-taxonomy's work — only alias fixes, no full audit.
#### Action 6: Contradiction callouts
For each pair of pages marked as contradicting each other (via relationships: contradicts in frontmatter, or flagged in Check 5):
> ⚠️ Contradiction flagged with [[Other Page]] callout already exists near the relevant claim.After all actions, write a report to synthesis/consolidation-<YYYY-MM-DD>.md:
---
title: Consolidation Report <YYYY-MM-DD>
category: synthesis
tags: [maintenance, consolidation]
sources: []
summary: Auto-generated consolidation report from wiki-lint --consolidate run on <date>.
lifecycle: draft
lifecycle_changed: <date>
tier: peripheral
created: <ISO timestamp>
updated: <ISO timestamp>
---
# Consolidation Report — <YYYY-MM-DD>
## Summary
- Broken links fixed: N
- Cross-references added: M
- Lifecycle states updated: K
- Tier demotions: D
- Tags normalized: T
- Contradiction callouts added: C
## Broken Link Fixes
- `concepts/foo.md:12` — [[OldTarget]] → [[correct-target]]
- `entities/bar.md:8` — [[Missing]] → `Missing` (no match found)
## Cross-References Added (orphan rescue)
- `concepts/baz.md` — now linked from: [[concepts/alpha]], [[skills/beta]]
## Lifecycle Updates
- `concepts/old-draft.md` — draft → reviewed (age 45d, confidence 0.74)
- `synthesis/stale-verified.md` — stale callout added (last updated 2025-10-01)
## Tier Demotions
- `concepts/unused-concept.md` — supporting → peripheral (0 links, 120 days stale)
## Tag Normalizations
- `entities/some-tool.md` — `ml` → `machine-learning`
## Contradiction Callouts
- `concepts/scaling.md` — flagged contradiction with [[synthesis/efficiency]]wiki-lint --consolidate — Dry Run
Planned actions (N total):
[1] Fix broken link: concepts/foo.md:12 [[OldTarget]] → [[correct-target]]
[2] Add cross-ref: concepts/baz.md ← [[concepts/alpha]] (orphan rescue)
[3] Lifecycle: concepts/old-draft.md → reviewed (age 45d, confidence 0.74)
[4] Tier demotion: concepts/unused.md → peripheral (0 links, 112 days stale)
[5] Tag alias: entities/some-tool.md: ml → machine-learning
[6] Contradiction callout: concepts/scaling.md ↔ [[synthesis/efficiency]]
Apply these 6 changes? [yes / no / select by number]- [TIMESTAMP] LINT_CONSOLIDATE links_fixed=N orphans_rescued=M lifecycle_updates=K tier_demotions=D tag_fixes=T contradiction_callouts=C report=synthesis/consolidation-YYYY-MM-DD.mdQMD is a search index, not the source of truth. If $QMD_WIKI_COLLECTION is empty or unset, skip this step. Run it only after this skill has written or rewritten vault markdown. If QMD refresh fails, do not roll back the vault changes; report the QMD status separately.
Use $QMD_CLI if set; otherwise use qmd.
${QMD_CLI:-qmd} updateIf the output says vectors are needed or embeddings may be stale, run:
${QMD_CLI:-qmd} embedVerify the collection with either:
${QMD_CLI:-qmd} ls "$QMD_WIKI_COLLECTION"or, when a specific page path is known:
${QMD_CLI:-qmd} get "qmd://$QMD_WIKI_COLLECTION/<page>.md" -l 5Record one of:
QMD refreshed: update + embed + verifiedQMD refreshed: update only + verifiedQMD skipped: QMD_WIKI_COLLECTION unsetQMD skipped: qmd CLI unavailableQMD failed: <short error summary>~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.