21.2 KB · updated 2026-07-06 · md

v0.3.0.md

docs/release-notes/v0.3.0.md

Tesserae v0.3.0 — Prose-to-code links, polyglot code graph, live sync

<!-- translations:start -->

한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch

<!-- translations:end -->

Released 2026-05-24 · PyPI · GitHub release · pip install --upgrade tesserae==0.3.0

Tesserae 0.3.0 closes the loop between prose decisions and the code symbols that implement them, and does so across 21 languages instead of v0.2.0's Python-only foundation. The release ships three additive features with no breaking changes: a discusses edge pass that mints typed links from session findings (SessionInsight, Decision, Hypothesis, Todo, Question, Takeaway) to the matching code nodes; a tesserae project sync-code subcommand that imports colbymchenry/codegraph's tree-sitter SQLite index into Tesserae's typed ResearchGraph; and a default-on live sync-code SessionStart hook that keeps that import fresh as the user edits code. Together they make Tesserae the only PKM-AI memory tool in the 2026 landscape where "what did we decide about X?" returns the decisions, the sessions they were made in, and the exact functions / classes / routes that implement them — across the polyglot stack a real research codebase actually uses.

Table of contents:

  1. MD0 edges — link prose to code symbols (feature H)
  2. MD0 — CodeGraph adapter, 21 languages
  3. Live sync-code SessionStart hook (post-v0.3.0 on MD0)
  4. Upgrading from v0.2.0
  5. Strategic context

What it is

An opt-in post-compile pass that scans every session-finding body for mentions of code symbols that exist in the graph, and mints a typed discusses edge from the finding to each matching code node. The source kinds are the six session-finding types the agent harness emits — SessionInsight, Decision, Hypothesis, Todo, Question, Takeaway — and the target kinds are every code-node variant Tesserae now knows about: CodeFunction, CodeClass, CodeMethod, plus the v0.3.0-new CodeInterface, CodeTrait, CodeStruct, CodeEnum, CodeEnumMember, CodeTypeAlias, CodeVariable, CodeConstant, CodeRoute, CodeComponent, CodeField, CodeNamespace, and the CodeSymbol fallback.

The extractor is three-tier by precision, so the agent-harness can recover identifiers users actually wrote without flooding the graph with false-positives:

TierWhat it matchesAcceptance rule
Strong: backticked` MyClass frontend.render `Always accepted — the author marked it as code.
Strong: dottedfrontend.render, models.User.saveAccepted if any segment matches an index entry; rejected if any segment is a stopword.
Weak: bare identifierMyClass, render (no backticks, no dot)Accepted only if the identifier is present in the code-graph index, and only after stopword filtering (len, int, str, True, self, data, etc.).

Symbols defined in multiple files get same-name fanout: a bare-identifier mention of User in a session finding produces one discusses edge per matching CodeClass (per file), so a downstream graph_ppr seeded at the finding fans out to every plausible implementation rather than arbitrarily picking one.

A new MCP tool — find_code_symbol_mentions(node_id) — exposes the same extractor to the agent at query time. It takes any node (typically a session finding) and returns every code symbol the body mentions, with the tier that matched. This is useful when you want to ask "which symbols does this decision actually touch?" without re-running the compile pass.

How to use

Opt in via an environment variable, then compile:

export TESSERAE_INSIGHT_SYMBOL_LINK=true
tesserae project compile

Once enabled, every session finding that mentions a known symbol gains one or more outgoing discusses edges. From an MCP client:

// Surface the symbols a specific decision touches
{
  "tool": "find_code_symbol_mentions",
  "arguments": { "node_id": "decision-2026-05-22-switch-to-rrf-fusion" }
}

// → returns
// [
//   {"node_id": "code-function-tesserae.retrieval.hybrid.reciprocal_rank_fusion",
//    "tier": "backticked", "kind": "CodeFunction"},
//   {"node_id": "code-class-tesserae.retrieval.hybrid.HybridRetriever",
//    "tier": "dotted",     "kind": "CodeClass"}
// ]

Or follow the new edges with graph_ppr to pull the neighborhood of code symbols a cluster of decisions touches:

{
  "tool": "graph_ppr",
  "arguments": {
    "seed_node_ids": [
      "decision-2026-05-22-switch-to-rrf-fusion",
      "hypothesis-2026-05-23-embedding-helps-paraphrases"
    ],
    "top_k": 20,
    "edge_type_weights": { "discusses": 3.0, "references": 1.5 }
  }
}

