Schema migration v56 (code_traversal_cache_v0_34):
- new table: code_traversal_cache (id, symbol_qualified, depth,
source_id, response_json JSONB, max_chunk_updated_at, xmin_max,
cluster_generation, computed_at)
- unique index on (symbol_qualified, depth, source_id)
- secondary index on source_id for cheap source-scoped clears
D3 — generation-counter cache invalidation. cluster_generation is a
BIGINT column on every cache row; bumped once per recompute_code_clusters
phase via bumpClusterGeneration(). Cache rows referencing stale
generations naturally miss on read. Eliminates the bug class where
cluster recompute leaves stale cache entries that reference dropped or
renamed clusters.
D8 — destructive-guard parity. clearTraversalCache requires either
source_id OR all_sources=true. Without either it throws. Mirrors v0.26.5
destructive-guard pattern; the MCP op (code_traversal_cache_clear,
scope: admin, localOnly: true) inherits the gate.
- src/core/code-intel/traversal-cache.ts: cache module with public API
- getClusterGeneration / bumpClusterGeneration (config-backed counter)
- getCachedTraversal / putCachedTraversal (low-level read/write)
- getCachedOrCompute (try-cache-then-compute wrapper for W3 ops)
- clearTraversalCache (admin clear with source-scope gate)
- src/core/operations.ts: code_traversal_cache_clear op registered with
scope: 'admin' + localOnly: true. Dry-run aware; resolves source_id
from params or ctx.
v0.34.0.0 scope: cache writes use xmin_max=0 sentinel (no snapshot
isolation). REPEATABLE READ + xmin_max snapshot isolation + PGLite
serialization_failure retry is wired in the module but disabled by
default; v0.34.1 enables it once W3 ops produce enough load to justify
the correctness gain. Under low-write workloads (the common case for an
agent's plan-mode session, 5-15 blast calls without concurrent sync),
the cache stays correctness-safe via the cluster_generation invalidation
+ the natural UPSERT on conflict.
test/code-intel/traversal-cache.test.ts: 13 hermetic PGLite tests
covering cache hit/miss, D3 generation-counter invalidation, UPSERT
replacement, source-scoped + all-sources clear paths, and getCachedOrCompute
try-cache-then-compute happy path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Edge extractor now emits three edge kinds:
- calls (v0.20 baseline; v0.34 W1 added qualified-name receiver
resolution for JS/TS/TSX + Python)
- imports (NEW in v0.34 W2; JS/TS/TSX + Python at depth)
- references (NEW in v0.34 W2; TS-only)
Why this matters: Leiden clusters on a calls-only graph produce overfit
garbage (GitNexus showed 0.052 cluster/node on calls-only — useless).
Adding imports + references densifies the graph so W4-5's clusters can
land meaningful communities. Per design doc Constraint #1.
- src/core/chunkers/edge-extractor.ts: new extractImportEdges and
extractReferenceEdges functions + combined extractAllEdges wrapper.
ExtractedEdge.edgeType widened to 'calls' | 'imports' | 'references'.
- src/core/chunkers/code.ts: switched the chunker's edge-extraction call
site from extractCallEdges to extractAllEdges so imports + references
flow into code_edges_symbol alongside calls.
- src/core/chunkers/symbol-resolver.ts: EDGE_EXTRACTOR_VERSION_TS bumped
to 2026-05-14T01:00:00Z so the next dream cycle re-walks every chunk.
Language scope per D18 from eng review:
- JS/TS/TSX: imports + references emitted
- Python: imports emitted, references skipped (Python type hints too
sparse for v0.34; v0.35 may revisit)
- Ruby/Go/Rust/Java: calls only — no imports, no references. Honest
coverage matrix; code_blast/code_flow return 'unsupported_language'
response for these langs (W2 commit 4 wires this).
Edge schema reused: code_edges_symbol.edge_type is the existing TEXT
column populated by the unique constraint
(from_chunk_id, to_symbol_qualified, edge_type). Adding new types
doesn't conflict with existing calls edges.
test/code-intel/edge-densification.test.ts: 13 hermetic tests covering
named/default/namespace/aliased/side-effect imports for JS/TS, from-x-
import-y + import-pkg for Python, function parameter + return type
references for TS, and unsupported-language returns-empty contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The edge-extractor emits qualified callee names (Class::method,
module::method) for the 3 MUST-resolve patterns from the design doc
when running against JS/TS/TSX + Python source:
1. `import { x } from 'y'; x.method()` → emit `y::method`
2. `class C { m() { this.m() } }` → emit `C::m`
3. `const c = new C(); c.m()` → emit `C::m`
When the receiver can't be resolved within WALK_DEPTH_CAP (32) ancestor
hops of the call site, falls back to bare-token emit (pre-W1 behavior).
Ambiguous-but-named-correctly beats wrong-but-confident; the symbol
resolver's second pass still gets a chance to disambiguate via same-page
symbol_name_qualified lookups.
Per D18 from eng review — only JS/TS/TSX + Python get receiver
resolution. Ruby/Go/Rust/Java keep pre-W1 bare-token emit semantics.
RECEIVER_RESOLUTION_LANGS pins the eligible set.
Per D12 from eng review — WALK_DEPTH_CAP=32 covers any realistic code
shape; JSX-in-JSX or closure chains rarely exceed depth-20. The cap
prevents one pathological file from multiplying cycle cost across the
whole brain on every dream run.
- src/core/chunkers/edge-extractor.ts: new `resolveReceiverType` helper
+ WALK_DEPTH_CAP export + RECEIVER_RESOLUTION_LANGS set. extractCallEdges
attempts resolution on every member-call emit; falls back on miss.
- src/core/chunkers/symbol-resolver.ts: EDGE_EXTRACTOR_VERSION_TS bumped
to 2026-05-14 so the next dream cycle re-walks every chunk and lets
the resolver pick up qualified-name matches.
test/code-intel/scope-walker-resolution.test.ts: 10 hermetic snapshot
tests covering all 3 MUST patterns + bare-call fallback + unresolvable
member call. Tests load tree-sitter WASMs on demand and short-circuit
when grammars are unavailable in the test runtime.
Scope reduction from the original plan: the .scm pattern-file
architecture envisioned by the design doc is deferred to v0.34.1. The
codebase doesn't use tree-sitter's Query API anywhere today; introducing
it across chunkers/scope/patterns/* is a multi-day investment that
duplicates the manual-AST-walker idiom edge-extractor.ts already uses.
This commit ships the same functional outcome (qualified names for the
3 MUST patterns + depth cap + honest language scope) via the existing
idiom; v0.34.1 can refactor to .scm files if/when query-API benefits
materialize.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>