Personal Obsidian vault -- an external brain for work notes, decisions, performance tracking, and Claude context.
This vault has obsidian-skills installed in .claude/skills/. Follow these skill conventions:
-
obsidian-markdown: Obsidian-flavored markdown -- wikilinks, embeds, callouts, properties. See
references/for callout types, embed syntax, and property specs. Always prefer[[wikilinks]]over markdown links. -
obsidian-cli: CLI commands for vault operations when Obsidian is running. See CLI section below.
-
json-canvas: Create
.canvasfiles with nodes, edges, and visual layouts. Seereferences/EXAMPLES.md. -
obsidian-bases: Create
.basefiles with views, filters, and formulas. Bases core plugin is enabled. Seereferences/FUNCTIONS_REFERENCE.md. -
defuddle: Extract clean markdown from web pages via
defuddle parse <url> --md. -
qmd: Semantic search across the vault via QMD. Use PROACTIVELY before reading files. Preference order — pick the highest surface available and stop:
mcp__qmd__query,mcp__qmd__get,mcp__qmd__multi_get,mcp__qmd__status— registered MCP tools. If you see them in your tool menu, they are live and pre-scoped to this vault's index. Use them first; no--indexargument needed.qmd --index <name> query|search|vsearch|get|multi-get— CLI fallback for one-off shell checks or when the MCP server is unavailable. Always pass--index <name>, where<name>is theqmd_indexfield fromvault-manifest.jsonwhen set and otherwise the vault folder name slugified, so the SQLite store stays isolated from other vaults on the machine.- Grep / Glob / Read — last resort, only when QMD is not installed at all.
The MCP server (
.mcp.json→.claude/scripts/qmd-mcp.mjs), the CLI, and the SessionStart hook all read the same manifest field, so every surface scopes to the same store. On a fresh clone, runnode --experimental-strip-types .scripts/qmd-bootstrap.tsonce to build the index. Note: the MCP connect-time banner can read "0 documents" when the server registers before the SessionStart reindex finishes — a stale snapshot, not the dead-search state the self-heal guards; don't diagnose it.
Defined in .claude/commands/. Claude Code auto-surfaces every command with its description in the session's skills list — that injected list is the live catalog. Do not maintain a command table here (it drifts; the injected list can't — see Write-Correctness law 6). Agents without that injection (Codex, Gemini, Cursor): read brain/Skills.md — it carries the full command catalog plus usage docs and workflow sequences.
| Folder | Purpose | Key Files |
|---|---|---|
Home.md |
Vault entry point -- embedded Base views, quick links | Open this first |
vault-manifest.json |
Template metadata -- version, infrastructure vs user content boundaries, frontmatter schemas, version fingerprints | Used by /om-vault-upgrade for migration |
CHANGELOG.md |
Version history -- tracks template releases (v1--v3.3) with what changed | Reference for upgrade paths |
bases/ |
All Bases centralized -- dynamic views for navigation | Work Dashboard (incl. Stale Actives), Recently Touched (recency by real mtime — the answer to "what's most recent", not filename dates), Incidents, People Directory, 1-1 History, Review Evidence, Competency Map, Templates |
work/ |
Work notes index | Index.md (detailed MOC) |
work/active/ |
Current projects only (1-3 files) | Move here when starting, move to archive when done |
work/archive/YYYY/ |
Completed work organized by year | Grows over time |
work/incidents/ |
Incident docs (main note + RCA + deep dive + drafts) | Per-incident grouping |
work/1-1/ |
1:1 meeting notes (accumulate weekly) | Named <Person> YYYY-MM-DD.md |
work/meetings/ |
Meeting notes inbox -- staging area for raw exports, processed by /om-intake |
Drop files, run /om-intake |
perf/ |
Performance framework, brag doc | Brag Doc.md (index) |
perf/brag/ |
Quarterly brag notes | One per quarter, e.g. Q1 2025.md |
perf/competencies/ |
Atomic competency notes (link targets) | One note per competency |
perf/evidence/ |
PR deep scans, data extracts for reviews | Named <Person> PRs - <Period>.md |
perf/<cycle>/ |
Review cycle briefs + artifacts | Review briefs (private, manager, peer) |
brain/ |
Claude's operational knowledge | Memories.md, Key Decisions.md, Patterns.md, Gotchas.md, Skills.md, North Star.md |
memories/YYYY/MM/ |
Cross-repo agent memory -- durable lessons recorded over MCP by sessions working in other repositories. Time is the only thing in the path; reach is declared in frontmatter | Browse via bases/Memories.base; never edit by hand |
org/ |
Organizational knowledge index | People & Context.md (MOC) |
org/people/ |
Atomic person notes | One note per person |
org/teams/ |
Team notes as graph nodes | One note per team |
reference/ |
Codebase knowledge, architecture maps | Flow docs, architecture docs |
thinking/ |
Scratchpad for drafts and reasoning | Named YYYY-MM-DD-topic.md |
templates/ |
Obsidian templates | Work Note.md, Decision Record.md, etc. |
.claude/commands/ |
Slash commands (auto-surfaced in-session; catalog in brain/Skills.md) |
One .md per command |
.claude/agents/ |
Subagents | See subagents table below |
.claude/scripts/ |
Hook scripts + the MCP server | session-start.ts, classify-message.ts, validate-write.ts, pre-compact.ts, stop-checklist.ts, charcount.ts, om-mcp.mjs (see Reaching the vault from another repo below) |
.claude/skills/ |
Obsidian + QMD skills | Loaded automatically via Skill tool |
When Obsidian is running, prefer CLI over raw filesystem — it provides vault-aware search, backlink discovery, and property management. On macOS, open Obsidian before invoking the CLI: the first obsidian call launches the Electron app (visible window flash) if no instance is running; subsequent calls forward args silently. In non-interactive contexts where you can't guarantee Obsidian is open (background hooks, automation), prefer filesystem reads instead.
obsidian read file="Note Name" # Read a note
obsidian create name="Name" content="..." silent # Create without opening
obsidian append file="Name" content="..." # Append to note
obsidian search query="text" limit=10 # Vault-aware search
obsidian backlinks file="Name" # Discover connections
obsidian tags sort=count counts # List all tags
obsidian tasks daily todo # Open tasks
obsidian daily:read # Today's daily note
obsidian property:set name="status" value="done" file="Name"
obsidian orphans # Unlinked notesfile= resolves like a wikilink (by name). path= for exact path from root. Use silent to prevent files from opening. Run obsidian help for full reference.
The SessionStart hook automatically injects rich context: vault file listing, North Star goals, active work, recent git changes, open tasks (aggregated from work/active/ and the vault root, excluding infrastructure files), vault-hygiene drift flags (act on them or run /om-tidy), and triggers a QMD re-index. Most context is already loaded -- you don't need to manually read files.
Shortcut: Run /om-standup for a structured morning kickoff that reads everything and presents a summary with suggested priorities.
If doing it manually:
- Read
Home.md-- vault entry point with embedded dashboards - Read
brain/North Star.md-- ground suggestions in current goals - Check
work/Index.md-- see active projects and recent notes - Scan
brain/Memories.md-- index of memory topics, then read relevant topic notes obsidian tasks daily todo-- see pending items
When the user says "wrap up", "let's wrap", "wrapping up", or similar -- invoke /om-wrap-up automatically. This runs a full review of the session.
If /om-wrap-up is not invoked, at minimum do these before wrapping up:
- Archive completed projects:
git mvfromwork/active/towork/archive/YYYY/, updatestatus: completed(or use/om-project-archive) - Update
work/Index.mdif new notes or decisions were created - Update the relevant brain topic note (
brain/Key Decisions.md,brain/Patterns.md,brain/Gotchas.md) with key learnings - Update
org/People & Context.mdif org knowledge changed - Update
perf/Brag Doc.mdif wins or impact were achieved - Offer to update
brain/North Star.mdif goals shifted or new focus emerged - Verify all new notes link to at least one existing note (orphans are bugs)
- If work demonstrates competencies, add competency links to the work note's
## Related - Run
/om-vault-auditif the session created many notes
Skip steps that don't apply. The goal is transferring durable knowledge from conversation to vault state.
Use thinking/ for drafts, reasoning, and analysis before writing final notes. Thinking notes are scratchpads, not storage. They exist to help you reason -- once the reasoning produces durable knowledge, promote it to proper notes and delete the scratchpad.
- Create a thinking note:
thinking/YYYY-MM-DD-descriptive-name.md - Use the Thinking Note template
- Reason through the problem, analyze options, draft content
- Promote findings to atomic notes in the correct folder (not one monolith -- one note per distinct concept)
- Delete the thinking note -- it served its purpose
- If the thinking process itself is worth preserving (unusual), keep it but link to the promoted notes
- Always use YAML frontmatter with at minimum
date,description(~150 chars),tags, and type-specific fields. Work notes and incidents also needquarter(e.g.,Q1-2026). Incidents needticket,severity,role. - Use templates from
templates/. Fill{{placeholders}}with real values. 2b. Write fully, organize structurally — the vault tidies itself. Size is a STRUCTURE signal, never a brevity signal: when a note crosses the ~25KB organization threshold (bytes, not lines — giant single-line entries hide in low line counts), the PostToolUse hook flags it at write time and the hygiene scan flags it at session boundaries. The response is always a SPLIT (domain notes, event-log satellites, or a cluster folder — content moved verbatim, one-liner index left behind, inbound links retargeted), never trimming content. The same hook flags write-time topic clusters — token overlap is blind, so judge genuine shared context before grouping./om-tidyis the acting half. Exempt:*Archive*notes (bulk is their job). - Place files correctly:
- Active work notes, decisions, peer review prep --
work/active/ - Completed work notes --
work/archive/YYYY/(by year) - Incident docs --
work/incidents/ - 1:1 meeting notes --
work/1-1/ - Performance content --
perf/(cycle subfolder for review briefs) - PR evidence --
perf/evidence/ - Competency definitions --
perf/competencies/ - People --
org/people/ - Teams --
org/teams/ - Claude operational context --
brain/ - Codebase knowledge --
reference/ - Drafts --
thinking/ - Vault root:
Home.md,CLAUDE.md,AGENTS.md,GEMINI.md,vault-manifest.json,CHANGELOG.md,CONTRIBUTING.md,README.md,LICENSE,.gitignore. No user notes at root.
- Active work notes, decisions, peer review prep --
- Name files descriptively. Use the note title as filename. Date convention: prefix point-in-time notes with a date — 1:1s (
<Person> YYYY-MM-DD.md), dated captures, meeting exports. Living and entity notes use a bare title — work notes, person/team notes, competencies,brain/,reference/. Why the split: a creation-date prefix on a continuously-edited note inverts the recency signal (a living doc looks stale while being updated daily). For "what's most recent," usebases/Recently Touched.base(real modified time), never filename dates. - Group multi-note workstreams in a subfolder. Once a workstream has more than one note, it gets a folder (
work/active/<Topic>/; the archive mirrors the grouping). Folders are the lifecycle/context axis only — links remain the primary organization (wikilinks resolve by name across folders, so grouping never breaks links, QMD, or the graph). The hygiene scan and write-time cluster sensor flag candidates;/om-project-archivemoves whole clusters.
| Type | Location | Naming | Key Sections |
|---|---|---|---|
| Work note | work/active/ (then archive/YYYY/ when done) |
Descriptive title | Context, What/Why, Links, Related |
| Incident | work/incidents/ |
Ticket number or descriptive title | Context, Root Cause, Timeline, Impact, Analysis, Related |
| 1:1 note | work/1-1/ |
<Person> YYYY-MM-DD.md |
Key Takeaways, Action Items, Quotes, What to Watch, Related |
| PR analysis | perf/evidence/ |
<Person> PRs - <Period>.md |
PR Count, Projects, Quality, Growth, Full Table |
| Review brief | perf/<cycle>/ |
<Cycle> Review Brief.md |
Arc, Impact, Competencies, Documentation Trail |
| Person note | org/people/ |
Full name | Role & Team, Relationship, Key Moments, Notes |
| Team note | org/teams/ |
Team name | Members, Scope, Interactions |
| Competency | perf/competencies/ |
Competency name | Definition, level criteria, Evidence (via backlinks) |
| Brain note | brain/ |
Topic name | Topic-specific content |
| Domain note | Beside its family index | <Index> - <Domain> (e.g. Gotchas - Tooling) |
Substance lives here; the family index keeps a one-liner per entry. Born from monolith splits — never re-inline into the index |
| Event-log satellite | Beside its core note | <Core> — <Event> Log or dated title |
Chronological bulk offloaded from a person/project core note; the core links it. Dated entries, verbatim moves |
| Archive note | Beside its live note | <Live Name> Archive[ — <window>] |
Bulk by design, hygiene-exempt (name contains "Archive"). Verbatim zero-loss moves; live note keeps a one-liner index + link |
Graph-first, not folder-first. Folders help browse in the sidebar. Links help discover through connections. Both matter, but links are the primary organizational tool.
A note without links is a bug. When creating a note, the FIRST thing to do after writing content is add wikilinks. Every new note must link to at least one existing note.
Atomicity rule: Before writing or appending to any note, ask: "Does this cover multiple distinct concepts that could be separate nodes?" If a note has or would have 3+ independent sections that don't need each other to make sense, split into atomic notes that link to each other.
Note types have graph roles:
- Evidence nodes (work notes, 1:1s, PR analyses): add outbound links to concepts they demonstrate
- Concept nodes (competencies, patterns): stay definitional -- evidence arrives via backlinks
- Index nodes (Index, Brag Doc, Memories, People & Context): actively curate links -- they're navigational
- Person nodes (org/people/): link to projects, teams, evidence. Receive backlinks from work notes.
Link syntax:
[[Note Title]]-- standard wikilink[[Note Title|display text]]-- aliased link[[Note Title#Heading]]-- deep link to section![[Note Title]]-- embed content inline[[Note Title#^block-id]]-- link to specific block
- Work note <-> Decision: bidirectional links
- Work note -> Competency: in
## Related, link to competencies demonstrated - Work note -> Team: in
## Related, link to team(s) involved - Work note -> Person: link people involved (especially in 1:1 notes)
- Person -> PR analysis: link to their evidence file if one exists
- Brag Doc -> Work note: every entry links to evidence
- Memories -> Source: every memory links to where it was learned
- Index -> Everything:
work/Index.mdlinks to all work notes - North Star -> Projects: active focus areas link to project work notes
Update these when creating or archiving notes:
work/Index.md-- add to Active Projects or Recent Notes, move completed to Archivebrain/Memories.md-- index of memory topics. Add new memories to the relevant topic note, not here.brain/Skills.md-- register vault-specific workflows and slash commandsorg/People & Context.md-- update when people, teams, or org structure changesperf/Brag Doc.md-- log wins with links to evidence, add new quarters as needed
When a decision earns a record. Any ONE of these holding is enough:
- Hard to reverse — undoing it later costs real work (a schema, a public contract, a naming scheme).
- Surprising without context — a competent reader would ask "why is it like this?" and the code alone cannot answer. This is the clause that stops the next contributor from "fixing" something deliberate.
- A real trade-off — something was genuinely given up, so the losing option's case is worth keeping.
None hold → no record; the tracker and the commit message are enough. The gate is deliberately symmetric: it prescribes records where they are missing today, and it licenses not writing one everywhere else, which is what keeps the record set worth reading. Without it, records cluster where writing them was easy rather than where losing them is expensive.
Once it clears the gate:
- Create in
work/using the Decision Record template - Link from the work note(s) that led to the decision
- Add to the Decisions Log table in
work/Index.md - If significant, note in
brain/Key Decisions.md
When significant work is completed, add to perf/Brag Doc.md with links to the work note(s). Categorize under Impact, Technical Growth, Collaboration, or Feedback.
brain/North Star.md is a living document of goals and focus areas.
- Read it at the start of substantial sessions
- Reference it when suggesting priorities or trade-offs
- Update it when the user signals a shift in goals
- Both the user and Claude write to it
Use tags in frontmatter (not inline):
- Type:
work-note,decision,perf,thinking,north-star,competency,person,team,brain - Index:
index,moc - Status (frontmatter field):
active,completed,archived,proposed,accepted,deprecated - Team (frontmatter field on people + work notes): your team names, e.g.
Backend,Platform,Mobile - Cycle (frontmatter field on review-related notes):
h2-2024,h1-2025, etc. - Person (frontmatter field on evidence notes): full name of the person
- Project: as needed, e.g.
project/auth-refactor
Beyond tags, use these frontmatter properties to enable search and Bases views:
cycle: h2-2024-- find all review material for a cycleperson: "Jane Smith"-- find all evidence related to a personteam: Backend-- find all notes related to a teamstatus: active-- find active projectsquarter: Q1-2026-- find all work for a quarter (used by Work Dashboard Base)ticket: TICKET-123-- find incident by ticket numberseverity: high-- incident severityrole: incident-lead-- your role in an incident
All project memories live in the vault. The ~/.claude/ MEMORY.md is an auto-loaded index that points to vault locations. The ~/.claude/ MEMORY.md is the only file that should exist there -- it is an auto-loaded index. Never create additional memory files in that directory.
| System | Location | Purpose |
|---|---|---|
| MEMORY.md | ~/.claude/projects/.../memory/MEMORY.md |
Auto-loaded index only. Pointers to vault notes. |
| Vault memories | brain/ topic notes |
Git-tracked, Obsidian-browsable, linked. All durable knowledge lives here. |
| Cross-repo memories | memories/YYYY/MM/ |
Written by sessions in other repos over MCP, under an enforced epistemic contract. See below. |
Which one, when you are working inside the vault: brain/. Always. The memories/ tree is written by the om MCP server on behalf of sessions elsewhere, and the server refuses a write from a session running inside the vault — a memory recorded here would be scoped to the vault-as-a-project and reach only the sessions that already read every note directly. Write the note normally and let the hooks file it.
When asked to "remember" something:
- Find or create the appropriate
brain/topic note (Gotchas, Patterns, Key Decisions, etc.) - Add the knowledge there with a wikilink to context
- Update
brain/Memories.mdindex if a new topic note was created - Do NOT create additional files in
~/.claude/projects/.../memory/beyond MEMORY.md -- they are not version-controlled
The SessionStart hook injects a Brain Topics (read on demand) index listing each brain/ topic note with its description and an (empty) marker for stub notes. Treat that index as a menu:
- When the user's message touches a topic from the index (debugging → Gotchas, "how do we usually…" → Patterns, "why did we decide" → Key Decisions, "which command / slash" → Skills), query QMD first before answering — call
mcp__qmd__querywith aqueryargument describing the topic (or fall back toqmd --index <name> query "<topic>"if MCP is unavailable). The search covers the whole vault, so filter or prioritize results whosefilepath is underbrain/. Do not assume the topic name alone scopes the search. - If QMD is unavailable, read the specific
brain/note directly with the Read tool. Don't load all ofbrain/— only the one(s) matching the topic. - Skip notes marked
(empty)in the index — they're stubs with no substantive content. - After answering, if the conversation produced durable knowledge, update the relevant brain note (see the "remember" workflow above).
The vault normally only helps while you are sitting in it. The om MCP server makes it reachable from a coding session running in any other repository: that session can search the vault, read notes, follow the graph, and record back into it.
1. Register the server in the consuming project's .mcp.json:
{
"mcpServers": {
"om": {
"command": "node",
"args": ["<absolute path to your vault>/.claude/scripts/om-mcp.mjs"]
}
}
}2. Add a short section to that project's own CLAUDE.md, telling it the vault exists and to consult it.
[!warning] Step 2 is not documentation garnish. Measured: with the server wired and no repo-side instruction, a session made zero vault calls and went on to implement a design the vault had recorded as explicitly rejected. With the instruction present, it refused and cited the note.
The reason is an asymmetry in how MCP
instructionspropagate: a prohibition holds reliably, while a positive "go consult the vault" is advisory and gets skipped whenever a nearer source exists. The server can stop a session doing something; only the project's own law makes one go looking.
[!warning] Do not register the raw
qmdserver in a consuming repo. It searches every note directly, with no notion of which memories were written for which project — so the repo matches against lessons meant for unrelated ones. Applying each memory's declared scope on top of the index is exactly whatomadds, and going around it returns the wrong things, not merely more of them.
| tool | purpose |
|---|---|
search |
semantic + keyword search over exposed notes |
expand |
a note's graph neighbourhood — links out and backlinks |
recall |
durable lessons scoped to the calling repo, most specific first |
remember |
record a lesson that will still be true in a different repo |
record_work |
record what happened in this repo, filed where it belongs |
reason |
judgement across several notes — spawns a second session, so it takes longer |
health |
is the wiring intact? |
Plus notes as readable resources, and recall_topic / prior_art as prompts you invoke yourself from the / menu.
The other tools answer without inference. reason reads the vault with a second Claude session, so it is slower and uses more — reach for it when search or recall returned the notes but not the judgement. It seeds itself from search, so there is no need to search first.
It runs on your own CLI default model, so the vault answers at the level you are already working at. MCP gives a server no way to see the calling session's model, so inheriting the CLI default is the closest reachable thing to "the same model I am using".
Every call is on the record — including one that produced no answer, since the log is written before any refusal. It lands in .claude/om-mcp-audit.jsonl with the question, cost, turns, terminal reason, model asked for, model that ran, wall time, and the roots the spawn was given. health reports the day's figure, and says at least when the log was too big to read whole rather than quietly under-reporting.
It reads what this vault serves, and nothing else. The spawn is handed the same three rules the exposure policy applies to search results: your mcp_exposed_roots, your mcp_never_expose filenames, and private:-tagged notes — the last two matter because they live inside exposed roots. Unset, that is simply your user_content_roots, so the boundary is whatever you already declared rather than anything this tool adds. It is also told not to read the memory root, since a memory belongs to the project it was scoped to.
The server stays responsive while it runs. Other tools answer normally mid-call, and a shutdown ends any spawn still in flight rather than orphaning it.
One optional key, which does not ship set:
"reason": { "model": "claude-haiku-4-5" } // pin a model instead of inheritingLeft unset — the default — it uses your own Claude settings.
[!warning] If you pin a model, use a FULL id.
--model haikuis not honoured by the CLI and does not error — it silently runsclaude-sonnet-5. Bare aliases (haiku,sonnet,opus) are therefore dropped in favour of inheriting; anything else is passed through as written. Every answer names the model that actually ran, and says whether it was the pinned one.
If a run ends early you get no answer, not a truncated one, plus the evidence search already found — a partial synthesis presented as complete is the one outcome worse than none.
Answers land in .claude/om-reasoning/ (gitignored) marked confidence: inferred. They are deliberately not recorded as memories — a spawned conclusion is reasoning, not verified knowledge, and the calling session decides whether any of it earns a remember.
This is the part worth understanding, and it is what the layer is for. Every memory declares its reach when written:
scope: project,projects: [a, b]— reaches those repos and no othersscope: platform,platforms: [ios]— reaches any repo on that platformscope: general— reaches everywhere
A reader never widens what the writer declared, so a sibling app on the same platform does not inherit another app's project-scoped constraints. recall with explain: true reports why each memory was shown and how many were withheld.
Getting the reach wrong is corrected the same way any other error is — remember is append-only, so re-file the lesson with the right scope and pass supersedes: ["<its exact title>"]. Narrowing takes effect: a superseded memory is served only where its correction can follow it, so the caller the correction excludes stops receiving the wider stale copy rather than receiving it alone with nothing to replace it.
A repo is identified by its folder name, which is usually right and occasionally not: two repos both called api share one identity, so each receives the other's memories. Write a distinct name into a .om-project file at the repo root to separate them — health reports which repo it thinks is calling and where that name came from.
Your vault, your notes, your session. The default is the vault's own user_content_roots, at the granularity it declares them (work/active/, not all of work/). Set mcp_exposed_roots only if this vault holds material that is not yours to share — employer-confidential notes, a client's data.
| key | default | meaning |
|---|---|---|
mcp_exposed_roots |
(empty → user_content_roots) |
folders whose notes may be read |
mcp_never_expose |
(empty) | filenames withheld regardless of folder |
memory_root |
memories |
where cross-repo memories live |
mcp_inbox |
inbox |
fallback destination for record_work |
memories/ is never served as an ordinary note whatever the config says — memories carry their own scope, and the note surface would bypass it. A note tagged private in frontmatter is never served either.
That holds on every surface, including the one that reads across both. recall serves a promoted block out of brain/ when a capture's promoted: marker carries an anchor (promoted: "brain/Gotchas - Engineering#^om-a1b2c3"), so a foreign repo gets the corrected version rather than the capture as first written. It asks the same exposure predicate as everything else, so a withheld or private note is refused there too, and a marker with no anchor still serves nothing. Write anchors with /om-tidy — the block you point at is what a session in another repo reads.
[!note] Keeping vault material out of repos is the contract's job, not this list's. A session can read the vault directly, so narrowing what the server serves prevents nothing on its own. What works is the prohibition injected into the calling session, plus
.claude/om-mcp-audit.jsonl(gitignored), which records every read with the calling repo.
Run health when something that should be in the vault cannot be found — every failure in this layer presents identically as "no results", and that tool is what tells them apart.
- Folders group by purpose, links group by meaning. A note lives in ONE folder (its home) but links to MANY notes (its context).
- When creating a note, add wikilinks FIRST. A note without links is a bug.
- Prefer bidirectional links: if A links to B, B should link back to A (unless B is a concept node that receives backlinks passively).
- Before creating a new subfolder, ask: "Can I solve this with a tag, a property, or a link instead?" Folders are for browsing convenience, not for categorization.
- After every substantial session, verify new notes have at least one inbound link.
- Writing about a person? --
org/people/ - Writing about a team? --
org/teams/ - Writing about how the codebase works? --
brain/(Patterns, Gotchas, Key Decisions) - Writing about what Claude should remember? --
brain/Memories.mdtopic notes - Capturing a 1:1 meeting? --
work/1-1/ - Deep scanning PRs for review? --
perf/evidence/ - Creating review briefs? --
perf/<cycle>/ - Tracking active project work? --
work/active/ - Capturing an incident? --
work/incidents/(use/om-incident-capture) - Dumping unstructured info? -- use
/om-dumpto auto-classify and route everything
When capturing data from Slack, DMs, or meetings:
- Project evidence (PRs, technical decisions, delivery) -- goes to the relevant
work/note - Review prep (peer selection, manager strategy, brag framing) -- goes to review-related notes in
perf/orwork/ - People dynamics (feedback, relationships, career) -- goes to
org/people/notes - Personal conversations -- only capture if review-relevant; otherwise skip
Specialized agents in .claude/agents/ for heavy operations. They run in isolated context windows.
| Agent | Purpose | Invoked by |
|---|---|---|
brag-spotter |
Finds uncaptured wins and competency gaps | /om-wrap-up, /om-weekly |
context-loader |
Loads all vault context about a person, project, or concept | Direct |
correction-sweep |
Finds every note restating a corrected fact; classifies each as authoritative, restatement, or historical. Never edits | /om-correct |
cross-linker |
Finds missing wikilinks, orphans, broken backlinks | /om-vault-audit |
people-profiler |
Bulk creates/updates person notes from Slack profiles | /om-incident-capture |
review-prep |
Aggregates all performance evidence for a review period | /om-review-brief |
slack-archaeologist |
Full Slack reconstruction -- every message, thread, profile | /om-incident-capture |
vault-librarian |
Deep vault maintenance -- orphans, broken links, stale notes | /om-vault-audit |
review-fact-checker |
Verifies every claim in a review draft against vault sources | /om-self-review, /om-review-peer |
vault-migrator |
Classifies, transforms, and migrates content from a source vault | /om-vault-upgrade |
Five lifecycle hooks in .claude/settings.json:
| Hook | When | What |
|---|---|---|
| SessionStart | On startup/resume | QMD re-index + self-heal, inject North Star, active work, recent changes, tasks, file listing (folders past a note-count threshold collapse to one line), vault-hygiene drift flags (completed-not-archived, ungrouped clusters, 25KB oversize, stale open loops, meetings-inbox pressure); the whole injection is held under a byte budget — over it, low-priority sections degrade to pointers and the closing injection-size meter names each one it dropped |
| UserPromptSubmit | Every message | Classifies content (decision, incident, win, 1:1, architecture, person, project update) and injects routing hints |
| PostToolUse | After writing .md |
Validates frontmatter + wikilinks, blocks misplaced memory files, flags notes crossing the 25KB organization threshold (split, don't trim) and write-time topic clusters |
| PreCompact | Before context compaction | Backs up session transcript to thinking/session-logs/ |
| Stop | End of every session | Lightweight checklist reminder + concrete vault-hygiene drift findings (same scan as SessionStart). For thorough review, use /om-wrap-up instead. |
Each law exists because its absence caused real correction work in vaults running this template. Violating them re-creates documented failures.
- Single-source status. A project's volatile status (version, counts, released/blocked, dates) lives in exactly ONE place — its note's frontmatter + top status line. Every other note links to it and never restates it. Why: one wrong status statement hardened into ~8 notes downstream and had to be swept out.
- Correction-sweep protocol. When a fact is corrected, find every restatement and fix them all in the same pass.
/om-correct "<the corrected fact>"is the acting half — grep alone finds only the notes that copied the wording, and the expensive half is the paraphrase: the same claim in different words, invisible to grep and indistinguishable from correct prose to every structural check. It is also the half most likely to survive a manual sweep, because the sweeper stops when grep goes quiet. A correction callout on top of a note whose body still says the wrong thing is NOT a correction — future sessions re-absorb the stain from the body. Notes that correctly record what was believed at the time are preserved, never rewritten; see Law 5. - Mark inference. Anything not verified against source (code, repo, primary doc, the person) carries an explicit
(TBC)/(unverified)/(inferred)marker. Never state inference bare. - Date-stamp volatile facts. Counts, versions, org structure, tool maturity: write "as of YYYY-MM-DD" so staleness is self-evident instead of silent.
- Attribution vs. creation dates may differ (a
quarter:field vs. thedate:field's quarter) — that's legitimate, not a bug to "fix". - No counts in instruction files. Hardcoded counts ("11 slash commands") in CLAUDE.md/README rot silently — describe, don't count.
-
For "what's most recent," use
bases/Recently Touched.base(real modified time), never filename dates — a creation-date prefix on a continuously-edited note inverts the recency signal. -
Never modify
.obsidian/config files unless explicitly asked. -
Preserve existing frontmatter when editing notes.
-
Git sync is handled by the user's preferred method (obsidian-git, manual commits, etc.) -- don't configure git hooks or auto-commit.
-
When asked to "remember" something, write to the relevant
brain/topic note with a link to context. Never create memory files in~/.claude/-- they are not git-tracked. -
Prefer Obsidian CLI over filesystem when Obsidian is already running. On macOS, the first
obsidiancall launches the Electron app (visible window flash) if no instance is running — open Obsidian once at session start, then subsequent calls forward args silently. In non-interactive contexts where you can't guarantee Obsidian is open (background hooks, automation), prefer filesystem reads. -
Always invoke Obsidian skills via the Skill tool before doing vault work. Load
obsidian-markdownwhen creating/editing.mdfiles. Loadobsidian-cliwhen running vault commands. Loadobsidian-basesorjson-canvaswhen working with those file types. -
Always check for and suggest connections between notes.
-
Every note must have a
descriptionfield (~150 chars). Claude fills this automatically. -
Zero data loss: when reorganizing, always use
git mv. Never delete without explicit user confirmation.