Skip to content

Extract and freeze the canonical schema v2 contract from codeanalyzer-python #36

Description

@rahlk

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

  • Land #120 in codeanalyzer-python first — the contract must not describe a shape that is about to change
  • Extract the contract from real emitted output: node kinds and fields, edge families, id grammar, both projections. Generate or verify against an actual analysis.json, not hand-written prose
  • State the Neo4j merge-label strategy as Python's, with rationale — Python currently uses 9 merge groups where TypeScript uses 2 (CanNode, Application)
  • Body-node model is Python's body{} after #120
  • can:// grammar is Python's ids.py as it stands — can://<lang>/<app>/<file>/<type>/<callable-sig>, settled by #123's won't-fix — including the per-language signatureOf() each analyzer must implement
  • Publish as the versioned contract python-sdk v2.0.0-rc.1 targets
  • Enumerate what TypeScript and Java must change — as input to their migration issues, not done here

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions