--- name: "kblinter" description: "Run the knowledge-base linter against the Obsidian vault at D:\\Repos\\Obsidian and surface the findings. Use whenever the user asks to lint, audit, or check the health of the vault/Second Brain. Triggers: /kblinter, \"lint the vault\", \"audit my notes\", \"check vault health\", \"run the kb linter\", \"find orphan notes\", \"find broken links in the vault\"." --- # kblinter — Vault Knowledge Base Linter Wrapper around `D:\Repos\Obsidian\meta\scripts\lint.py`. The script implements the checks defined in `AGENTS.md` §5.3. ## Vault layers (drives which checks apply) | Layer | Lives in | Lint behavior | |---|---|---| | **Raw** | `Transcripts/`, `00 - Chats/`, `Attachments/`, `*.vtt`, `*.msg` | Skipped — never linted | | **Wiki** | `01 - Daily/`, `02 - Meetings/`, `03 - Squads/`, `04 - Connects/` (except Drafts), `05 - Resources/` | Full schema enforcement | | **Workshop** | `04 - Connects/Drafts/` (plus any path in `WORKSHOP_DIRS` in `lint.py`) | Exempt from frontmatter / orphan / broken-link / duplicate / meeting-provenance. Still checked for stale + sensitivity. | | **Schema** | `AGENTS.md`, `meta/`, `99 - Templates/`, any `Templates/` subfolder | Skipped from most content checks | ## What it checks | Check | Rule | Layers affected | |---|---|---| | YAML parse errors | Frontmatter block exists but YAML invalid | wiki | | Missing frontmatter | Wiki-layer page missing required keys for its `type` | wiki | | Orphan wiki pages | No inbound `[[wiki link]]` from any other note (excludes `type: daily` and `type: moc`) | wiki | | Stale pages | `status: active` updated >30d ago; `status: draft` updated >90d ago | wiki + workshop | | Broken wiki links | `[[...]]` that doesn't resolve | wiki | | Sensitivity mismatch | Body contains "Confidential" / customer markers without `sensitivity:` frontmatter | wiki + workshop | | Duplicate canonical keys | Multiple notes with same `(parent_dir, date, type, stem)` | wiki | | **Meeting provenance** | Wiki page under `03 - Squads/*/Meetings/` lacking a `sources:` or inline `[[link]]` to `02 - Meetings/` or `Transcripts/` | wiki | | Transcripts inbox backlog | Files in `Transcripts/` >7 days old | n/a | | MOC gaps | Squad subfolder with >10 wiki pages but no MOC hub note | n/a | **The linter NEVER auto-fixes.** It writes a Markdown report and exits. ## Canonical vs squad-side meetings The vault deliberately keeps meetings in two places — both are valid `type: meeting`: - **`02 - Meetings/YYYY-MM-DD - /`** = **canonical record** (raw VTT + auto summary). Chronological, one folder per meeting. - **`03 - Squads/<name>/Meetings/`** = **squad-lens commentary**. Organized by squad. MUST `sources:` link back to the canonical record (enforced by the `meeting_provenance` check). When fixing a `meeting_provenance` gap: add `sources: ["[[02 - Meetings/YYYY-MM-DD - .../summary]]"]` (or the relevant VTT path) to the squad-side note's frontmatter. The check also accepts inline `[[02 - Meetings/...]]` wikilinks in the body. ## How to run ### Default (write report) ```powershell python D:\Repos\Obsidian\meta\scripts\lint.py ``` Writes `D:\Repos\Obsidian\meta\lint-report-YYYY-MM-DD.md`. Prints a one-line summary. ### JSON ```powershell python D:\Repos\Obsidian\meta\scripts\lint.py --json ``` ### Custom output / alternate vault ```powershell python D:\Repos\Obsidian\meta\scripts\lint.py --out X --vault Y ``` ## Default workflow when invoked 1. Run the linter. Overwriting today's report is fine. 2. Read the report. 3. Summarize to the user: - Total + per-check counts - Top 5–10 most-actionable items (priority: broken links > sensitivity mismatch > meeting provenance > duplicates > frontmatter > orphans) - Inbox backlog items — concrete unblocked actions - MOC gaps — suggest a name 4. Offer to fix specific issues. NEVER fix without explicit approval. Common fixes: - Add missing frontmatter (consider `meta/scripts/frontmatter_backfill.py --dry-run` first) - Repair a broken link - Add sensitivity label - Add `sources:` to a squad-side meeting note (for `meeting_provenance` gaps) - Create a missing MOC hub note 5. Append a log line to `D:\Repos\Obsidian\meta\log.md`: `<ISO timestamp> | kblinter | lint | meta/lint-report-YYYY-MM-DD.md | <total> issues | <per-check breakdown>` ## Adding a new workshop path Edit `WORKSHOP_DIRS` at the top of `meta/scripts/lint.py`: ```python WORKSHOP_DIRS = { "04 - Connects/Drafts", "some/new/scratch/area", # add here } ``` Update AGENTS.md §1 and §2 to document the new workshop path. ## Don'ts - Don't auto-edit any wiki page based on lint findings without explicit approval. - Don't relax a rule because it found something — update AGENTS.md deliberately instead. - Don't summarize the entire report verbatim — pick the most actionable items. - Don't run on `D:\Repos\TradeTrail` or any other directory. Hard-coded for the Obsidian vault. ## When the script fails Surface stderr verbatim and suggest: - `python -c "import yaml"` (PyYAML required) - Check `D:\Repos\Obsidian\meta\scripts\lint.py` exists - `--json` for raw output ## Extending the linter New checks live in `lint.py` registered in the `CHECKS` tuple. Each check takes `list[Note]` and returns `list[dict]` with `path` and `msg` keys. When adding a check: 1. Decide which layers it applies to (filter by `n.layer` early) 2. Document in `AGENTS.md` §5.3 3. Update the "What it checks" table here 4. Add a smoke test