Synthesis of a day of research into how to get code-level and concept-level knowledge graphs that link together, are traversable by Claude Code, and handle updates — plus whether Obsidian is still the right tool. Verdict: stay on Obsidian, keep markdown as the source of truth, derive two disposable graphs, and build one reconciler. Backed by four dated surveys: code-graph-survey-2026-09-07, memory-graph-survey-2026-09-07, pkm-survey-2026-09-07, ruflo-evaluation-2026-09-07.

For Agents

Read this before proposing anything graph-shaped for this vault or for ai-project-brain. Five facts that older notes and older blog posts get wrong:

  • claude-mem has been disabled since 2026-03-15. Installed at v10.5.2 but absent from enabledPlugins. Its 1326 observations are all from March, 60 pending messages are failed, and its configured model is claude-sonnet-4-5. Do not treat it as a live memory store. Do not read from it.
  • The documenter hook is orphaned and inert. ~/.claude/hooks/obsidian-documenter-trigger.sh is referenced only in claudeconf, not in live settings, and its COUNTER_FILE is keyed on $$, so the threshold can never fire. Documentation reaches this vault only through model compliance with CLAUDE.md, never automatically.
  • Kuzu is archived (2025-10-10, Apple acquisition). Use LadybugDB if an embedded graph database is ever needed. Graphiti deprecated its embedded Kuzu backend as a result. The architecture below needs no graph database at all.
  • mem0 removed graph memory from open source in v3 (April 2026).
  • ruflo is rejected for this purpose. No code parser, no node table, duplicate facts accumulate forever. See ruflo-evaluation-2026-09-07. Its init appends to the global ~/.claude/CLAUDE.md and cleanup never reverses that.

The one thing worth building is the reconciler — a job that resolves note code_refs against a current code index and flips status: stale on misses. Nothing off the shelf does this for this vault’s conventions.

Question and method

How to get code-level and concept-level knowledge graphs that link together, are traversable by Claude Code, handle updates, and whether Obsidian is still the right tool. Added mid-research: offload Claude Code to telep-mainframe; evaluate ruflo.

Method: two read-only audits (vault, knowledge stack), one probe of prior art (ai-project-brain, graphtest), four web surveys (code graphs, concept and temporal graph memory, PKM tools, ruflo), one Claude Code docs pass on remote operation. All star counts and dates verified on 2026-09-07.

Verdict

  1. Stay on Obsidian. No alternative offers typed edges, plain text, and an agent API together. The gap is a machine-readable layer, not the app.
  2. Architecture: markdown is the source of truth. Two derived, disposable graphs (concept index, code index) joined by one anchor (structured code references in note frontmatter).
  3. Update handling is a reconciliation job, not a database feature. Resolve every note’s code anchors against the current code index and flip status: stale on misses. Nothing off the shelf does this for the vault’s conventions; it is the one thing worth building.
  4. Move Claude Code’s home to telep-mainframe. It collapses the cross-Mac state problem (settings, auto-memory, transcripts, hooks, indexes) into one machine that already hosts telep-kb and alexandria.
  5. Skip ruflo. Stop building a graph database app; narrow ai-project-brain to the reconciler or drop it.

What exists today

Vault (~/levandor_obsidian)

MeasureValue
Notes / project folders1051 / 44
Wikilinks16,788 (598 bare heading refs)
Notes with frontmatter96.5%
Typed link fields, vault-wide21 (related 9, repo 8, parent 3, superseded_by 1)
Code identifiers in prose43,707 backticked, 5,116 paths, 2,729 file:LINE refs (2,617 unqualified)
Files with exactly one commit606 of 1079
Notes with updated: / status/outdated95 / 5
Near-duplicate title pairs (excluding handovers)51
Dangling link targets103
Name collisions[[LOG]] x158 and [[TOPICS]] x161 against 43 files each; [[vuer_oss]] x189 against 2
Query pluginsnone (Dataview, Breadcrumbs, Juggl absent; Bases on with 0 .base files)
Syncgit only via obsidian-git; three author identities; pull --rebase failing on 19 modified + 6 untracked paths at audit time
Data lossauto-backup commit 93aa1d7 (2026-08-31 03:33) deleted projects/alexandria/ (5 notes, ~3000 lines); recoverable from 93aa1d7^

