Parent: #35 · Roadmap: docs/design/roadmap.md (candidates 1, 2, 4)
Rewritten 2026-08-20. The original framing — compare three analyzers and reconcile them into a negotiated contract — no longer holds. codeanalyzer-python is the reference implementation: it defines the contract, and codeanalyzer-typescript and codeanalyzer-java migrate to what it emits. This issue extracts and freezes that contract rather than negotiating one.
Problem
There is no document stating what canonical schema v2 is, so "conformant" is unfalsifiable. Each analyzer validates its emitter against its own declared schema, and the roadmap's comparison table — the only cross-analyzer record — has at least one verified error: it credited codeanalyzer-python with structured decorators it did not have until #128 landed. Corrected in 22289e2.
The contract cannot simply be read off main today, because one breaking change is still queued inside codeanalyzer-python itself: #120 converges call_sites[]/accessed_symbols[]/local_variables[] into body{}. Freezing before it lands publishes a contract with a known break in it, and forces python-sdk through a second release candidate.
The identity grammar is now settled: #123 (a <service> segment on can:// ids) was closed won't fix along with its TypeScript sibling and parent epic. Ids keep the form can://<lang>/<app>/<file>/<type>/<callable-sig>.
Scope boundary
Writes the contract document from what codeanalyzer-python emits. Does not migrate TypeScript or Java, does not implement #120 or #123, and does not change python-sdk.
Goals
Caveats and known risks
- Python leading means Python's current divergences become the contract, including ones where a sibling arguably chose better — 9 merge groups versus TypeScript's 2 is the clearest. Decide each deliberately rather than inheriting it by omission.
- Java has no
can:// ids and no statement-level nodes in Neo4j; its migration is a major version, not an adaptation.
python-sdk v2.0.0-rc.1 pins whatever this freezes. A change after rc.1 costs an rc.2, which is the cost this issue's ordering exists to avoid.
- The roadmap comparison table is evidence of unknown reliability — one row was verifiably wrong. Re-measure against emitters rather than citing it.
Definition of done
- Contract committed to
docs/design/specs/ in this repo
- Every node kind, field, edge family and id form Python emits appears in it, each verified against a real
analysis.json rather than asserted
- Every place Python's choice differs from TypeScript's or Java's is listed with an explicit ruling, not left implicit
codeanalyzer-typescript and codeanalyzer-java migration issues cite it as their target
python-sdk v2.0.0-rc.1 models it, with the backwards-compatibility window stated in its CHANGELOG
Parent: #35 · Roadmap:
docs/design/roadmap.md(candidates 1, 2, 4)Problem
There is no document stating what canonical schema v2 is, so "conformant" is unfalsifiable. Each analyzer validates its emitter against its own declared schema, and the roadmap's comparison table — the only cross-analyzer record — has at least one verified error: it credited
codeanalyzer-pythonwith structured decorators it did not have until #128 landed. Corrected in22289e2.The contract cannot simply be read off
maintoday, because one breaking change is still queued insidecodeanalyzer-pythonitself: #120 convergescall_sites[]/accessed_symbols[]/local_variables[]intobody{}. Freezing before it lands publishes a contract with a known break in it, and forcespython-sdkthrough a second release candidate.The identity grammar is now settled: #123 (a
<service>segment oncan://ids) was closed won't fix along with its TypeScript sibling and parent epic. Ids keep the formcan://<lang>/<app>/<file>/<type>/<callable-sig>.Scope boundary
Writes the contract document from what
codeanalyzer-pythonemits. Does not migrate TypeScript or Java, does not implement #120 or #123, and does not changepython-sdk.Goals
codeanalyzer-pythonfirst — the contract must not describe a shape that is about to changeanalysis.json, not hand-written proseCanNode,Application)body{}after #120can://grammar is Python'sids.pyas it stands —can://<lang>/<app>/<file>/<type>/<callable-sig>, settled by #123's won't-fix — including the per-languagesignatureOf()each analyzer must implementpython-sdkv2.0.0-rc.1 targetsCaveats and known risks
can://ids and no statement-level nodes in Neo4j; its migration is a major version, not an adaptation.python-sdkv2.0.0-rc.1 pins whatever this freezes. A change after rc.1 costs an rc.2, which is the cost this issue's ordering exists to avoid.Definition of done
docs/design/specs/in this repoanalysis.jsonrather than assertedcodeanalyzer-typescriptandcodeanalyzer-javamigration issues cite it as their targetpython-sdkv2.0.0-rc.1 models it, with the backwards-compatibility window stated in its CHANGELOG