Repository navigation
Description
Parent: codellm-devkit/.github#35 · Spec: docs/design/specs/2026-09-03-python-sdk-canonical-v2.md (#318)
Problem
python-sdk models the schema four times, once per language (cldk/models/{java,python,typescript,c}). All three analyzers now emit canonical v2 — codeanalyzer-python 1.4.0, codeanalyzer-typescript 1.2.0, codeanalyzer-java 3.0.1 — while the SDK pins 0.3.1 / 0.4.3 and bundles the 2.4.1 JAR, so it is the last consumer still parsing v1.
The spine is common to all three: id / kind / span, source once on the module node, named-map containment, a body{} map keyed by ordinal id, split cfg / cdg / ddg / summary lists on the callable, and identity-only edges at application scope. That agreement is what gets modelled once.
Scope boundary
The shared model layer and the Python leg that validates it — they land together, because a spine designed with no consumer is a spine designed against the spec rather than against a real payload. TypeScript is leg 2 (#307), Java is leg 3 (#310), the query layer is leg 4 (#311).
Does not collapse the per-language facades (PythonAnalysis / JavaAnalysis / TypeScriptAnalysis / CAnalysis) into one — explicitly out of scope for the parent epic. Does not change what any analyzer emits.
Goals
-
cldk/models/cpg/defines the spine once: envelope (Analysis,AnalyzerInfo), containers (Application,Module,Type,Callable), leaves (BodyNode,Field,Parameter,Decorator,Import,Comment,Span), edges (CallEdge,CfgEdge,CdgEdge,DdgEdge,IdEdge), and the repository-artifact layer - Per-language subclasses narrow the container maps by redeclaring them and add typed extras — no generics, no untyped
extra="allow"dicts (spec D2) - No language model package redefines a spine field
- Python leg: pin
codeanalyzer-python==1.4.0, subclasses, view layer, Neo4j backend, facade — pin bump and model migration in the same PR, since v1 models cannot parse v2 output and vice versa - The
PyCallableOverviewprojection pattern (#180/feat(python): bulk/projected accessors for the Python facade (#180) #181) generalises into the view layer, which is where API stability is bought:.codeand.call_sitesbecome computed views overbody/source - The nine-entry divergence register (spec § 5) filed onto Extract and freeze the canonical schema v2 contract from codeanalyzer-python .github#36 as the extraction input
- Ships as
2.0.0-rc.1; closes Incorporate codeanalyzer-python v1.2.0 #315, Incorporate codeanalyzer-python v1.3.0 #316, Incorporate codeanalyzer-python v1.4.0 #317, and Python get_method_bodies lacks the None-code omission filter (twin of the TS #298 rule) #301
Withdrawn from the previous revision
- The Java blocker. The earlier body said the SDK "cannot move to identity-only edges while its producer emits rich ones", and marked the Java half blocked on Migrate to canonical schema v2: can:// ids, statement-level CPG, identity-only edges codeanalyzer-java#179.
codeanalyzer-java3.0.1 emitsJCallEdge{src, dst, prov, weight},JIdEdge{src, dst}and a canonicalbodymap.JGraphEdgesand_CALLABLES_LOOKUP_TABLEsurvive only in the SDK's own v1 models — SDK-side legacy retirement, not an upstream dependency. - "Invisible to users until the pins move off the 0.x line." True when written; the pin bump is now part of leg 1 rather than a follow-up, so nothing about this work is deferred behind a separate pin-moving issue.
- "May need promotion to its own epic." Epic fixing caller method #35 keeps coordinating (spec D5). Per-leg work items are filed at pickup.
Caveats and known risks
- The contract this models is not frozen: Extract and freeze the canonical schema v2 contract from codeanalyzer-python .github#36 is open and
codeanalyzer-schemaholds only a README. The SDK models what the three analyzers actually emit and records every divergence rather than blocking (spec D1) — which means today's divergence is absorbed into the SDK's view layer until COBOL support #36 retires it. - Removing
_CALLABLES_LOOKUP_TABLEis behavioural, not a refactor: today a miss silently fabricates a callable withis_implicit=Trueand-1sentinels rather than failing, so consumers may depend on edges resolving to synthetic nodes. That lands in leg 3 (python-sdk 2.0 leg 3a: Java on schema v2 — models, both backends on the 3.0.1 vocabulary, probe, pin #310), not here. - Four of the nine registered divergences are parity-clause breaches on the Python side (
config_reads_unresolvedvsconfig_reads,base_classesvsbase_types/interfaces,attributesvsfields, and v1 line fields retained alongsidespan). The SDK absorbs them; each is a line item COBOL support #36 can retire.
Definition of done
cldk/models/cpg/exists and no language model package redefines a spine field.- The Python facade loads
codeanalyzer-python1.4.0 output, and every public accessor's name, signature and return type is unchanged — proven by the existing Python facade tests running unmodified. - Both projections agree:
analysis.jsonand the Python Neo4j backend return the same facts for the same run. pyproject.tomlpinscodeanalyzer-python==1.4.0.- The divergence register is filed on .github#36.
2.0.0-rc.1cut; Incorporate codeanalyzer-python v1.2.0 #315, Incorporate codeanalyzer-python v1.3.0 #316, Incorporate codeanalyzer-python v1.4.0 #317 and Python get_method_bodies lacks the None-code omission filter (twin of the TS #298 rule) #301 closed.
Activity
codeanalyzer-*andpython-sdk.github#35rahlk commentedon Sep 4, 2026
Superseded by codellm-devkit/.github#55 and the spec at
docs/design/specs/2026-09-03-agent-facing-query-facade.md(#318).Why, precisely. This issue's premise was that canonical v2 exists so the schema can be modelled once — one
Node/Edge/Application. Measured against the three released analyzers it does not hold:typecallableType's eight are
id,kind,span,name,signature,callables,types,comments. Callable's ten areid,kind,span,signature,return_type,body,cfg,cdg,ddg,summary. That is identity, containment and the graph — and not one shared semantic declaration field.attributes/fields, modifiers, decorators, base classes, exported/async/abstract/ambient, records and overloads all diverge. A sharedTypebase would carry five identity fields and two dicts while every question a caller actually asks lived in the subclass.So the sharing moves up a layer: from the model to the query shape. Each language keeps its own models; one generic ABC defines what can be asked of any of them.
Two claims in this issue's body are withdrawn on the evidence:
codeanalyzer-java3.0.1 emitsJCallEdge{src, dst, prov, weight},JIdEdge{src, dst}and a canonicalbodymap.JGraphEdgesand_CALLABLES_LOOKUP_TABLEsurvive only in the SDK's own v1 models — SDK-side legacy retirement, no upstream dependency. That work continues as python-sdk 2.0 leg 3a: Java on schema v2 — models, both backends on the 3.0.1 vocabulary, probe, pin #310.Nothing here is lost — the model migration is legs 1–3 of the new epic, and
_CALLABLES_LOOKUP_TABLE's removal is leg 2.