Spot-check of six old file:LINE references: four hold, one line drifted to properties: {, one file (vuer_cv/server/appcache.py) is gone.

Documenter compliance: tags 1016/1051, ## Related 714/1051, > [!info] For Agents 606/1051, project/ tag missing on 295 of 999 project notes. The most-violated rule is search-before-create (plan/design/complete triads, dated forks).

Knowledge stack

SystemState on 2026-09-07
Obsidian vaultthe only live cross-machine store; written by obsidian-documenter on model compliance, no hook
Auto-memory (~/.claude/projects/*/memory)live, per-project and per-machine; 30 project dirs; invisible across repos
claude-memdead since 2026-03-15: installed v10.5.2, not in enabledPlugins, 1326 observations all March, 60 pending messages failed, configured model claude-sonnet-4-5
Documenter hook (~/.claude/hooks/obsidian-documenter-trigger.sh)orphaned (referenced only in claudeconf, not live settings) and inert (COUNTER_FILE keyed on $$, threshold can never fire)
~/.claude/settings.jsonreal file, drifted from dotfiles (15 keys vs 6); only CLAUDE.md and RTK.md are symlinked
AgentOps .agents/~90 scaffolded dirs, one with content (railscale); ao reports false in environment.json
telep-kb MCPQdrant collections manuals (hybrid) and notes (vault, semantic only) on telep-mainframe; re-embeds on note content hash; blind to code edits; fail-closed on bad YAML silently drops project units
Transcripts1175 JSONL files, 933 MB, unindexed
Repos43 (Rust-heavy), 4 with CLAUDE.md, none with ADRs

The core gap

Only one file-to-knowledge edge exists in the whole stack (claude-mem files_modified), and it is inert data in a dead database. A rename invalidates zero notes anywhere.

Prior art

  • ai-project-brain (created 2026-09-07, /Volumes/bandi/coding): workspace scaffold on grafeo 0.5.42 (embedded, GQL, vector/text/hybrid indexes, CDC, file persistence). Does not compile (Path field, untyped field in repo.rs). No schema, no ingest, no CLI. Nested .git dirs from cargo new. Its vault note says “there isn’t any architecture yet”.
  • graphtest (graphanah, paused 2026-03-10): rustc_driver HIR walker with typeck-resolved references (qpath_res, type_dependent_def_id). Real name resolution, Rust only, nightly-pinned, no incremental mode, containment-only edges, TSV output. Left mid-session. Superseded by rust-analyzer scip, which gives stable symbols without a nightly pin.
  • alexandria (10 crates, ort + tokenizers + qdrant-client, deployed on telep-mainframe) and dionysus: vector and full-text systems. Local embedding infrastructure already exists on the server.

Landscape

What changed and invalidates older advice: Kuzu archived 2025-10-10 (Apple acquisition; maintained fork is LadybugDB). mem0 removed graph memory from open source in v3 (April 2026). Graphiti deprecated its embedded Kuzu backend. SCIP moved to the neutral scip-code org. GitHub stack-graphs archived. Sourcegraph Cody Free/Pro killed; no self-hostable OSS Sourcegraph. CodeQL CLI licence bars private repos. GitNexus is PolyForm Noncommercial.

Concept layer

Full detail in memory-graph-survey-2026-09-07 and pkm-survey-2026-09-07.

ToolFitNotes
Basic Memory (3,878 stars, AGPL, v0.23.2)adoptmarkdown is truth, SQLite disposable; ## Relations - type [[Target]] typed edges; MCP + bm tool CLI + HTTP; official Obsidian integration and Claude Code hooks; no temporal model; pre-1.0, syntax broke once at v0.21.0
Obsidian CLI 1.12use in hookslinks, backlinks, unresolved, orphans, deadends, base:query format=json; needs the app running; exit code always 0, parse stdout
Basesuse for viewsone-hop filters over properties and links, JSON via CLI; no joins, no traversal
Breadcrumbs v4.15 (now michaelpporter/breadcrumbs)optionalhuman-facing typed navigation over the same frontmatter
Graphiti (30,657 stars)later, if everonly true bi-temporal model (valid_at/invalid_at); needs FalkorDB or Neo4j and several LLM calls per write; extractor ignores reference_time on backfill (issue #1489)
trace-mcp (169 stars, MIT, single maintainer)try, base tier onlyvault + code in one SQLite graph; decisions with valid_from/valid_until bound to path::Symbol#kind; decision-verification returns ok, symbol_missing, code_changed; mine_sessions over transcripts with zero LLM calls; Rust edges are essentially only imports, trait-impl edge is a no-op, method ids collide across trait impls; file moves not handled; avoid max tier (patches Claude Code’s system prompt)
Dataviewavoidstale since 2025-11-17; Bases cannot read inline fields
claude-memdropa flat session log with LLM compression per session, not a graph; known token and RAM blowups

Code layer

Full detail in code-graph-survey-2026-09-07.

ToolFitNotes
Serena (28,931 stars, MIT)adopt nowLSP live (rust-analyzer, typescript-language-server, Pyright); no index to keep fresh; find_symbol, find_referencing_symbols; name path is file-scoped; issue #1556: hardcoded heavy rust-analyzer options, 40 GB processes on big Rust workspaces
codebase-memory-mcp (42,518 stars, MIT, C)adopt when cross-repo memory is wantedsingle binary, SQLite, read-only openCypher subset with explicit errors; TESTS and FILE_CHANGES_WITH edges; ids <project>.<path>.<name>; commits .codebase-memory/graph.db.zst with merge=ours so the graph travels through git; Rust in its “Good 75-89%” tier; 75 KB README, writes agent config files
SCIP to SQLitebuild-it-yourself optionrust-analyzer scip . then scip expt-convert; Rust symbols encode crate and module path, so they survive line churn and file moves; rust-analyzer’s SCIP is “pretty limited” per maintainers; scip-typescript and scip-python quiet since late 2025
code-graph-rag (5,055 stars, MIT)later, on the mainframebest-engineered: published schema, re-index p50 194 ms, only tool with rename detection (symbols.renamed, dangling_callers); needs Memgraph + Qdrant in Docker; Cypher you can MERGE note nodes into
Graphify (115,470 stars)steal the idea onlywikilinks become edges, exports an Obsidian vault; untyped nodes, graph.json, deleted nodes linger
GitNexus, CodeQL, Joern, Potpie, CodeGraphContext, Aider repo-map, Nuanced, FalkorDB code-graphruled outlicence, no Rust, parsers deleted, line-keyed ids, dormant, or archived

ruflo (renamed claude-flow, 71k stars)

Skip. The graph is one graph_edges SQLite table with no node table and bare TEXT ids. No code parser anywhere; extraction is Claude reading files and writing prose. Every insert mints a new UUID then INSERT OR IGNORE with no unique constraint, so duplicate facts accumulate forever; last_reinforced is never updated. ruflo-knowledge-graph, ruflo-rag-memory, ruflo-ruvector, ruflo-agentdb are markdown prompt packs with zero code. Default search is a table scan; real HNSW “not scheduled” (issue #2922). init installs hooks on nine events, spawns a daemon, and appends to global ~/.claude/CLAUDE.md; cleanup never reverses the global edit. ruvector’s Cypher executor returns an empty vector by design. Worth borrowing: the 15-line k-hop recursive CTE shape if the index lands on SQLite. Full evidence in ruflo-evaluation-2026-09-07.

Markdown as truth. Two derived graphs, one anchor. Everything below is additive to the current vault and reversible.

1. Note conventions (the concept graph)

---
title: Queue drain timeout fix
type: decision              # decision | architecture | debugging | runbook | reference | index
project: polymarket-fetch
status: active              # draft | active | stale | deprecated | superseded | archived
date: 2026-09-07
updated: 2026-09-07
verified: 2026-09-07        # last date code_refs resolved cleanly
supersedes: "[[2026-06-01 queue backpressure design]]"
superseded_by:
amends:
code_refs:
  - repo: polymarket_fetch
    path: crates/engine/src/queue.rs
    symbol: engine::queue::Queue::drain
tags: [project/polymarket-fetch, type/decision]
---
## Relations
- depends_on [[Tokio runtime notes]]
- implements [[Queue backpressure design]]

Rules:

  • A fixed relation vocabulary: depends_on, implements, supersedes, superseded_by, amends, relates_to, caused_by, fixed_by.
  • Never edit a decision to reverse it. Write the successor, set supersedes, flip the old note’s status.
  • Revise a living note in place and bump updated. Do not fork dated copies.
  • deprecated means no replacement; superseded names one.
  • No Dataview inline fields.
  • Rename LOG.md and TOPICS.md to project-scoped names through Obsidian so links rewrite. This kills the [[LOG]] x158 and [[TOPICS]] x161 collisions.
  • One tag form per project (project/x; drop the flat duplicates).

Index: Basic Memory pointed at the vault gives the typed-edge index, build_context traversal, and MCP for free. Fallback if it misbehaves: a 50-line parser over frontmatter and ## Relations into SQLite.

2. Code graph (derived, disposable, per machine)

Now: Serena as the always-correct symbol layer. When cross-repo memory matters: codebase-memory-mcp per repo, artifact committed. If note rot needs automatic rename detection: code-graph-rag in Docker on telep-mainframe, both Macs pointing at it.

Design rule

Do not persist symbol identity in notes beyond the anchor. Re-derive the code graph. It is disposable by design; only the anchor is durable.

3. The anchor and the reconciler (update handling)

code_refs replaces bare lib.rs:176. Each ref is repo + path + qualified symbol; store the SCIP string when an indexer emits one. The reconciler runs on the server (SessionEnd hook or systemd timer):

  1. Resolve each code_refs entry against the current index (SCIP SQLite, codebase-memory-mcp SQLite, or Serena find_symbol).
  2. Missing symbol gives status: stale plus a symbol_missing callout. Symbol body hash changed since verified gives status: stale plus code_changed. Resolved sets verified: today.
  3. Run obsidian unresolved, orphans, deadends and write one hygiene note.
  4. A Stale notes.base view (status == stale) is the review queue.

This is 150 to 300 lines. It is the correct scope for ai-project-brain if it continues; grafeo is unnecessary for it, SQLite suffices. trace-mcp’s decision-verification does the same three verdicts for its own decision table and can be trialled first.

4. Session capture

Disable claude-mem for good. Transcripts are already on disk; mine them with trace-mcp mine_sessions (regex, zero LLM) or Basic Memory’s Claude Code hooks. Keep obsidian-documenter as the deliberate write path, with search-before-create enforced by giving it Basic Memory search_notes and the revise-or-supersede rule.

Offloading Claude Code to telep-mainframe

Recommended topology from current docs: install Claude Code on the server, move repos there (git, not mounts), run sessions in tmux over Tailscale SSH, enable Remote Control for claude.ai and phone. ~/.claude on the server becomes the single home.

  • Keep Logic2, FreeCAD, Fusion on the Mac as user-scope HTTP MCP servers over the tailnet — see tailnet-service-exposure-convention.
  • JetBrains: Gateway with the Claude Code plugin installed on the host; run a RustRover backend on the server rather than exposing the Mac’s rustrover MCP.
  • Hooks run where Claude Code runs, so the reconciler and vault indexer live on the server.
  • Routines cannot target a personal machine; use systemd timers with claude -p.
  • Auth: /login paste-code flow; credentials in ~/.claude/.credentials.json; unset ANTHROPIC_API_KEY. One subscription across your own devices is allowed.

Unverified and risky

Unverified in docs: VS Code Remote-SSH, iTerm tmux -CC to a remote tmux (see iterm2-tmux-cc-setup), file watching over network mounts. Risk: telep-mainframe’s nvidia_drm modeset crashes would take down the only Claude host — see 2026-08-31-nvidia-drm-host-crash-embedding-pass. Also Serena’s rust-analyzer memory issue on large workspaces.

Immediate hygiene (independent of the graph decision)

Not executed

None of the commands below were run. The vault working tree had 19 modified and 6 untracked paths at the time of writing, unrelated to this research, and git pull --rebase fails on them. Restoring projects/alexandria is the user’s decision and was deliberately left alone.

cd ~/levandor_obsidian
git stash list; git status --short          # inspect the 19 modified + 6 untracked first
git checkout 93aa1d7^ -- projects/alexandria/   # restore the 5 deleted notes
  • Symlink ~/.claude/settings.json into dotfiles like CLAUDE.md, so hooks and plugins stop drifting between machines.
  • Fix or delete ~/.claude/hooks/obsidian-documenter-trigger.sh (the $$ counter bug).
  • Remove claude-mem and episodic-memory from the plugin list, or re-enable claude-mem with an allowed model.
  • Add project/ tags to the 295 untagged project notes; merge flat and hierarchical tag forms.
  • Fix the vault .gitignore for .obsidian/plugins/*/data.json and set one git identity per machine.

Open decisions for the user

Five open decisions

These are not decided. Nothing below has been acted on.

  1. Adopt Basic Memory on the vault, or run the convention with a script only.
  2. Code layer: Serena now, codebase-memory-mcp next, code-graph-rag on the server only if rename detection proves necessary.
  3. ai-project-brain: narrow to the reconciler (Rust, SQLite, optional MCP), or drop and trial trace-mcp.
  4. Move Claude Code home to telep-mainframe: yes or no, and whether repos move.
  5. claude-mem: remove or revive.

Source surveys

Full reports with URLs, dates and comparison tables, filed as separate notes:

Plus the vault audit, stack audit, prior-art probe, and Claude Code remote-operation notes relayed by the subagents, which are summarised above and not filed separately.