When to enable

  • You have session history with real code discussion — multi-week project where Claude Code sessions actually reference functions/classes/routes by name, not just describe behaviour in prose.
  • You want ask answers to cite the implementing symbol rather than only the README that mentions it.
  • You're consuming the graph from another tool (LSP-style "go to implementation" from a decision node) and need a queryable edge, not a re-extracted-at-query-time guess.

If your project is mostly prose docs and PDFs with little Claude Code history, the pass adds compile time and very few useful edges — leave it off.

Where it lives

Core implementation: MD0. Tool registration: tesserae/mcp_server.py.

Caveats

  • The bare-identifier tier is deliberately conservative — it requires an index hit and a non-stopword. Identifiers that are too common (run, get, set) will still pass the stopword filter but get drowned out by fanout to many files; this is a known trade-off, not a bug.
  • Same-name fanout is per-kind: a User mention that matches both a CodeClass and a CodeRoute produces edges to both. If you want only the class, filter at query time on target.kind == "CodeClass".
  • The pass runs after sync-code (or ingest-code) — if you enable feature H without a populated code graph, it has nothing to link against and produces zero edges. Compile order is handled automatically; no manual sequencing required.
  • Stopword list is global, not per-language; a Rust project that legitimately wants Vec as an identifier already works (Vec is not in the stopword set), but project-local overrides are a planned v0.4 follow-up.

2. tesserae project sync-code — CodeGraph adapter, 21 languages

What it is

A new CLI subcommand that imports an external colbymchenry/codegraph SQLite index into Tesserae's typed ResearchGraph. CodeGraph is a tree-sitter-based polyglot code-graph extractor with its own MCP server; the adapter reads its .codegraph/codegraph.db, translates each row into a Tesserae ResearchNode, and writes .tesserae/code-graph.json so the next tesserae project compile picks it up — exactly like v0.2.0's ingest-code output, but populated from a much wider language base.

Language coverage jumps from 1 to 21: TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Swift, Kotlin, Scala, Dart, Svelte, Vue, Liquid, Pascal/Delphi, Lua, and Luau. (The v0.2.0 tesserae project ingest-code stdlib-ast extractor still works unchanged for users who want a Python-only path with no Node-runtime dependency.)

To represent what tree-sitter actually surfaces across those languages, v0.3.0 adds 14 new ResearchNodeType variants:

New node kindWhat it represents
CodeInterfaceTS/Java/C# interface, Swift protocol
CodeTraitRust trait, Scala trait
CodeStructC/C++/Rust/Go/Swift struct
CodeEnum, CodeEnumMemberEnum and its variants (Rust, Swift, TS)
CodeTypeAliastype X = … (TS/Rust/Swift), typedef (C/C++)
CodeVariable, CodeConstantTop-level let/const/var, language-appropriate
CodeRouteHTTP route handler (framework-specific extractors)
CodeComponentSvelte/Vue component, React FC, etc.
CodeFieldStruct/class field
CodeParameterFunction/method parameter (when CodeGraph emits it)
CodeNamespaceC++ namespace, C# namespace, TS namespace
CodeSymbolFallback for any kind we haven't typed yet

…and 8 new edge types: implements, exports, references, instantiates, overrides, decorates, type_of, and returns. These edges map directly onto what CodeGraph's resolver computes per language; Tesserae's existing calls, imports, inherits_from, contains, and declared_in edges remain unchanged and continue to be populated.

The single most important design choice in the adapter is the id_seed strategy. CodeGraph's own row id embeds start_line, so the same symbol gets a new id every time you add a blank line above it — fatal for downstream edges that store the id. The adapter discards CodeGraph's id and reseeds Tesserae's id as f"{file_path}:{kind}:{qualified_name}". Result: a symbol's Tesserae id survives line-shifting edits unchanged, and feature H's bare-identifier matches in session findings keep resolving to the same node across many compiles.

How to use

# One-shot: read .codegraph/codegraph.db, write .tesserae/code-graph.json,
# then the next compile picks it up.
tesserae project sync-code

# Explicit project root + custom DB / output paths
tesserae project sync-code \
  --project /path/to/repo \
  --db /path/to/.codegraph/codegraph.db \
  --output /path/to/.tesserae/code-graph.json

# Watch loop — re-run sync-code whenever the DB changes.
# Useful if you don't want the SessionStart hook in feature 3 but still
# want the code graph to stay fresh while you edit.
tesserae project sync-code --auto-sync

