---
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//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`:
` | kblinter | lint | meta/lint-report-YYYY-MM-DD.md | issues | `
## 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