Skip to main content
skill CX Codex

wiki-lint · ar9av — Claude Code skill: what it does, how to install

Audits Obsidian vault health by identifying broken wikilinks, orphan pages, missing frontmatter, and stale content without reading every file in full.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add Ar9av/obsidian-wiki --skill wiki-lint -a claude-code
Open on GitHub

What wiki-lint does

The skill performs a structural health check on an Obsidian vault in a report-only format. It detects pages with zero incoming wikilinks, flags broken targets while accounting for section anchors and aliases, and inspects frontmatter fields like title, tags, sources, and updated. Folders such as _archives/, _raw/, _readouts/, and .obsidian/ are skipped automatically.

The process avoids bulk file reads by relying on frontmatter-scoped greps and targeted section lookups. When the obsidian-wiki executable is present on PATH, it runs obsidian-wiki lint "$OBSIDIAN_VAULT_PATH" --json with schema rules defined in $OBSIDIAN_VAULT_PATH/AGENTS.md. If the CLI is absent, it executes deterministic grep routines instead.

It requires the OBSIDIAN_VAULT_PATH environment variable or a local .env file to locate the vault. Any empty or whitespace-only configuration values cause the run to fail closed immediately rather than falling back to defaults.

What it helps with

  • identify orphan pages lacking incoming wikilinks
  • detect broken wikilinks to missing markdown files
  • flag notes missing required frontmatter attributes
  • spot stale content by checking source modification timestamps

Example prompts for the agent

  • Run a wiki health check using wiki-lint
  • Scan the Obsidian vault for broken wikilinks and orphan notes

How to install wiki-lint

1Skills CLI (Vercel)

npx skills add Ar9av/obsidian-wiki --skill wiki-lint -a claude-code

2Manually: put the files in place

~/.claude/skills/wiki-lint/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/wiki-lint/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about wiki-lint

How to install wiki-lint in Claude Code?

Run npx skills add Ar9av/obsidian-wiki --skill wiki-lint -a claude-code.

What does wiki-lint do?

It audits an Obsidian vault for broken links, orphan files, schema inconsistencies in frontmatter, and stale pages, outputting a diagnostic report.

Is the obsidian-wiki CLI required to run checks?

No, the CLI binary is optional. If obsidian-wiki is not on your PATH, the skill falls back to grep-based inspections across the vault.

Source text

SKILL.md · MIT
---
name: wiki-lint
description: >
  Audit wiki health: broken links, orphans, contradictions, stale content, lifecycle and tag issues. Use for maintenance or health checks. Default is report-only; use wiki-dedup for page-identity merges.
---

# Wiki Lint — Health Audit

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.

## Before You Start

**Writing profile:** Before drafting or rewriting natural-language Markdown, read and apply the `Writing Profile Resolution` section in `llm-wiki/SKILL.md`. Framework schema, provenance, safety, and operation-specific requirements take precedence.
Apply `WRITING.md` preferences only to generated consolidation reports; deterministic findings and fixes keep their existing formats.

1. **Resolve config** — follow the Config Resolution Protocol in `llm-wiki/SKILL.md` (inline `@name` override → walk up CWD for `.env` → global config → prompt setup). This gives `OBSIDIAN_VAULT_PATH` plus any `OBSIDIAN_ALLOWED_LIFECYCLES`, `OBSIDIAN_ALLOWED_RELATIONSHIP_TYPES`, `OBSIDIAN_REQUIRED_TRUST_FIELDS`, and `OBSIDIAN_SCHEMA_SOURCE` values.
2. **Read owner rules** — if `$OBSIDIAN_VAULT_PATH/AGENTS.md` exists, read it before interpreting any schema. Owner rules override framework defaults.
3. **Form the effective schema** — record the schema source locator plus effective required/optional frontmatter, lifecycle values, relationship types, and provenance markers. Framework values are defaults; preserve owner extensions and relaxed requiredness exactly. Never coerce an owner type to a framework type.
4. Read `index.md` for the full page inventory
5. Read `log.md` for recent activity context

Pass the effective schema to deterministic checks explicitly. For example, add each owner extension with `--allow-lifecycle` / `--allow-relationship-type`, replace trust requiredness with repeatable `--required-trust-field`, and identify the authority with `--schema-source "$OBSIDIAN_VAULT_PATH/AGENTS.md"`. The JSON report's `schema` block must match the schema you formed before findings are accepted.

Schema precedence is CLI flags > resolved environment/config values > framework defaults; lifecycle and relationship extensions remain additive. Strip every override before use. An explicitly configured empty or whitespace-only value—and any empty comma-separated list entry—fails closed; never treat it as a valid lifecycle, relationship type, required field, or authority locator. Remove the variable instead when defaults are intended.

When `obsidian-wiki` is on PATH, prefer `obsidian-wiki lint "$OBSIDIAN_VAULT_PATH" --json` (with the same schema override flags as above) and fold the JSON `findings` into the health report. If the CLI is unavailable, keep the grep-based checks below — do not require the binary.

If the JSON report includes `findings.snapshot_mismatch`, list those pages in the report. Missing or drifted `snapshots:` is **not** missing required frontmatter (section 3 still covers only title, category, tags, sources, created, updated). Do not rewrite `sources:` to fix snapshot drift. Run `obsidian-wiki snapshots apply --from-json <lint.json>` (dry-run), show the preview, and pass `--apply` only after explicit user confirmation. Apply writes quoted `"[[_raw/_archived/stem|stem]]"` list items (native Property links with display text). `wiki-lint --consolidate` still does not apply this finding.

## Lint Checks

Run these checks in order. Report findings as you go.

**Scope:** skip `_archives/`, `_raw/`, `_readouts/`, and `.obsidian/` in every check. These hold frozen snapshots, unprocessed staging drafts, and derived readouts (saved by `wiki-narrate`) — they are not knowledge-graph pages, so orphan, frontmatter, and link checks don't apply to them.

### 1. Orphaned Pages

Find pages with zero incoming wikilinks. These are knowledge islands that nothing connects to.

**How to check:**
- Glob all `.md` files in the vault
- For each page, Grep the rest of the vault for `[[page-name]]` references
- Pages with zero incoming links (except `index.md` and `log.md`) are orphans

**How to fix:**
- Identify which existing pages should link to the orphan
- Add wikilinks in appropriate sections

### 2. Broken Wikilinks

Find `[[wikilinks]]` that point to pages that don't exist.

**How to check:**
- Grep for `\[\[.*?\]\]` across all pages
- Extract the link target: drop everything from the first `|` (alias) or `#` (heading/block anchor), and strip a trailing backslash left by an escaped `\|` inside a table cell
- Skip a target whose extension is an attachment type (`.png`, `.jpg`, `.gif`, `.svg`, `.webp`, `.pdf`, `.canvas`, `.base`, audio and video): it's an embed, not a page link, and has no entry in the `.md` inventory this check compares against
- Do **not** treat every dot as an extension — `[[Node.js]]`, `[[Next.js]]`, and `[[v1.2 release notes]]` are page links whose names happen to contain a dot, and dropping them would both miss real broken links and make the target page look like an orphan
- Strip an explicit `.md` suffix from what remains, then check if a corresponding `.md` file exists

**How to fix:**
- If the target was renamed, update the link
- If the target should exist, create it
- If the link is wrong, remove or correct it

### 3. Missing Frontmatter

Every page should have: title, category, tags, sources, created, updated.

**How to check:**
- Grep frontmatter blocks (scope to `^---` at file heads) instead of reading every page in full
- Flag pages missing required fields

**How to fix:**
- Add missing fields with reasonable defaults

### 3a. Missing Summary (soft warning)

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:**
- Grep frontmatter for `^summary:` across the vault
- Flag pages without it, **but as a soft warning, not an error** — older pages predating this field are fine; the check exists to nudge ingest skills into filling it on new writes.
- Also flag pages whose summary exceeds 200 chars.