A typical end-to-end flow on a new project:

# 1. Have CodeGraph index the repo (its own CLI; one-time setup)
npx codegraph index .

# 2. Pull that into Tesserae's typed graph
tesserae project sync-code

# 3. Normal compile picks up code-graph.json and (if enabled) runs feature H
export TESSERAE_INSIGHT_SYMBOL_LINK=true
tesserae project compile

# 4. Now ask questions that span prose and code
tesserae project ask "Which functions implement the RRF fusion decision?"

When to enable

  • Your project is anything other than pure Python, or pure Python plus one Node frontend.
  • You already use CodeGraph for its MCP server's "find references" / "find implementations" — the adapter lets the same index power Tesserae's typed graph at zero extra extraction cost.
  • You want route- or component-level granularity (CodeRoute, CodeComponent) in addition to functions and classes, which the v0.2.0 ingest-code does not produce.

If you're Python-only and don't want a Node runtime in your toolchain, tesserae project ingest-code (v0.2.0) still works and produces a compatible code-graph.json.

Where it lives

Core implementation: MD0. CLI wiring: tesserae/cli.py (the sync-code subparser, around line 897). The v0.2.0 ingest-code subcommand at tesserae/code_graph_extractor.py is unchanged.

Caveats

  • Requires a populated .codegraph/codegraph.db (CodeGraph's own one-time npx codegraph index . step). The adapter fails fast with an actionable error message if the DB is missing.
  • CodeGraph's resolver is best-effort across 21 languages; expect occasional unresolved references in pathological generics or macro-heavy code. The adapter records what CodeGraph emits and does not attempt to re-resolve.
  • Some node kinds (CodeParameter, CodeField) are emitted only when CodeGraph's per-language extractor surfaces them — coverage varies by language. The fallback CodeSymbol kind catches anything tree-sitter sees that we haven't typed yet, so nothing is silently dropped.
  • Atomic writes use PID + random suffix for the temp file (not a shared .tmp) so two concurrent sync-code invocations don't collide — this matches the same fix that recently landed in the batch manifest writer.

3. Live sync-code SessionStart hook (post-v0.3.0 on main)

What it is

A small SessionStart hook in the Tesserae plugin that closes the "code graph that keeps updating" loop. CodeGraph's MCP server already auto-watches the filesystem and keeps .codegraph/codegraph.db fresh as you edit — but Tesserae's .tesserae/code-graph.json only refreshed when you ran sync-code by hand. This hook backgrounds tesserae project sync-code --project <root> every time Claude Code opens a session, but only if the SQLite DB is newer than the derived JSON.

Default-on; opt-out per project via .claude/tesserae.local.md frontmatter:

---
hooks:
  sync_code_on_start: false
---

The hook skips silently when any of the following holds, so it never adds noise to projects that don't use CodeGraph:

  • The project doesn't use CodeGraph (no .codegraph/codegraph.db).
  • .tesserae/code-graph.json is already at-or-newer than the DB.
  • The tesserae binary isn't on PATH.
  • Another sync-code is already running for the same project root (a pgrep-based re-entry guard mirroring the SessionEnd compile guard that landed in 0a3d35f).
  • The user opted out via sync_code_on_start: false.

When it does fire, you see one line in the session header:

  ⟳ syncing code-graph from CodeGraph (background)

…and the actual sync-code invocation runs detached (setsidnohup → bare & fallback), with output captured to .tesserae/.session-start-hook.log so you can inspect what happened without it leaking into the Claude session transcript.

This hook landed post-v0.3.0 on main (PR #11, commit 395e9fb) and will ship in the next compile / plugin marketplace publish; v0.3.0 PyPI itself doesn't bundle it because the hook lives in the plugin, not in the tesserae Python package.

How to use

If you installed the Tesserae plugin via the Claude Code marketplace, you already have it — the hook is on by default. To verify it ran:

tail -n 20 .tesserae/.session-start-hook.log

To turn it off for a single project (e.g. a Python-only repo where ingest-code is preferred):

mkdir -p .claude
cat > .claude/tesserae.local.md <<'EOF'
---
hooks:
  sync_code_on_start: false
---

This project uses `tesserae project ingest-code` (Python AST) instead of
the CodeGraph adapter, so the live sync-code hook is disabled.
EOF

To force a one-off sync without waiting for a new session, just run tesserae project sync-code directly — the hook and the manual command share the same pgrep guard, so they won't double-run.

When to enable

  • You want the "keeps updating" property without remembering to re-run sync-code after each edit batch.
  • Your editor sessions are short and frequent (open a session, ask a question, close) — the hook makes sure each new session sees a current code graph.
  • You're already running CodeGraph's MCP server with file-watching — the hook just propagates that freshness into Tesserae.

Leave it off when you're in a tight inner loop and don't want any background work spawned per session, or when the project doesn't use CodeGraph at all (the hook silently no-ops in that case, but explicit opt-out is cleaner).

Where it lives

  • Hook entry point: MD0
  • Setting parser: MD0 — the read_plugin_setting table gained a sync_code_on_start=true default.
  • Re-entry guard: same pgrep -f "tesserae project sync-code.*${project_root}" pattern as the SessionEnd compile guard (hooks/session-end.sh).

Caveats

  • The hook only fires on SessionStart, not on every file save. If you want sync-code to run continuously while you edit (without opening a new Claude session), use tesserae project sync-code --auto-sync from feature 2 in a separate terminal.
  • mtime comparison uses BSD stat -f %m (macOS) → GNU stat -c %Y (Linux) → [[ A -nt B ]] fallback. On exotic filesystems with low-resolution mtimes, you may occasionally skip a sync that ran less than a second after a CodeGraph index — the next session will catch it.
  • The background spawn uses setsid when available, nohup next, then a bare &. On extremely locked-down shells where none of these detach the process, the sync will still run but may block session start by a few seconds. Open a terminal-friendly shell or set sync_code_on_start: false in that case.

Upgrading from v0.2.0

pip install --upgrade tesserae==0.3.0

That's the whole upgrade — v0.3.0 is additive with no breaking changes. Default tesserae project compile behaviour is unchanged; the new pieces are opt-in and discoverable.

Environment variable for the new opt-in pass (feature 1):

# Enable the post-compile prose-to-code discusses-edge pass (feature H)
export TESSERAE_INSIGHT_SYMBOL_LINK=true

New CLI subcommand to add to your muscle memory (feature 2):

tesserae project sync-code             # one-shot CodeGraph → Tesserae import
tesserae project sync-code --auto-sync # watch loop

The v0.2.0 tesserae project ingest-code subcommand still works unchanged — pick sync-code if your project uses anything beyond Python, pick ingest-code if you don't want a Node-runtime dependency.

New MCP tool the agent now has available (feature 1):

  • find_code_symbol_mentions(node_id) — at-query-time symbol extraction over any node body.

New plugin setting (feature 3, post-v0.3.0 on main):

# .claude/tesserae.local.md
---
hooks:
  sync_code_on_start: true   # default; set to false to disable the live sync hook
---

Everything else — graph_ppr, hybrid search_nodes, embedding_status, list_communities, fresh_insights, community summaries, decay scoring, supersedes, schema-drift, slash commands, the wiki / Obsidian projection — is unchanged from v0.2.0.


Strategic context

Tesserae v0.2.0 laid down typed code-graph nodes and the prose-side primitives (decay, supersedes, community summaries). v0.3.0 is the release where the two halves connect: feature H mints typed discusses edges from decisions and hypotheses to the symbols they touch, and the CodeGraph adapter makes those symbols cover the polyglot stack a real research codebase actually uses — TypeScript, Rust, Go, Swift, and 17 more — instead of just Python.

The wave-1 PKM-AI landscape we surveyed before this milestone has no other tool that does this. Cursor Memories is a per-project text bag — no graph, no typed edges. Claude CLAUDE.md is one flat markdown file. Cline memory-bank is a directory of markdown files, no typed extraction. Aider CONVENTIONS.md is a single prompt-prefix file. None of them link a prose decision to the function or class that implements it; none of them know that a Decision and a CodeRoute are different kinds of thing. v0.3.0's discusses edge across 21 languages of typed code nodes is the wedge — and the live SessionStart hook is what makes that wedge feel like a property of the environment rather than a workflow the user has to remember.

The closest peer in the broader GraphRAG space — Microsoft GraphRAG — has typed entities and community summaries (which Tesserae v0.2.0 mirrors) but no code-graph layer at all; it's a corpus-RAG system, not a project-memory compiler. HippoRAG 2's PPR primitive (Tesserae v0.2.0) and Mem0's memory operations (Tesserae v0.2.0 supersedes) round out the prose-side state of the art. v0.3.0 adds the dimension none of them touch: typed edges from the agent's own findings into typed nodes representing the code those findings are about, in the languages real codebases are actually written in.

See also: