Repository navigation
Description
Plan
docs/design/specs/native-v2-model.md (committed on the work branch; design record for the whole rewrite)
Problem
The analyzer carries two model families for one output: stages build the v1 TSApplication (src/schema/schema.ts + graphs.ts), and every emit reshapes the whole tree into V2Application through a ~720-line transform (src/schema/v2/emit.ts + dataflow.ts) — re-bucketing containers, assigning can:// ids, converting call_sites[] to body{}, filtering attributes through a 30-key DROP set with recursive null-pruning. Every schema feature lands twice, and the wire shape of a node is defined nowhere — it is the residue of a filter. codeanalyzer-python retired the equivalent shim (its schema models are the native compute model, with small per-run passes: assign_ids / l1_body / l2_callees / call_graph_ids); this issue does the same here.
Scope boundary
Internal rewrite only. The wire does not move: analysis.json at every level, graph.cypher, schema.neo4j.json, and schema_version 2.1.0 stay semantically identical (deep-equal gate). No SDK change, no sibling-analyzer change, no call-graph-provider behavior change, no dataflow-compute change. Not a schema evolution.
Goals
- Stage 0 — goldens harness: capture
analysis.json(-a 1..4) +graph.cypherfor both fixtures from the branch base; deep-equal transition test (key-order free, arrays ordered; cypher as sorted line set) - Stage 1 — L1 native: v2-bucketed span-only containers built by builders.ts; leaf nullables become optional (omit, never null); assignIds / l1Body / heritage passes; all symbol-table readers migrated; emit.ts loses the tree walk
- Stage 2 (landed with Stage 3 — the pass chain is order-coupled) — L2 native: endpoint homing, callee backfill, call-graph re-identification become core-spine passes; collision + dangling gates surfaced from the passes; emit.ts loses the L2 block
- Stage 3 — L3/L4 native: src/schema/v2/dataflow.ts moves to src/dataflow/attach.ts; program_graphs leaves the application model; envelope built in core; emit.ts deleted
- Stage 4 — teardown: delete v1 types + src/schema/v2/, rename V2* → TS*, delete goldens + harness, update CLAUDE.md / README / SCHEMA_DECISIONS.md, CHANGELOG (Changed, internal), bump 1.1.0
Caveats and known risks
- Old analysis caches store v1 shapes; the existing
analyzer_versioninvalidation drops them wholesale on first post-rewrite run — no migration code, by design. - Stage 1 blast radius: semantic_analysis and dataflow read symbol-table containers; the true coupling surface is discovered in Stage 1. The goldens bound the risk — a misread is a fixture diff, not silent drift.
- If dataflow workers consume serialized v1 module shapes across the process boundary, that surface migrates in Stage 1 with the other readers.
- Deep-equal is weaker than byte-equal on object key order, by construction — accepted trade, recorded in the spec so it is not "fixed" later.
Definition of done
- Transition test proves deep-equality against the Stage-0 goldens for both fixtures at -a 1, 2, 3, 4 and for graph.cypher — green on every stage commit, then deleted with the goldens at teardown.
- Existing standing gates green throughout and after: schema conformance, L1 ⊆ L2 ⊆ L3 ⊆ L4 monotonicity (test/schema-v2.test.ts), Neo4j conformance, full
bun test,bun run typecheck. - One model family remains, named TS*, and src/schema/v2/ no longer exists:
git grep -l "toV2\|V2Application\|V2Root\|V2Node"returns nothing. (TSApplicationsurvives by design — it now names the wire application ROOT node, python'sPyApplicationanalog; the internal working set isAnalysisInternal. Recorded in the spec's terminal-naming row.) - analyze() returns the enveloped application; serialize.ts contains no reshaping — JSON path is strip-internal-fields + write, Neo4j path is project(application) directly.
- CLAUDE.md architecture section describes the native model (no "v1→v2 emitter" language); CHANGELOG entry under Changed; version 1.1.0.
Activity