**How to fix:**
- Re-ingest the page, or manually write a short summary (1–2 sentences of the page's content).

### 4. Stale Content

Pages whose `updated` timestamp is old relative to their sources.

**How to check:**
- Compare page `updated` timestamps to source file modification times
- Flag pages where sources have been modified after the page was last updated

**Undated claims (line-level).** `obsidian-wiki lint` reports `undated_claims`: lines stating a count or amount that goes stale (stars, users, customers, downloads, deals, MRR/ARR) with no date on the line. Lines under a dated heading, pages in `journal/` or named `YYYY-MM-…`, code, and blockquotes are exempt. A warning, never a failure. For each one, either re-check the number and stamp it `(as of YYYY-MM-DD)`, replace it with a link to where the live number lives, or move it under a dated heading as history.

### 5. Contradictions

Claims that conflict across pages.

**How to check:**
- This requires reading related pages and comparing claims
- Focus on pages that share tags or are heavily cross-referenced
- Look for phrases like "however", "in contrast", "despite" that may signal existing acknowledged contradictions vs. unacknowledged ones

**How to fix:**
- Add an "Open Questions" section noting the contradiction
- Reference both sources and their claims

### 6. Index Consistency

Verify `index.md` matches the actual page inventory.

**How to check:**
- Compare pages listed in `index.md` to actual files on disk
- Check that summaries in `index.md` still match page content

### 7. Provenance Drift

Check 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:**
- For each page with a `provenance:` block or any `^[inferred]`/`^[ambiguous]` markers, count sentences/bullets and how many end with each marker
- Compute rough fractions (`extracted`, `inferred`, `ambiguous`)
- Apply these thresholds:
  - **AMBIGUOUS > 15%**: flag as "speculation-heavy" — even 1-in-7 claims being genuinely uncertain is a signal the page needs tighter sourcing or should be moved to `synthesis/`
  - **INFERRED > 40% with no `sources:` in frontmatter**: flag as "unsourced synthesis" — the page is making connections but has nothing to cite
  - **Hub pages** (top 10 by incoming wikilink count) with INFERRED > 20%: flag as "high-traffic page with questionable provenance" — errors on hub pages propagate to every page that links to them
  - **Drift**: if the page has a `provenance:` frontmatter block, flag it when any field is more than 0.20 off from the recomputed value
- **Skip** pages with no `provenance:` frontmatter and no markers — treated as fully extracted by convention

**How to fix:**
- For ambiguous-heavy: re-ingest from sources, resolve the uncertain claims, or split speculative content into a `synthesis/` page
- For unsourced synthesis: add `sources:` to frontmatter or clearly label the page as synthesis
- For hub pages with INFERRED > 20%: prioritize for re-ingestion — errors here have the widest blast radius
- For drift: update the `provenance:` frontmatter to match the recomputed values

### 8. Fragmented Tag Clusters

Checks 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:**
- For each tag that appears on ≥ 5 pages:
  - `n` = count of pages with this tag
  - `actual_links` = count of wikilinks between any two pages in this tag group (check both directions)
  - `cohesion = actual_links / (n × (n−1) / 2)`
- Flag any tag group where cohesion < 0.15 and n ≥ 5

**How to fix:**
- Run the `cross-linker` skill targeted at the fragmented tag — it will surface and insert the missing links
- If a tag group is large (n > 15) and still fragmented, consider splitting it into more specific sub-tags

### 9. Visibility Tag Consistency

Checks that `visibility/` tags are applied correctly and aren't silently missing where they matter.

**How to check:**

- **Untagged PII patterns:** Grep page bodies for patterns that commonly indicate sensitive data — lines containing `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` without `sources:`:** A page tagged `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 in taxonomy:** `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:**
- For untagged PII patterns: add `visibility/pii` (or `visibility/internal` if it's team-context rather than personal data) to the page's frontmatter tags
- For missing `sources:`: add provenance or escalate to the user — don't auto-fill
- For taxonomy contamination: remove the `visibility/` entries from `_meta/taxonomy.md`

### 10. Misc Promotion Candidates

Find pages in `misc/` that have accumulated enough project affinity to be promoted.

**How to check:**
- Glob `$OBSIDIAN_VAULT_PATH/misc/*.md`
- For each page, read the `affinity` frontmatter field
- Flag pages where any single project's score ≥ 3

**How to fix:**
- Run the `cross-linker` skill first if affinity scores look stale (e.g., `affinity: {}` on a page with many wikilinks)
- To promote: move the page to `projects/<project-name>/references/` (or another appropriate category), update its `category` frontmatter, remove `promotion_status`, and grep the vault for backlinks to update them

### 12. Confidence and Lifecycle Schema

Enforces the confidence + lifecycle frontmatter schema (see `llm-wiki/SKILL.md`, Confidence and Lifecycle section).

Two modes:
- **`--check`** (default, read-only) — reports errors and warnings
- **`--consolidate`** — may apply separately approved structural maintenance, but **never rewrites `base_confidence`**

Confidence is a semantic judgment. A deterministic tool cannot infer independent evidence lineages or whole-page claim coverage from source strings alone. Confidence automation therefore validates an explicitly approved manual trust ledger; it never substitutes URL counting for review.

#### Rule 12a — `lifecycle` enum validation

**How to check:** Grep frontmatter for `^lifecycle:` across all pages. Flag any value outside the effective lifecycle set (framework default: `{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 present value outside `[0.0, 1.0]`; flag absence only when the effective owner schema requires the field.

**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:
- Stale pages with `lifecycle: verified` with a louder annotation (these are the most dangerous — high-trust pages that may be wrong)
- All other stale pages as a standard warning

**How to fix:** `--fix` does **not** rewrite `lifecycle`. Staleness clears automatically when a re-ingest bumps `updated`.

#### Rule 12c-2 — Illegal lifecycle transitions

The lifecycle enum is a state machine, not a free-form label. `obsidian-wiki lint` reports `illegal_lifecycle_transitions` by comparing each page's current `lifecycle` against the value recorded in `_meta/trust-ledger.json` at its last review.

**Flagged:** any state falling back to `draft` (only ingest sets `draft`), and any exit from `archived` (terminal — a restore is a deliberate human delete-and-recreate).

**Not flagged:** `draft → verified`. Ledger snapshots are sparse, so a legitimate intermediate `reviewed` may have happened between two reviews; flagging it would fire on valid history.

Warns by default; fails under `--strict-trust`. Pages whose ledger entry predates the `lifecycle` field have no baseline and are skipped silently.

**How to fix:** n/a — a page that moved along a forbidden edge means either a skill wrote `lifecycle` when it shouldn't have, or a human transition needs recording. Surface for human resolution.

#### Rule 12d — Supersession integrity

**How to check:** For each page with `superseded_by: "[[target]]"`:
- Verify the target page exists
- Verify the target page is not itself `archived` (no circular or chained supersession)
- Verify there are no cycles (A supersedes B which supersedes A)
- Warn if `lifecycle != archived` while `superseded_by` is set (inconsistent state)

**How to fix:** n/a — flag for human resolution

#### Rule 12e — Confidence review integrity

**How to check:** Run the deterministic ledger validator first:

```bash
obsidian-wiki trust-check "$OBSIDIAN_VAULT_PATH" --strict --json --pretty
```

Use `--strict` for CI and scheduled gates: stale, unreviewed, or missing-page
warnings then return nonzero. Without `--strict`, `trust-check` remains a
read-only reporting command and returns nonzero only for hard ledger errors or
score mismatches.

The approved ledger lives at `_meta/trust-ledger.json`. Each entry records the human-reviewed score plus a SHA-256 fingerprint of material page content and evidence metadata. The fingerprint excludes volatile bookkeeping (`updated`, `base_confidence`, and lifecycle transition fields), so timestamp-only edits do not reopen review.

Interpret results as follows:

- `reviewed` — current material fingerprint and stored score both match the approved review; do **not** recompute from source strings.
- `stale` — body, summary, sources, provenance, tags, or relationships changed; perform a new manual lineage + claim-coverage review.
- `unreviewed` — page has no approved ledger entry; manual review is required.
- `score_mismatches` — material content still matches, but stored `base_confidence` differs from the approved value; fail the lint.
- `errors` — malformed/missing ledger data; fail the lint.

For a separately approved full-vault review, record the accepted state explicitly:

```bash
obsidian-wiki trust-record "$OBSIDIAN_VAULT_PATH" \
  --all --reviewed-at "<ISO-8601 timestamp>" --approved --json --pretty
```

After a separately approved review of only specific stale/unreviewed pages, update only those entries:

```bash
obsidian-wiki trust-record "$OBSIDIAN_VAULT_PATH" \
  --page "concepts/example.md" --page "skills/example.md" \
  --reviewed-at "<ISO-8601 timestamp>" --approved --json --pretty
```

`--approved` means a human approved every score being recorded. It is a workflow
assertion, not a cryptographic signature: keep `_meta/trust-ledger.json` under
version control and require human diff review before merging ledger changes.
`--all` is valid only after a full-vault review; use repeatable `--page` for
partial reviews so unrelated stale pages remain open. Never run `trust-record`
merely to silence warnings.

**Manual recomputation protocol for stale/unreviewed pages:**

1. Decompose the page into material claims and map each claim to evidence.
2. Collapse dependent evidence into independent lineages: files/commits from one repository, retries in one task chain, snapshots plus their captured source, duplicate memories, and parent/child tasks each count once.
3. Assign reviewed quality per independent lineage using `llm-wiki` buckets.
4. Compute the raw base score, then assess whole-page claim coverage. The formula is a starting point, not an automatic target.
5. Classify the result as `raise`, `keep`, `lower`, or `repair first`; require approval before changing `base_confidence` or refreshing the ledger.

**How to fix:** There is no automatic confidence fix. Apply only an explicitly approved exact patch, verify its scope, then refresh only the reviewed ledger state. `--consolidate` must never rewrite `base_confidence`.

#### Current enforcement

Under framework defaults, every non-reserved content page must contain a
finite `base_confidence` in `[0.0, 1.0]` and a documented lifecycle value.
An owner schema may relax either field; present values remain validated.
Missing or malformed trust fields, malformed ledger data, and a missing required
ledger are hard errors. New pages with valid trust fields but no approved ledger
entry are `unreviewed`; material changes to approved pages are `stale`.

#### Output additions

Add to the Wiki Health Report:

```markdown
### 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` — confidence review stale: material fingerprint changed; manual lineage + coverage review required
- `entities/mismatch.md` — confidence mismatch: stored=0.80, approved=0.59
```

Append to the `LINT` log entry:
```
- [TIMESTAMP] LINT ... lifecycle_issues=N
```

### 13. Typed Relationships Validity

Validate `relationships:` frontmatter blocks. Skip pages that have no `relationships:` block — the field is optional.

**Framework-default types:** `extends`, `implements`, `contradicts`, `derived_from`, `uses`, `replaces`, `related_to`. Validate against the effective set after applying owner extensions.

**How to check:**
- Grep frontmatter for `^relationships:` across all vault pages
- For each page that has a `relationships:` block, read its frontmatter (not the full page body)
- For each entry in the block:
  1. **Type validation** — flag any `type:` value not in the allowed set above
  2. **Broken target** — strip `[[` 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.
     Before normalizing, drop everything from the first `|` (alias) or `#` (heading/block anchor): a pipe-aliased or heading-anchored target is not broken just because the literal bracket contents don't match a filename.
  3. **Self-reference** — flag any entry where the resolved target equals the page's own node id

**How to fix:**
- Invalid type: report the value and effective schema source. Correct it only if it is absent from both framework defaults and owner extensions; never replace a valid owner type with `related_to`.
- Broken target: update or remove the entry; if the target page should exist, create it first
- Self-reference: remove the entry

**Output additions:**

```markdown
### 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=N
```

### 14. Event-Time Validity

Validate `valid_from` / `valid_until` / `superseded_by` frontmatter. All three are optional — skip pages that carry none of them.

`created`/`updated` are ingestion time; these three are event time, when the claim itself was true. A page whose `valid_until` has passed is **historical**, not wrong: it stays in the vault and in the graph, and only drops out of default retrieval.

**How to check:**
- Grep frontmatter for `^valid_from:`, `^valid_until:`, `^superseded_by:` across all vault pages
- For each page that has any of them:
  1. **Date parseability** — each value must be `YYYY-MM-DD` or a full ISO 8601 timestamp. Flag anything else.
  2. **Window order** — flag any page where `valid_until` precedes `valid_from`
  3. **Dangling successor** — strip `[[`/`]]` from `superseded_by`, drop everything from the first `|` or `#`, strip `.md`, normalize, and check the page exists. Flag unresolved targets and self-references.

`obsidian-wiki lint` reports all three as `temporal_errors` (dates, windows) and `superseded_dangling` (successors). A bracketed dangling successor also shows up in `broken_links`.

**Why the dates fail rather than warn:** retrieval treats an unparseable window as current, so an unreported typo lets a stale claim answer as fact — the exact failure the fields exist to prevent.

**How to fix:**
- Unparseable date: rewrite as `YYYY-MM-DD`; if the real date is unknown, remove the field rather than guessing
- Inverted window: confirm which date is wrong with the page's sources; never silently swap them
- Dangling successor: create the replacement page, correct the pointer, or remove it if nothing replaced the claim
- Never "fix" a historical page by rewriting it as current — that destroys the record the window exists to keep

**Output additions:**

```markdown
### Event-Time Issues (N found)
- `references/gateway-nginx.md` — valid_until (2026-04-01) precedes valid_from (2026-05-01)
- `references/old-limits.md` — valid_until "soon" is not a date
- `references/legacy-auth.md` — superseded_by "[[oauth-rollout]]" resolves to no page in vault
```

Append to the `LINT` log entry:
```
... temporal_issues=N
```

### 11. Synthesis Gaps

Identify 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:**
- List all pages in `synthesis/` — collect the concept pairs each one already covers (from its `[[wikilinks]]` or title)
- Pick 10-15 frequently linked concepts from `concepts/` and `entities/`
- For each pair, run a quick grep to count pages that link to both:
  ```bash
  rg -l --glob '*.md' "\[\[ConceptA\]\]" "$OBSIDIAN_VAULT_PATH" > /tmp/a.txt
  rg -l --glob '*.md' "\[\[ConceptB\]\]" "$OBSIDIAN_VAULT_PATH" > /tmp/b.txt
  comm -12 <(sort /tmp/a.txt) <(sort /tmp/b.txt) | wc -l
  ```
- Flag pairs with co-occurrence ≥ 3 that have no existing synthesis page

**How to fix:**
- Run `/wiki-synthesize` to automatically discover and fill the top gaps

## Output Format

Report findings as a structured list:

```markdown
## 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
- `projects/foo.md:28` — undated claim: "GitHub: 6★" (stamp it, link it, or date the heading)

### 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` |
```

## After Linting

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=R
```

Offer to fix issues automatically or let the user decide which to address.

---

## Consolidate Mode (`--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.

### Safety protocol

**Always run in dry-run first.** Before writing anything:

1. Run all 12 lint checks (Step 1–12 above).
2. Print the planned consolidation actions as a structured list (see Dry-Run Output below).
3. Ask the user: `"Apply these N changes? [yes / no / select]"`.
4. Only proceed with writes after explicit confirmation. If the user selects individual actions, apply only those.
5. Never merge pages — use `wiki-dedup` for that. Only link, promote, demote, and flag. Never apply `snapshot_mismatch` here; that stays on the snapshots CLI with its own confirmation (see Before You Start).

### Consolidation actions (in order, after confirmation)

**Pre-write snapshot** — before the first file write, check whether the vault itself is the root of a Git repository. Merely being a subdirectory of a larger repository does not qualify: running `git add -A` there could capture unrelated files. If the vault is not a standalone Git repository, skip this step silently — no nagging, no suggesting `git init`.

```bash
VAULT_REAL_PATH=$(cd "$OBSIDIAN_VAULT_PATH" && pwd -P)
VAULT_GIT_ROOT=$(git -C "$OBSIDIAN_VAULT_PATH" rev-parse --show-toplevel 2>/dev/null || true)
SNAPSHOT_SHA=""

if [ -n "$VAULT_GIT_ROOT" ] && [ "$VAULT_GIT_ROOT" = "$VAULT_REAL_PATH" ]; then
  if git -C "$OBSIDIAN_VAULT_PATH" diff --quiet \
    && git -C "$OBSIDIAN_VAULT_PATH" diff --cached --quiet \
    && [ -z "$(git -C "$OBSIDIAN_VAULT_PATH" ls-files --others --exclude-standard)" ]; then
    SNAPSHOT_SHA=$(git -C "$OBSIDIAN_VAULT_PATH" rev-parse HEAD)
  else
    if ! git -C "$OBSIDIAN_...

© Ar9av · MIT · Ar9av/obsidian-wiki

Similar skills

Everything for Notes, tasks and projects →

wiki-lint by other authors comes first.

From the same set38

llm-wiki The schema side of a personal Obsidian wiki: source layers, categories, project trees and a page template. Use it when you're deciding how knowledge should be stored. CX Codex +1 4,100installs wiki-query Searches and synthesizes answers from a compiled Obsidian vault using graph indexing without modifying existing notes. CX Codex +1 3,600installs wiki-setup Builds the structure and config for an Obsidian wiki vault, and repairs a broken one. Useful on first run or after moving things around. CX Codex +1 3,600installs wiki-ingest Pulls documents, folders, pasted text and URLs into an Obsidian wiki, turning them into linked pages and skipping sources that have not changed. CX Codex +1 3,600installs wiki-update Distills architecture decisions and project context from your current repository into an Obsidian vault. Useful when you need durable notes without manual copy-pasting. CX Codex +1 3,400installs wiki-status Reports what an Obsidian wiki has ingested, what changed since the last run, and which pages went stale. Run it before deciding whether to append or rebuild the vault. CX Codex +1 3,400installs cross-linker Finds wiki pages that ought to reference each other but don't, then inserts the missing [[wikilinks]]. Useful right after a bulk import of notes. CX Codex +1 3,300installs tag-taxonomy This skill audits and normalizes tags across an Obsidian vault using a controlled vocabulary. It spots aliases, cleans up excess tags, and suggests canonical labels for new notes. CX Codex +1 3,200installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor