From arscontexta
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.
npx claudepluginhub agenticnotetaking/arscontexta --plugin arscontextaThis skill is limited to using the following tools:
Read these files to configure domain-specific behavior:
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 nightly Obsidian vault maintenance: detects broken wiki-links and orphan pages, syncs directory indexes, flags stale GitHub issues. Use for automated knowledge base linting.
Audits knowledge base for broken links, orphan notes, missing indexes, invalid frontmatter, and optional AI content staleness detection.
Share bugs, ideas, or general feedback.
Read these files to configure domain-specific behavior:
ops/derivation-manifest.md — vocabulary mapping, folder names, platform hints
vocabulary.notes for the notes folder namevocabulary.inbox for the inbox folder namevocabulary.note for the note type name in outputvocabulary.topic_map for MOC/topic map referencesvocabulary.topic_maps for plural formops/config.yaml — processing depth, thresholds
Three-space reference — ${CLAUDE_PLUGIN_ROOT}/reference/three-spaces.md for boundary rules (load only for full and three-space modes)
Templates — read template files to understand required schema fields for validation
If these files don't exist (pre-init invocation or standalone use), use universal defaults:
notes/inbox/Target: $ARGUMENTS
Parse the invocation mode immediately:
| Input | Mode | Categories Run |
|---|---|---|
empty or quick | Quick | 1 (Schema), 2 (Orphans), 3 (Links) |
full | Full | All 8 categories |
three-space | Three-Space | 5 (Three-Space Boundaries) only |
Execute these steps:
ops/health/YYYY-MM-DD-report.mdSTART NOW. Reference below explains each diagnostic category in detail.
Checks adapt to what the platform supports:
Report what CAN be checked, not what the platform lacks.
What it checks: Every {vocabulary.note} in {vocabulary.notes}/ and self/memory/ (if self/ is enabled) has valid YAML frontmatter with required fields.
How to check:
# Find all note files (exclude topic maps)
for f in {vocabulary.notes}/*.md; do
[[ -f "$f" ]] || continue
# Check: YAML frontmatter exists
head -1 "$f" | grep -q '^---$' || echo "FAIL: $f — no YAML frontmatter"
# Check: description field present
rg -q '^description:' "$f" || echo "WARN: $f — missing description field"
# Check: topics field present and links to at least one topic map
rg -q '^topics:' "$f" || echo "WARN: $f — missing topics field"
done
Additional checks:
_schema blocks if templates exist)description field is non-empty (not just present)topics field contains at least one wiki linkIf validate-kernel.sh exists in ${CLAUDE_PLUGIN_ROOT}/reference/, run it and include results.
Thresholds:
| Condition | Level |
|---|---|
| Any note missing YAML frontmatter | FAIL |
Any note missing description field | WARN |
Any note missing topics field | WARN |
| Any invalid enum value | WARN |
| All notes pass all checks | PASS |
Output format:
[1] Schema Compliance ............ WARN
2 notes missing description:
- notes/example-note.md
- notes/another-note.md
1 note missing topics:
- notes/orphaned-claim.md
12/15 notes fully compliant
What it checks: Every {vocabulary.note} has at least one incoming wiki link from another file.
How to check:
# For each note file, check if ANY other file links to it
for f in {vocabulary.notes}/*.md; do
[[ -f "$f" ]] || continue
basename=$(basename "$f" .md)
# Search for [[basename]] in all other files
count=$(rg -l "\[\[$basename\]\]" --glob '*.md' | grep -v "$f" | wc -l | tr -d ' ')
if [[ "$count" -eq 0 ]]; then
echo "WARN: $f — no incoming links (orphan)"
fi
done
Nuance: Orphans are not automatically failures. A note created today that hasn't been through /{vocabulary.cmd_reflect} yet is expected to be orphaned temporarily. Check file age:
| Condition | Level |
|---|---|
| Orphan note created < 24 hours ago | INFO (expected — awaiting reflect phase) |
| Orphan note created 1-7 days ago | WARN |
| Orphan note older than 7 days | FAIL (persistent orphan needs attention) |
| No orphans detected | PASS |
Output format:
[2] Orphan Detection ............. WARN
3 orphan notes detected:
- notes/new-claim.md (created 2h ago — awaiting reflect) [INFO]
- notes/old-observation.md (created 5d ago) [WARN]
- notes/forgotten-insight.md (created 14d ago) [FAIL]
Recommendation: run /reflect on forgotten-insight.md and old-observation.md
What it checks: Every wiki link [[target]] in every file resolves to an existing file.
How to check:
# Extract all wiki links from all markdown files
# For each unique link target, verify a file with that name exists
rg -oN '\[\[([^\]]+)\]\]' --glob '*.md' -r '$1' | sort -u | while read target; do
# Search for file matching this name
found=$(find . -name "$target.md" -not -path "./.git/*" 2>/dev/null | head -1)
if [[ -z "$found" ]]; then
echo "FAIL: dangling link [[${target}]] — no file found"
# Show which files contain this dangling link
rg -l "\[\[$target\]\]" --glob '*.md'
fi
done
Thresholds:
| Condition | Level |
|---|---|
| Any dangling link (target does not exist) | FAIL |
| All links resolve | PASS |
Why FAIL not WARN: Dangling links are broken promises. Every [[link]] a reader follows that leads nowhere erodes trust in the graph. Fix these immediately.
Output format:
[3] Link Health .................. FAIL
2 dangling links found:
- [[nonexistent-note]] referenced in:
- notes/some-claim.md (line 14)
- notes/another-claim.md (line 8)
- [[removed-topic]] referenced in:
- notes/old-note.md (line 22)
Recommendation: create missing notes or remove broken links
What it checks: Every {vocabulary.note}'s description adds genuine information beyond the title — not just a restatement.
How to check:
For each {vocabulary.note}:
description field from YAML frontmatterQuality heuristics:
| Check | Threshold |
|---|---|
| Description length | 50-200 chars ideal. < 30 chars = too terse. > 250 chars = too verbose |
| Restatement detection | If description uses >70% of the same words as the title = restatement |
| Information added | Description should mention mechanism, scope, or implication not in title |
Thresholds:
| Condition | Level |
|---|---|
| Description is a clear restatement of the title | WARN |
| Description is < 30 characters | WARN |
| Description is missing entirely | WARN (also caught by Category 1) |
| Description adds genuine new information | PASS |
This check requires judgment. Use semantic understanding, not just string matching. A description that uses different words but says the same thing as the title is still a restatement.
Output format:
[4] Description Quality .......... WARN
2 descriptions are restatements:
- notes/quality-matters.md
Title: "quality matters more than quantity"
Description: "quality is more important than quantity in knowledge work"
Issue: restates title without adding mechanism or implication
- notes/structure-helps.md
Title: "structure without processing provides no value"
Description: "having structure without processing it is not valuable"
Issue: exact restatement
Recommendation: rewrite descriptions to add scope, mechanism, or implication
What it checks: Content respects the boundaries between self/, {vocabulary.notes}/, and ops/. Each space has a purpose — conflating them degrades search quality, navigation, and trust.
Before running this check: Read ${CLAUDE_PLUGIN_ROOT}/reference/three-spaces.md for the full boundary specification.
Six conflation patterns to detect:
Queue state, health metrics, task files, or processing artifacts appearing in {vocabulary.notes}/ directory.
Detection:
# Check for ops-pattern YAML fields in notes
rg '^(current_phase|completed_phases|batch|source_task|queue_id):' {vocabulary.notes}/ --glob '*.md'
# Check for task file patterns in notes
rg '## (Create|Reflect|Reweave|Verify|Enrich)$' {vocabulary.notes}/ --glob '*.md'
| Found | Level |
|---|---|
| Any ops-pattern content in {vocabulary.notes}/ | WARN |
Agent identity or methodology content mixed into user's knowledge graph. Agent's operational observations appearing in user's {vocabulary.notes}/ space.
Detection:
# Check for agent-reflection patterns in notes (methodology observations, workflow assessments)
rg -i '(my methodology|I observed that|agent reflection|session learning|I learned)' {vocabulary.notes}/ --glob '*.md'
| Found | Level |
|---|---|
| Any agent-reflection content in {vocabulary.notes}/ | WARN |
Genuine insights trapped in session logs, observations, or ops files that should be promoted to {vocabulary.notes}/.
Detection:
# Check for claim-like content in ops that could be notes
# Look for files with description fields in ops/observations/ or ops/methodology/
rg '^description:' ops/observations/*.md ops/methodology/*.md 2>/dev/null
| Found | Level |
|---|---|
| Content in ops/ with note-like schema (description + topics) | INFO (may be intentional, flag for review) |
Temporal state stored in self/ (should be in ops/), or identity content stored in ops/ (should be in self/).
Detection:
# Check for temporal/queue content in self/
rg '^(current_phase|status|queue):' self/ --glob '*.md' 2>/dev/null
# Check for identity content in ops/
rg -i '(my identity|I am|who I am|my personality)' ops/ --glob '*.md' 2>/dev/null
Domain knowledge stored in self/ instead of {vocabulary.notes}/.
Detection:
# Check self/memory/ for notes that have topics linking to notes-space topic maps
rg '^topics:.*\[\[' self/memory/*.md 2>/dev/null | grep -v 'identity\|methodology\|goals\|relationships'
When self/ is disabled, verify ops/ absorbs self-space content correctly. Check that goals and handoffs are in ops/, not floating in {vocabulary.notes}/.
Detection:
# If self/ doesn't exist, check that goals/handoffs are in ops/
if [[ ! -d "self/" ]]; then
# Check for goals or handoff content in notes/
rg -i '(my goals|current goals|handoff|session handoff)' {vocabulary.notes}/ --glob '*.md'
fi
Thresholds:
| Condition | Level |
|---|---|
| Any boundary violation (5a, 5b, 5d, 5e) | WARN |
| Trapped knowledge in ops (5c) | INFO |
| Self-absence effects (5f) | WARN |
| All boundaries intact | PASS |
Output format:
[5] Three-Space Boundaries ....... WARN
1 boundary violation detected:
Ops into Notes (infrastructure creep):
- notes/task-tracking.md contains queue state fields (current_phase, batch)
- Should be in ops/queue/ not notes/
1 potential trapped knowledge:
- ops/observations/interesting-pattern.md has note-like schema
Consider promoting to notes/ via /reduce or direct creation
Recommendation: move task-tracking.md to ops/queue/
What it checks: The inbox-to-knowledge ratio. High ratios indicate collector's fallacy — capturing more than processing.
How to check:
# Count items in each space
INBOX_COUNT=$(find {vocabulary.inbox}/ -name '*.md' -not -path '*/archive/*' 2>/dev/null | wc -l | tr -d ' ')
NOTES_COUNT=$(find {vocabulary.notes}/ -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
QUEUE_COUNT=$(find ops/queue/ -name '*.md' -not -path '*/archive/*' 2>/dev/null | wc -l | tr -d ' ')
# Calculate ratio
if [[ $((INBOX_COUNT + NOTES_COUNT)) -gt 0 ]]; then
RATIO=$((INBOX_COUNT * 100 / (INBOX_COUNT + NOTES_COUNT)))
else
RATIO=0
fi
echo "Inbox: $INBOX_COUNT | Notes: $NOTES_COUNT | In-progress: $QUEUE_COUNT | Ratio: ${RATIO}%"
Thresholds:
| Condition | Level |
|---|---|
| Inbox-to-total ratio > 75% (3:1 inbox to notes) | FAIL |
| Inbox-to-total ratio > 50% | WARN |
| Inbox items > 20 | WARN (regardless of ratio) |
| Inbox-to-total ratio <= 50% AND inbox <= 20 | PASS |
Output format:
[6] Processing Throughput ........ WARN
inbox: 12 | notes: 8 | in-progress: 3 | ratio: 60%
Inbox items outnumber processed notes — collector's fallacy risk
Recommendation: run /reduce on oldest inbox items or /pipeline for end-to-end processing
What it checks: Notes not modified recently AND with low link density. Staleness is condition-based (low link density + no activity since N new notes were added), not purely calendar-based.
How to check:
For each {vocabulary.note}:
stat or git log)for f in {vocabulary.notes}/*.md; do
[[ -f "$f" ]] || continue
basename=$(basename "$f" .md)
# Last modified (days ago)
mod_days=$(( ($(date +%s) - $(stat -f %m "$f" 2>/dev/null || stat -c %Y "$f" 2>/dev/null)) / 86400 ))
# Incoming link count
incoming=$(rg -l "\[\[$basename\]\]" --glob '*.md' | grep -v "$f" | wc -l | tr -d ' ')
if [[ $mod_days -gt 30 ]] && [[ $incoming -lt 2 ]]; then
echo "STALE: $f — $mod_days days old, $incoming incoming links"
fi
done
Thresholds:
| Condition | Level |
|---|---|
| Any note > 30 days old with < 2 incoming links | WARN |
| Any note > 90 days old with 0 incoming links | FAIL |
| All notes either recent or well-connected | PASS |
Prioritization: Sort stale notes by:
Output format:
[7] Stale Notes .................. WARN
4 stale notes detected (>30d old, <2 incoming links):
- notes/old-observation.md (92d, 0 links) [FAIL — consider archiving or reweaving]
- notes/early-claim.md (45d, 1 link) [WARN]
- notes/setupial-thought.md (38d, 1 link) [WARN]
- notes/first-draft.md (31d, 0 links) [WARN]
Recommendation: run /reweave on these notes to find connections, or archive if no longer relevant
What it checks: Each {vocabulary.topic_map} has a healthy number of linked notes, and notes in the same topic area are actually linked.
How to check:
For each {vocabulary.topic_map} file:
topics footer/field)# For each topic map
for moc in {vocabulary.notes}/*.md; do
[[ -f "$moc" ]] || continue
# Check if this is a topic map (has type: moc in frontmatter)
rg -q '^type: moc' "$moc" || continue
moc_name=$(basename "$moc" .md)
# Count notes linking to this topic map
note_count=$(rg -l "\[\[$moc_name\]\]" {vocabulary.notes}/ --glob '*.md' | grep -v "$moc" | wc -l | tr -d ' ')
echo "$moc_name: $note_count notes"
done
Thresholds:
| Condition | Level |
|---|---|
| {vocabulary.topic_map} with < 5 notes | WARN (underdeveloped — consider merging into parent) |
| {vocabulary.topic_map} with > 50 notes | WARN (oversized — consider splitting into sub-{vocabulary.topic_maps}) |
| {vocabulary.topic_map} with > 40 notes | INFO (approaching threshold) |
| Notes exist in topic area but not linked to {vocabulary.topic_map} | WARN (coverage gap) |
| All {vocabulary.topic_maps} in 5-50 range with good coverage | PASS |
Context phrase check:
# Check for bare links in topic map Core Ideas (links without context phrases)
for moc in {vocabulary.notes}/*.md; do
rg -q '^type: moc' "$moc" || continue
# Look for "- [[note]]" without " — " context
rg '^\s*- \[\[' "$moc" | grep -v ' — ' | grep -v '^\s*- \[\[.*\]\].*—'
done
Bare links without context phrases are address book entries, not navigation. Every link in a {vocabulary.topic_map} should explain WHY to follow it.
Output format:
[8] MOC Coherence ................ WARN
3 topic maps checked:
- knowledge-work: 28 notes [PASS]
- graph-structure: 52 notes [WARN — oversized, consider splitting]
3 bare links without context phrases
- agent-cognition: 3 notes [WARN — underdeveloped, consider merging]
Recommendation: split graph-structure into sub-topic-maps; add context phrases to bare links
After running all applicable diagnostic categories, check these condition-based triggers. These are NOT the 8 categories above — they are cross-cutting signals that suggest specific skill invocations.
| Condition | Threshold | Recommendation |
|---|---|---|
| Pending observations | >= 10 files in ops/observations/ | Consider running /rethink |
| Open tensions | >= 5 files in ops/tensions/ | Consider running /rethink |
| Inbox items | >= 3 items | Consider /reduce or /pipeline |
| Unprocessed sessions | >= 5 files in ops/sessions/ | Consider /remember --mine-sessions |
| Orphan notes | Any persistent (> 7d) | Run /reflect on orphaned notes |
| Dangling links | Any | Fix broken references immediately |
| Stale notes | Low links + old | Consider /reweave |
| {vocabulary.topic_map} oversized | > 40 notes | Consider splitting |
| Queue stalled | Tasks pending > 2 sessions without progress | Surface as blocked |
| Trigger coverage gap | Known maintenance condition has no configured trigger | Flag gap itself |
How to check condition counts:
# Pending observations
OBS_COUNT=$(find ops/observations/ -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
# Open tensions
TENSION_COUNT=$(find ops/tensions/ -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
# Inbox items
INBOX_COUNT=$(find {vocabulary.inbox}/ -name '*.md' -not -path '*/archive/*' 2>/dev/null | wc -l | tr -d ' ')
# Unprocessed sessions
SESSION_COUNT=$(find ops/sessions/ -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
# Queue stalled
PENDING_TASKS=$(jq '[.tasks[] | select(.status=="pending")] | length' ops/queue/queue.json 2>/dev/null || echo 0)
The meta-trigger: Include a "trigger coverage" check. Compare known maintenance conditions against what is actually being checked. If a maintenance condition has no corresponding check or trigger configured, that gap itself is a finding. This prevents the failure mode where maintenance debt accumulates undetected.
The complete health report follows this structure. Every report, regardless of mode, uses this format.
=== HEALTH REPORT ===
Mode: [quick | full | three-space]
Date: YYYY-MM-DD
Notes scanned: N | Topic maps: N | Inbox items: N
Summary: N FAIL, N WARN, N PASS
FAIL:
- [Category N]: [brief description of failure]
[specific files and details]
WARN:
- [Category N]: [brief description of warning]
[specific files and details]
PASS:
- [Category N]: [confirmation]
---
[1] Schema Compliance ............ PASS | WARN | FAIL
[details — specific files, specific issues]
[2] Orphan Detection ............. PASS | WARN | FAIL
[details — specific files, age, link count]
[3] Link Health .................. PASS | WARN | FAIL
[details — specific dangling links, where referenced]
[4] Description Quality .......... PASS | WARN | FAIL (full mode only)
[details — specific files, title vs description comparison]
[5] Three-Space Boundaries ....... PASS | WARN | FAIL (full/three-space mode)
[details — specific boundary violations by type]
[6] Processing Throughput ........ PASS | WARN | FAIL (full mode only)
inbox: N | notes: N | in-progress: N | ratio: N%
[7] Stale Notes .................. PASS | WARN | FAIL (full mode only)
[N notes older than 30d with <2 incoming links, sorted by priority]
[8] MOC Coherence ................ PASS | WARN | FAIL (full mode only)
[details — note count per topic map, coverage gaps, bare links]
---
Maintenance Signals:
[condition-based triggers from table above, if any thresholds met]
- observations: N pending (threshold: 10) [TRIGGERED | OK]
- tensions: N pending (threshold: 5) [TRIGGERED | OK]
- inbox: N items (threshold: 3) [TRIGGERED | OK]
- sessions: N unprocessed (threshold: 5) [TRIGGERED | OK]
---
Recommended Actions (top 3, ranked by impact):
1. [Most impactful action — specific command + specific file]
2. [Second priority — specific command + specific file]
3. [Third priority — specific command + specific file]
=== END REPORT ===
Write every health report to ops/health/YYYY-MM-DD-report.md. If multiple reports are run on the same day, append a counter: YYYY-MM-DD-report-2.md.
This creates a health history that /architect can reference when proposing evolution. Trends across reports reveal systemic patterns that individual reports miss.
Every finding MUST name the specific file(s) involved.
Bad: "some notes lack descriptions" Good: "notes/example-note.md and notes/another-note.md are missing the description field"
Bad: "there are dangling links" Good: "[[nonexistent-claim]] is referenced in notes/old-note.md (line 14) and notes/related.md (line 22) but no file with that name exists"
Not all issues are equal. The recommended actions section ranks by impact:
| Impact Tier | Examples | Why |
|---|---|---|
| Highest | Dangling links, persistent orphans | Broken promises in the graph — readers hit dead ends |
| High | Schema violations, boundary violations | Structural integrity — compounds into larger problems |
| Medium | Description quality, stale notes | Retrieval quality — degraded but not broken |
| Low | {vocabulary.topic_map} size warnings, throughput ratio | Maintenance debt — matters at scale |
| Level | Meaning | Action Required |
|---|---|---|
| FAIL | Structural issue — something is broken | Fix before it compounds |
| WARN | Improvement opportunity — something is suboptimal | Address when convenient |
| PASS | Category is healthy | No action needed |
| INFO | Noteworthy but not actionable | Context for understanding |
FAIL is reserved for: Dangling links (broken graph), persistent orphans (> 7 days), severe schema violations (missing frontmatter entirely), critical boundary violations.
WARN is for everything else that is suboptimal but not broken.
Runs categories 1-3 only: Schema, Orphans, Links.
Use when:
Runtime: Should complete in < 30 seconds for vaults up to 200 notes.
Runs all 8 categories plus maintenance signals.
Use when:
Runtime: May take 1-3 minutes for larger vaults due to description quality analysis and {vocabulary.topic_map} coherence checks.
Runs category 5 only: boundary violation checks.
Use when:
Runtime: Should complete in < 30 seconds.
A vault with 0 notes is not unhealthy — it is new. Report:
Notes scanned: 0 | Topic maps: 0 | Inbox items: N
All categories PASS (no notes to check)
Maintenance Signal: inbox has N items — consider /reduce to start building knowledge
When self/ does not exist:
When qmd/MCP tools are unavailable:
Health report findings feed into other skills:
| Finding | Feeds Into | How |
|---|---|---|
| Orphan notes | /reflect | Run reflect to find connections for orphaned notes |
| Stale notes | /reweave | Run reweave to update old notes with new connections |
| Description quality issues | /verify or manual rewrite | Fix descriptions to improve retrieval |
| Schema violations | /validate | Run validation to fix specific schema issues |
| Boundary violations | Manual restructuring | Move files to correct space |
| Processing throughput | /reduce or /pipeline | Process inbox items to improve ratio |
| {vocabulary.topic_map} oversized | Manual split or /architect | Split oversized {vocabulary.topic_maps} into sub-{vocabulary.topic_maps} |
| Accumulated observations | /rethink | Review and triage observations |
| Accumulated tensions | /rethink | Resolve or dissolve tensions |
The health-to-action loop:
/health (diagnose) -> specific findings -> specific skill invocation -> /health (verify fix)
Health is diagnostic only — it measures state without prescribing changes. /architect reads health reports and proposes changes with research backing. The separation matters: health tells you WHAT is wrong, architect tells you WHY and HOW to fix it.