mn:save — Memory Routing Cascade
Invocation marker (both runtimes): begin your reply with the exact line
🧠 mn:save (mnemo) → running— the user-visible confirmation that this skill actually loaded. Emit it once per invocation, before any other output.
Portable paths
Resolve <mnemo-root> once to the absolute plugin root before reading bundled files or running bundled scripts. In Claude Code, use ${CLAUDE_PLUGIN_ROOT}; in Codex, derive it from this loaded SKILL.md path (skill directory → skills/ → plugin root). Replace <mnemo-root> with that quoted absolute path in every command — never execute the placeholder literally and never hunt versioned cache directories.
When another mnemo skill must run, use the runtime-native path: Claude Code invokes mn:<skill> through its Skill tool; Codex reads <mnemo-root>/skills/<skill>/SKILL.md completely and follows it with the prepared input. For user-facing explicit syntax, render /mn:<skill> in Claude Code and $mnemo:<skill> in Codex.
Save information to multiple memory backends with graceful degradation. Each backend is tried independently — if one fails, others still work.
Prerequisites & config
Obsidian is preferred but not required (skill degrades gracefully). Config at ~/.mnemo/config.json — full schema including cascade.* toggles in <mnemo-root>/references/config-schema.md.
Workflow
Step 0: Classify the Input
First — is this worth saving at all? (Especially when saving proactively, unprompted.) Save only if a future session would act differently for knowing it: a solved bug + root cause, a non-obvious decision + why, a gotcha that bit you, a durable fact, an actionable rule. Skip as a NOOP — say "nothing worth persisting here" and stop — for routine steps, anything the repo/git already records, one-off chatter, or what a future agent would re-derive trivially. Saving noise pollutes recall; a lean vault is the asset.
Never persist secrets — mask tokens / keys / passwords as <REDACTED> before writing (a note is durable and may sync).
Then determine what type of information is being saved:
| Type | Goes to | Example |
|---|---|---|
| fact | Obsidian taxonomy_roles.fact + optional claude-mem |
"Heroku standard-0 has 25 auto-backups" |
| insight | Obsidian taxonomy_roles.insight + optional claude-mem |
"CLI-first is 70,000x cheaper because of token savings" |
| decision | Obsidian taxonomy_roles.fact + optional claude-mem + Claude auto memory when error-preventing |
"We chose SCOPE over TextGrad for self-correction" |
| gotcha | Obsidian taxonomy_roles.fact + Claude auto memory + possibly CLAUDE.md |
"execSync with shell=true is banned in antomate" |
| principle / pain / stance (how the user thinks / what pains them / their quality bar — the business-logic & mental-model layer) | Obsidian taxonomy_roles.insight (narrower semantic type in kind:) + optional claude-mem |
"the user prefers ambitious-first with honest trade-offs" · "pain: pasting a wall of prompt every session" |
| source | Obsidian taxonomy_roles.source + optional claude-mem |
External article, tool, research finding |
| actionable rule | .claude/rules/<domain>.md (auto-inject, path-scoped) — Step 3.5 only |
"After touching sign_epl.py, always gate the Kontur call on the flag" |
Recall vs actionable rule — the fork this cascade turns on. A recall item (fact / insight / decision / source) answers "what / why" and is fetched on demand. An actionable rule — "never do X / always do Y" tied to specific code — must auto-surface when a future agent opens the relevant file, before it repeats the mistake. Its home is
.claude/rules/(native path-scoped auto-load, Step 3.5), not recall memory. Most saves are recall; route to.claude/rules/only when the rule would have prevented an error by appearing at the right moment.Routing consequence — apply this to every step below. A recall item flows through Steps 1-4 normally. An actionable rule goes to Step 3.5 only: skip Steps 1-3 (Obsidian / claude-mem / memory/ are superseded by the auto-injecting rule file — don't double-write), and Step 4 (CLAUDE.md) fires only as the fallback if Step 3.5 declined (e.g.
project_rulesdisabled). One kind → one home.
Step 0b: Shape the note — typed slots, atomicity, one rationale
Depth comes from structure, not volume. The unit you retrieve must equal the unit you address — a well-titled atom, never a 5000-word blob. A blob is unretrievable point-precisely (you can't grab the right slice from it) and drifts straight into the auto-ingest fork mnemo's founding principle rejects (docs/design-decisions.md).
Split, don't dump. If the material carries ≥2 separable claims, create ≥2 atomic notes — plus, only if a genuine cross-note insight emerges, one insight-role synthesis linking them. Never truncate; never fuse unrelated claims into one note. "Maximally detailed" is a property of the corpus (many atoms + one narrative session note + connect links), never of a single note.
Two non-negotiables per typed note:
- Claim-shaped title (title-as-API): a statement, not a topic —
{fact_prefix}Redis fail-open keeps reads alive when cache is down, not{fact_prefix}Redis. If no single claim fits the whole note, it isn't atomic → split. because/ rationale in the body: the premise the claim rests on, so a future agent sees why and never silently revokes a decision whose condition it can't see.- Gate strength: hard for
decision/ actionable rule /principle·pain·stance— reject the write until both exist. Soft nudge for a plainfact(a bare fact may legitimately have no deeper because).
Route the body into the slot template for its semantic type (these are body shapes written into the same Markdown string — not new backends, scripts, or frontmatter machinery):
- decision → one Y-statement: In the context of
<X>, facing<concern>, we chose<option>over<alt>, to achieve<quality>, accepting<downside>, because<rationale>. (Carries context + choice + rejected + goal + trade-off + reason in a single atom.) - gotcha / business rule →
GIVEN <precondition>/WHEN <action>/THEN <observable>+Because:+Fails-when:(the provoking input → wrong outcome, so the rule is grep-able by its symptom). - principle / pain / stance (the how-the-user-thinks / business-logic / pain layer — W4) → JTBD slots:
Job:(what they're trying to get done) /Pain:(functional · emotional · social) /Done-well:(the quality bar that makes output acceptable) /Anti-goal:(what they explicitly reject). Turns taste into a checkable bar a future agent can self-audit against. - fact / insight → claim-title + a BLUF first line (the load-bearing statement on top — it survives context down-weighting) + evidence/links.
- source → external material + your annotations (unchanged).
W4 notes are human-authored, not psychoanalysis. A principle/pain/stance note pins one atomic claim about how the user thinks, decides, or hurts — authored or confirmed by the user in the conversation, never the agent inventing a dossier (that would be auto-ingest by the back door, violating the human-authored principle). On a proactive (unprompted) W4 save, draft the atom and confirm the claim with the user before writing.
Record the semantic sub-type. Write kind: <decision|gotcha|principle|pain|stance> into frontmatter whenever the semantic type is narrower than its taxonomy role — this makes [kind:pain] property-searchable in /mn:ask. Plain fact/insight/source omit kind: (it would only echo the role).
Step 1: Obsidian (Primary — for the user)
Skip if: cascade.obsidian.enabled is false, or Obsidian CLI returns "Unable to connect"
Validate the role map before naming the note: its key set must be exactly fact, insight, source, session, and moc; every target must name an entry in config.taxonomy; and the functional roles must self-map as session → session and moc → moc. Then resolve fact/decision/gotcha through taxonomy_roles.fact, insight/principle/pain/stance through taxonomy_roles.insight, and source through taxonomy_roles.source, using the mapped entry's prefix and tag for the filename/frontmatter (record the narrower semantic type in kind: — Step 0b). The W4 types piggyback on the insight role exactly as decision/gotcha piggyback on fact — no taxonomy_roles schema change, so it works on every existing vault without re-setup. If the role map is absent and all five legacy Zettelkasten taxonomy keys exist, use the documented deterministic fallback. Otherwise stop this backend and offer setup — never infer routing from a human-facing prefix.
python3 "<mnemo-root>/scripts/safe-read.py" search <<< '{"query":"{key words}","vault":"{vault}"}'
If duplicate found → ask: update existing or create new?
Create through the bundled JSON-stdin writer (both runtimes):
python3 "<mnemo-root>/scripts/vault-write.py" <<< '{"action":"create","vault":"{vault}","note":"{mapped_prefix}{descriptive title}","content":"{one JSON-escaped Markdown string containing frontmatter, body, and links}"}'
The Markdown string contains type: {mapped taxonomy key}, the mapped configured tag, date/source, an optional kind: semantic sub-type and optional aliases: (Step 0b + note-quality rules), the body shaped by its typed slot, {links_section}, a mapped moc link, and useful ghost links. Serialize it as JSON data; do not interpolate it into obsidian create/append. The helper discovers the vault via argv-only CLI, then performs a contained atomic create. Backticks, quotes, and $(...) remain inert data. A conflict means another writer created the note — re-read instead of overwriting.
Note quality rules (details + tables in <mnemo-root>/references/tool-routing.md):
- Naming: never
#/.///.mdin the title — they break wikilinks (#→heading anchor) or the CLI (.→truncation). Sanitize beforecreate. Use—or space. - Fact-role title = a statement, not a topic (Matuschak «title as API» / Умэсао):
{fact_prefix}Redis fail-open keeps reads alive when cache is down, NOT{fact_prefix}Redis. - Insight-role note = non-trivial synthesis of ≥2 facts (new insight not present in either alone), not "linked two notes."
- Insight handed off with
cites:(e.g. from/mn:askcompounding): when the caller passes an insight plus acites:source list and a pre-built{links_section}, writecites: [{sources}]into frontmatter (right afterdate:) and use the caller's links block verbatim instead of generating a bare hub link. - Body follows the typed slot for its semantic type (Step 0b) — decision→Y-statement, gotcha→GIVEN/WHEN/THEN, principle·pain·stance→JTBD, fact·insight→claim+BLUF+evidence. Depth is distributed across atoms, never concentrated in one blob.
aliases:are retrieval keys, not content — add EN/RU synonyms or a short name to frontmatter when a future search would plausibly reach for a different term than the title (aliases: [save redesign, редизайн save]). A polyglot vault: an English-titled note should still answer a Russian query. Add when obvious; never fabricate — a key, not authored prose.- Two link layers: inline with context in the body («contradicts [[X]]», «builds on [[Y]]») +
{links_section}for MOC/nav. A bare link without context is noise. - Short project names (
[[Diadoc]],[[BTS Holding]]) need a hub note — Obsidian doesn't resolve bare links via alias (by design). If[[ShortName]]is referenced and noShortName.mdexists, create it: a one-liner redirecting to the note type mapped bytaxonomy_roles.moc. - Staleness is type-driven, not stamped here. The
dateyou write is the review anchor —healthderives review cadence from the note'stype(configreview.staleDays), so you don't add a review date. Exception: for a fast-rotting fact (a volatile API quirk, a "current as of" price) add an optionalttl: <days>to the frontmatter to age it faster than its type default. Don't addreviewed:— that's the snooze health/the user stamps later. See<mnemo-root>/references/config-schema.md→ "Optional per-note frontmatter". - Load-bearing
[[links]]go OUTSIDE code fences — a wikilink inside a```block is NOT parsed into the graph (by design), so it's silently lost to backlinks. Agents emit code blocks constantly — keep navigable links in prose. Full vault conventions (Bases-first computed indexes, schema self-policing, concurrent-edit safety):<mnemo-root>/references/vault-conventions.md.
Add to the mapped hub with an optimistic targeted insert:
python3 "<mnemo-root>/scripts/vault-write.py" <<< '{"action":"insert","vault":"{vault}","note":"{mapped hub note}","anchor":"{unique stable anchor copied from safe-read}","position":"after","content":"\n- [[{note name}]] — {why it belongs here}"}'
If the anchor is missing/non-unique or the note changes during publication, the helper fails closed; re-read and retry. Never fall back to inline CLI content.
On error: Log ⚠️ Obsidian: skipped (not connected), continue to next backend.
Step 2: claude-mem (Optional Semantic Search — cross-session recall)
Skip if: cascade.claude_mem.enabled is false. This is the default in new installs because many users intentionally disable claude-mem for CPU/RAM reasons.
Use the bundled helper — it auto-detects the claude-mem version for provenance, reads dynamic values as JSON from a single-quoted herestring, and sends the HTTP request without a shell. A summary containing quotes, backticks, or $(...) remains data. It also bakes in the v12.3.9 gotchas documented below:
python3 "<mnemo-root>/scripts/claude-mem-save.py" <<< '{"url":"{claude_mem_url}","type":"{type}","project":"{current project or general}","summary":"{one-line summary of what was saved}","note":"{note name if created}","vault":"{vault}"}'
API field name (v12.3.9): the request body key is text, not content. Earlier versions accepted content; as of v12.3.9 the API returns {"error": "text is required and must be non-empty"} if you send content. Confirmed during v0.7.3 smoke test — verified in claude-mem source.
v12.3.9 metadata gotcha — custom fields are dropped silently. POST returns {"success": true, "id": ...} but the stored observation only persists text + API-generated fields (type, title, narrative, facts, concepts, content_hash, created_at, ...). Custom metadata.* entries (including project, obsidian_note, obsidian_vault, claude_mem_version) are not retrievable from the observation record. The project field on the stored record is forced to the calling plugin's project (claude-mem), not metadata.project.
Workaround (used above): embed the key provenance fields (note name, vault, CM version) directly into text as a bracketed tail. Losing structured filtering hurts less than losing the data entirely — full-text search still finds the provenance. Keep the metadata: {...} block in the POST anyway so recovery is automatic once upstream fixes drop-silent behavior. Track upstream: thedotmack/claude-mem — search for metadata / project override.
Why obsidian_note + obsidian_vault: once upstream restores metadata persistence, claude-mem search results can link back to the full Obsidian note. Future /mn:ask --deep will show a direct wikilink alongside the observation.
Why claude_mem_version: v11.0.1 disabled semantic-inject by default, v12.0.0 introduced the file-read gate. Tagging observations by version lets retrieval logic filter legacy entries when needed.
On error: Log ⚠️ claude-mem: skipped (port {port} not responding), continue. Do not start ChromaDB or the claude-mem worker automatically.
Step 3: Runtime-owned memory (Claude write; Codex read-only)
Skip if: cascade.memory_dir.enabled is false — or this is an actionable rule (it goes to Step 3.5; .claude/rules/ supersedes a memory/ copy, never write both).
Only consider this layer if the information prevents the coding agent from making errors in future sessions and is not an actionable rule:
- Gotchas, commands, conventions
- NOT business context (that's Obsidian's job)
Claude Code: write only to the active auto-memory directory already exposed by Claude. Honor ${CLAUDE_CONFIG_DIR:-~/.claude}/settings.json → autoMemoryDirectory; otherwise use the repository-derived projects/<project>/memory/ location. If autoMemoryEnabled or CLAUDE_CODE_DISABLE_AUTO_MEMORY disables it, skip. Never write ./memory/ in the repo.
Codex: skip this write. ${CODEX_HOME:-~/.codex}/memories/ is generated state owned by Codex; it may be inspected/read for recall, but mnemo must not create, edit, append, or maintain its index manually. Obsidian is the durable user-authored save surface in Codex.
How to write — keep the index lean (autodream discipline):
Claude auto memory is two layers: topic files (the detail) + MEMORY.md (a lean index — a table | File | Read when… |, one short "Read when…" row of recall triggers per topic, ≤~200 chars). Never dump prose into MEMORY.md — a bloated index gets truncated on load and old entries become invisible.
- Write the detail to a topic file (
{topic}.md) — create or update it. - Add/refresh one thin index row in
MEMORY.mdpointing to it (link + "Read when…" triggers: names / IDs / PR# / domain terms). Never a paragraph. - If
MEMORY.mdlinks aMEMORY-archive-index.mdat the top, aged/older rows live there — add aged rows there (not the lean index) and read it when recalling old context.
The index is periodically re-slimmed by autodream (memory consolidation). Resolve that cross-project guide under ${CLAUDE_CONFIG_DIR:-~/.claude}/memory/autodream-principles.md.
On error: Log ⚠️ memory/: skipped (directory not found), continue.
Step 3.5: .claude/rules/ — actionable path-scoped rules (auto-inject)
Skip if: cascade.project_rules.enabled is false (default true).
Fires only for an actionable rule (Step 0) — never for recall items. The test: would this rule have prevented an error if it had auto-surfaced the moment the agent opened the relevant file? Yes → here. "What we did / why" → recall memory (Steps 1-3), not here.
Why .claude/rules/ and not CLAUDE.md: Claude Code natively auto-loads .claude/rules/*.md. A file with paths: frontmatter loads only when the agent touches a matching file (path-scoped, zero idle-context cost); a file without paths: loads every session (always-on). It is the granular evolution of the old "dump a rule into CLAUDE.md" branch (Step 4). Docs: https://code.claude.com/docs/en/memory.md (§ "Path-specific rules", "User-level rules"). The load trigger is paths: — not description: (that field is for humans skimming the dir; it does not affect loading).
1 — Pick the level:
- Rule is specific to this repo (names its files, domains, deploy quirks) → project
.claude/rules/(committed in the repo). - Rule is generic / cross-project (a language convention, a git habit, a universal gotcha that applies in every repo) → user-global
~/.claude/rules/(auto-applies on every project on this machine).
2 — Find or create the target file (project shown; use ~/.claude/rules/ for global):
ls .claude/rules/*.md 2>/dev/null
- Read each file's
paths:/ domain. Append the rule to the file whose scope covers the code it governs (under the matching section, surgical insert). - No file matches by meaning → create a new
<domain>.md. Don't wedge an unrelated rule into the nearest file (that's the wrong-abstraction smell at the doc level). .claude/rules/doesn't exist → create the dir and the file. A first rule bootstraps the convention; do not silently fall back to CLAUDE.md just because the folder is missing.
3 — Frontmatter for a new file:
---
paths:
- "src/<area>/**" # globs for the files this rule governs → path-scoped auto-load
- "tests/<area>/**"
description: "<one line, for humans skimming .claude/rules/ — NOT a load trigger>"
---
Omit paths: only for an always-on rule (rare in a project — it costs context every session). A generic global rule usually omits paths: (it should always apply).
4 — Write it. These are plain repo / dotfiles outside the Obsidian vault → use Write/Edit, never the Obsidian CLI/MCP (no vault graph to join, and obsidian create content= would shell-expand backticks). Match the file's existing section style; append surgically, don't reformat neighbors. Verify the YAML after writing — a broken-indent paths: entry silently drops the whole file from auto-load (real incident: a 0-indent list item under paths: made the rule never load).
Codex / AGENTS.md gotcha: Codex does not read .claude/rules/ — only AGENTS.md (nested, 32 KiB hard limit, silent truncate past it). If the project has an assemble-AGENTS build-step (rules → AGENTS.md), the rule reaches Codex on rebuild — run it and confirm wc -c AGENTS.md stays < 32768. No build-step + Codex devs on the repo → also surface the critical rule into AGENTS.md by hand.
On error / not applicable: Log ⚠️ .claude/rules: skipped (recall item / cascade.project_rules disabled), continue.
Step 4: CLAUDE.md (Only critical error-preventing rules)
Skip if: cascade.claude_md.enabled is false (default) — or the item is an actionable rule already handled by Step 3.5 (cascade.project_rules.enabled true). This branch is the fallback, reached only when Step 3.5 declined (e.g. project_rules disabled).
Only write here if the rule is:
- 1-2 lines max
- Violation would cause a real error or bad behavior
- Not already covered by Obsidian or memory/
This is almost never needed. Most things go to Obsidian + claude-mem. For any rule tied to code, prefer Step 3.5 (.claude/rules/) — CLAUDE.md is the fallback only when you genuinely can't use a rules file (and Step 3.5 already creates the dir/file when missing, so that's rare).
Step 5: Report
💾 Memory saved:
Content: "{short summary}"
Type: {semantic type} → taxonomy `{mapped type key}`
Backends:
1. Obsidian ✅ → "{mapped prefix}{title}" in {mapped hub name}
2. claude-mem ⏭ skipped (disabled)
3. runtime memory ⏭ {Claude: skipped/not error-preventing | Codex: read-only generated state}
3.5 .claude/rules ⏭ skipped (recall item, not an actionable rule)
4. CLAUDE.md ⏭ skipped (not critical rule)
For an actionable rule, the rule branch is the only write (one kind → one home):
💾 Memory saved:
Content: "after touching sign_epl.py, gate the Kontur call on the flag"
Type: actionable rule
Backends:
1. Obsidian ⏭ skipped (rule, not recall)
2. claude-mem ⏭ skipped (rule, not recall)
3. runtime memory ⏭ skipped (rule → .claude/rules supersedes a memory copy)
3.5 .claude/rules ✅ → .claude/rules/te5-frontend.md (path-scoped) — appended
4. CLAUDE.md ⏭ skipped (Step 3.5 handled it)
Or with failures:
💾 Memory saved (partial):
1. Obsidian ⚠️ skipped (not connected — restart Obsidian)
2. claude-mem ⏭ skipped (disabled)
3. Claude auto memory ✅ → {active auto-memory directory}/topic.md updated
⚠️ Restart Obsidian, then run this `save` skill again (`/mn:save` in Claude Code, `$mnemo:save` in Codex) to complete sync.
After a new note is created (not a plain append/update, not an actionable-rule write), offer to run the canonical connect skill using the current runtime's explicit syntax: "New note created — run /mn:connect (Claude Code) or $mnemo:connect (Codex) to surface related notes and cross-link it?" After an explicit user save, wait for their choice. If this was a proactive save mid-task, delegate to connect immediately to surface suggestions; connect still never applies links without confirmation. A fresh note that never gets connected becomes an orphan; this closes the save→connect loop without changing the original workflow.
Decision Matrix
| Information type | Obsidian | claude-mem | Claude auto memory | Codex generated memory | .claude/rules/ | CLAUDE.md |
|---|---|---|---|---|---|---|
| Fact (atomic) | ✅ mapped fact role | Optional | ❌ | read-only | ❌ | ❌ |
| Insight (synthesized) | ✅ mapped insight role | Optional | ❌ | read-only | ❌ | ❌ |
| External source | ✅ mapped source role | Optional | ❌ | read-only | ❌ | ❌ |
| Decision | ✅ mapped fact role | Optional | ✅ if prevents errors | read-only | ❌ | ❌ |
| Principle / pain / stance (how-the-user-thinks, W4) | ✅ mapped insight role (kind: set) |
Optional | ❌ | read-only | ❌ | ❌ |
| Gotcha | ✅ mapped fact role | ✅ | ✅ | read-only | ❌ (path-scoped + code-tied → classify as rule, row below) | rare |
| Command/convention | ✅ mapped fact role | ✅ | ✅ | read-only | ❌ | ❌ |
| Actionable path-scoped rule | ❌ | ❌ | ❌ | read-only | ✅ primary | ⚠️ fallback only |
Gotchas
Common failures in <mnemo-root>/references/gotchas.md. Tool-routing rationale in <mnemo-root>/references/tool-routing.md. Skill-specific rules:
- Graceful degradation is the point — never fail completely. If Obsidian IPC is hung, skip it and save to enabled fallback backends. The user can retry when Obsidian recovers.
- Don't duplicate Obsidian content in Claude auto memory — different audiences. Obsidian is for the user (cite-able, searchable in vault); Claude auto memory is only for error prevention. Codex memory is never a manual write target.
- claude-mem is optional — many users won't have it running on :37777. Skip silently, don't warn.
- CLAUDE.md is almost never written to — only 1-2 line rules that prevent actual errors. Target: <120 lines total to preserve prompt budget.
- Recall vs actionable rule is the routing fork (Step 3.5) — recall ("what / why") is fetched on demand; an actionable rule ("never X / always Y" tied to code) must auto-surface when the agent opens the file. Misrouting a rule into recall = it never fires when it matters. Misrouting recall into
.claude/rules/= idle context bloat. When unsure, ask "would this have prevented an error by appearing at the right file?" .claude/rules/≠ CLAUDE.md — native path-scoped auto-load.paths:is the load trigger (notdescription:); nopaths:= always-on. A new rule creates the<domain>.md(and the dir) when none matches — don't wedge it into an unrelated file. Verify the YAML: a broken-indentpaths:entry silently drops the whole file from loading..claude/rules/files live outside the vault — like runtime memory files, never[[wikilink]]them and never write them via the Obsidian CLI/writer. PlainWrite/Edit.- Codex is blind to
.claude/rules/— it reads onlyAGENTS.md(nested, 32 KiB silent-truncate). For a repo with Codex devs, route the critical rule into the AGENTS.md build-step (or by hand) too, and keepwc -c AGENTS.md < 32768. - Depth = structure, not volume (Step 0b) — split deep material into atomic, claim-titled notes with typed slots; never one exhaustive blob (unretrievable point-precisely + drifts into the rejected auto-ingest fork). Every typed note needs a claim title +
because(hard-gated fordecision/ actionable rule /principle·pain·stance).principle/pain/stancecapture the user's mental model only as user-authored/confirmed atoms — never an agent-invented dossier. - Always check duplicates before creating Obsidian notes — clobbering a note silently is worse than any write latency.
- Ghost notes generously — wrap entities in
[[wikilinks]]even when the target doesn't exist yet. Enables future entity discovery. - Never
[[wikilink]]a runtime-memory file — use inline code — runtime memory and project files (CLAUDE.md,AGENTS.md) live outside the Obsidian vault graph. A wikilink to them becomes a permanent unresolved ghost. If a real vault counterpart exists, link that mapped taxonomy note instead. - Mapped hub link mandatory for every typed Obsidian note.