From brain-os
Runs nightly Obsidian vault maintenance: detects broken wiki-links and orphan pages, syncs directory indexes, flags stale GitHub issues. Use for automated knowledge base linting.
npx claudepluginhub sonthanh/brain-os-pluginThis skill uses the workspace's default tool permissions.
Pipeline skill. Runs nightly (2am CEST, sonnet) via `/brain-os:schedule`. Auto from day 1 per `working-rules.md § 2` — no review phase for cron/maintenance skills.
Performs extended vault cleanup: full audit plus stale content scan, outdated references, content quality review, redundant tags, broken external links, and template compliance. Ideal for messy Markdown vaults.
Runs vault health diagnostics in 8 categories including schema compliance, orphans, links, three-space boundaries, stale notes, MOC coherence. Quick/full/three-space modes yield FAIL/WARN/PASS reports with fixes.
Lints Obsidian wiki vaults for orphan pages, dead wikilinks, stale claims, missing cross-references, frontmatter gaps, empty sections. Generates lint reports, Dataview dashboards, and canvas maps.
Share bugs, ideas, or general feedback.
Pipeline skill. Runs nightly (2am CEST, sonnet) via /brain-os:schedule. Auto from day 1 per working-rules.md § 2 — no review phase for cron/maintenance skills.
Vault path + GitHub task repo: read from ${CLAUDE_PLUGIN_ROOT}/brain-os.config.md (or user-local ~/.brain-os/brain-os.config.md). Keys: vault_path:, gh_task_repo:. Substitute $GH_TASK_REPO below with the configured value.
{vault}/daily/organize-reports/YYYY-MM-DD.md{vault}/daily/skill-outcomes/vault-lint.log{vault}/thinking/references/bidirectional-linking.md, {vault}/thinking/references/compiled-truth-template.md| Flag | Effect |
|---|---|
--dry-run | Skip ALL mutations (no git tag, no file writes, no archiving). Produce report of what would change only. |
| (no flags) | Full run — tag, lint, fix, report. |
Skip entirely if --dry-run.
cd {vault}
git tag pre-vault-lint-$(date +%Y-%m-%d) HEAD
If the tag already exists (re-run same day), append -N suffix: pre-vault-lint-2026-04-13-2.
Scan all .md files in the vault for [[...]] wiki-links. For each link:
[[@...]] Obsidian @-mention links — links whose target starts with @ are person-mention syntax, not file references; skip them..md extension optional){source_file}:{line} → [[{target}]]Do NOT auto-fix broken links. List them in the report under ## Broken Links.
Find .md files that are NOT referenced by any wiki-link from another file. Exclude:
README.md files (index pages)CLAUDE.md, RESOLVER.md, MEMORY.md (system files)daily/ (dated artifacts, expected to be unlinked)business/intelligence/emails/ (auto-generated dated intelligence artifacts, same exclusion rationale as daily/)Clippings/ (Obsidian web clipper artifacts, standalone by design)people/, companies/, meetings/ (auto-generated email entity records — database-style, orphaned by design; bidirectional link gaps for entity pages are handled by Phase A3, not orphan detection)private/ (gitignored zone)thinking/aha/, thinking/originals/ (standalone artifacts)working-rules.md, skill-spec.md, etc.)List orphans in the report under ## Orphan Pages.
Per {vault}/thinking/references/bidirectional-linking.md:
Scope: entity pages only — people/, companies/, meetings/, business/decisions/, business/projects/.
For each wiki-link from entity page A → entity page B, check that B also links back to A. Collect one-way links as: {A} → [[{B}]] (no back-link).
List violations in the report under ## One-Way Entity Links.
Run {vault}/scripts/update-readmes.sh to regenerate directory README files. If the script produces any git diff, note which directories were out of sync in the report.
Detect SSOT violations across all CLAUDE.md files in the ecosystem:
{vault}/CLAUDE.md~/.claude/CLAUDE.md~/work/## ...) from each fileList violations in the report under ## CLAUDE.md Drift. Include which file should be the SSOT owner (the one listed in ~/.claude/CLAUDE.md as canonical).
Tasks live as GitHub issues at $GH_TASK_REPO. This phase flags open issues that have not been touched in >90 days — the /status panel hides them silently, so they accumulate as silent backlog rot.
gh issue list -R $GH_TASK_REPO --state open --limit 100 \
--json number,title,updatedAt,labels,url
If $GH_TASK_REPO is unset, skip Phase B entirely and note in the report: Phase B skipped — gh_task_repo not configured.
For each issue where updatedAt is older than 90 days:
## Stale Issues as #N — [title] (updated YYYY-MM-DD, Nd ago) — url.--dry-run, post a comment:
gh issue comment N -R $GH_TASK_REPO --body "vault-lint: no activity for Nd. Review and either close, re-label \`status:backlog\`, or update the body to keep it alive."
Skip issues that already received a vault-lint: comment in the last 30 days (idempotency — query comments via gh issue view N --json comments).
Scan vault for references to external entities that may no longer exist:
Grep vault .md files for absolute paths matching ~/work/... or /Users/.../work/.... For each unique path, check if the directory exists. Flag missing paths.
Scan {vault}/daily/handovers/ for files older than 30 days. For each, query gh issue list -R $GH_TASK_REPO --state open --search "<handover-filename>" — flag as potentially stale if any open issue still references it.
Scan {vault}/daily/grill-sessions/ for files with no section heading matching ## Decision as a prefix (case-insensitive — matches ## Decisions, ## Decisions Made, ## Decisions (locked), ## Decision Summary, ## Settled Decisions, etc.) or # Decision as an H1 prefix (case-insensitive — matches # Decisions, # Decision Summary, etc.) or where the matching section is empty. Flag as incomplete.
Exclude files whose filename contains autogrill — these are auto-generated planning/design sessions where a formal ## Decisions section is not required by design.
Items where automated detection is uncertain go into ## Needs Review in the report. Do not auto-fix these — human judgment required.
Write to {vault}/daily/organize-reports/YYYY-MM-DD.md:
---
date: YYYY-MM-DD
skill: vault-lint
mode: {full | dry-run}
---
# Vault Lint Report — YYYY-MM-DD
## Summary
- Broken links: N
- Orphan pages: N
- One-way entity links: N
- Index out of sync: N directories
- CLAUDE.md drift: N violations
- Stale issues flagged: N
- Dead entities flagged: N
- Items needing review: N
## Broken Links
{list or "None found."}
## Orphan Pages
{list or "None found."}
## One-Way Entity Links
{list or "None found."}
## Index Sync
{list of out-of-sync directories or "All indexes up to date."}
## CLAUDE.md Drift
{list or "No drift detected."}
## Stale Issues
{list or "None."}
## Dead Entities
{list or "None."}
## Needs Review
{list or "Nothing uncertain."}
If --dry-run, prefix the title: # Vault Lint Report — YYYY-MM-DD (DRY RUN) and note that no changes were made.
Follow {vault}/skill-spec.md § 11. Append to {vault}/daily/skill-outcomes/vault-lint.log:
{date} | vault-lint | lint | ~/work/brain-os-plugin | daily/organize-reports/{date}.md | commit:{hash} | {result}
Result logic:
pass — all runnable phases completed cleanly, OR all skips were context-constrained (Phase A4: update-readmes.sh absent; Phase B: gh unauthenticated or gh_task_repo unset). Context-constrained skips are expected in MCP-mode execution — the report is fully correct for the phases that ran. "Needs Review" items are informational output (they're what the skill is supposed to surface for a living vault), not a failure signal.partial — a phase encountered an unexpected error and recovered gracefully (not a context-constrained skip — those log pass).fail — a phase errored and the skill aborted before writing its report.If result == fail, auto-invoke /brain-os:improve vault-lint. Phase errors are the real skill-improvement signal. Context-constrained skips (A4/Phase B unavailable) now log pass — they carry no actionable improvement signal. An unexpected recovered error (partial) is also low-signal by itself; only a hard abort (fail) reliably indicates a SKILL.md gap worth fixing. The eval gate inside /improve still prevents regressions if invoked.
Intentional divergence from
skill-spec.md § 11auto-improve convention. The cross-skill default isif result != pass. vault-lint tightens to== failbecause context-constrained skips (Phase A4: noupdate-readmes.sh; Phase B: noghauth /gh_task_repounset) now logpass, and unexpected recovered errors (partial) are still not reliable improvement signals. If you're reviewing this and considering a "fix back to the convention," readdaily/improve-reports/2026-04-23-vault-lint.mdfirst — the tightening was the explicit fix for 100% partial rate + wasted auto-improve cycles across 4 consecutive nightly runs. The evalauto-improve-on-fail-onlywill fail if the tightening is reverted.
Skip if --dry-run.
cd {vault}
git add daily/organize-reports/ daily/skill-outcomes/
git commit -m "vault-lint: nightly run {date}"
git pull --rebase && git push
[[foo]] matches any/nested/foo.md if there's only one foo.md.rm. The git tag enables rollback if anything goes wrong.people/, companies/, meetings/, business/decisions/, business/projects/. Don't enforce bidirectional links on grill notes, aha moments, or research reports.scripts/update-readmes.sh script must exist in the vault. If missing, skip A4 and note it in the report.gh CLI authenticated (gh auth status). If unauthenticated or gh_task_repo: unset, the phase skips silently with a report note — it does not fail the whole run.