Skip to content

Epic: python-sdk 2.0 — a static-analysis front end for humans and agents #55

Description

@rahlk

Spec

python-sdk/docs/design/specs/2026-09-03-agent-facing-query-facade.md (codellm-devkit/python-sdk#318)

Summary

CLDK 2.0 becomes a complete static-analysis front end — symbol table, source bodies, repository artifacts, call graph, control and data flow, addressing, reachability, slicing and taint — behind one surface serving both human and agent callers.

This epic is a sibling of #35, not a child. #35 is schema-v2 conformance across the analyzers; this is the SDK's query facade. The divergence register the design produced belongs to #35 and #36 and is filed there.

The premise that changed

#35's SDK children (codellm-devkit/python-sdk#309, #308) assumed the SDK could model the schema once — one Node / Edge / Application. Measured against the three released analyzers that does not hold:

Node python typescript java shared by all three
type 15 23 16 8
callable 26 20 17 10

Every survivor is identity, containment or the graph. Not one shared semantic declaration field — attributes/fields, modifiers, decorators, base classes, exported/async/abstract/ambient, records and overloads all diverge. 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.

How the design was derived

By taking a production agent pipeline apart stage by stage and asking of each: what is this stage doing that is really a static-analysis query, and what signature would remove it? Five code-domain stages collapse — alert-to-sink mapping becomes locate_many, index building disappears into a corrected enumeration, entrypoint cataloguing drops a sharded LLM substage because analyzers already emit is_entrypoint, the reachability cone becomes a server-side traversal that returns its unresolved-dispatch ledger, and narrative source-to-sink argument becomes taint() with per-edge provenance.

That pipeline is one of several possible consumers. An IDE assistant, a code-review agent, a migration tool and a documentation generator decompose into the same four moves — locate, read, relate, prove.

Design decisions

Nine, locked in the spec. The load-bearing ones:

  • One surface for humans and agents. Bulk calls, returned diagnostics, named rather than positional addressing and server-side traversal are ordinary good API design, not agent accommodation. Splitting the surface would let the human half rot.
  • No shared declaration models. Each language owns its own; Python's stay a re-export.
  • One generic ABC in three families — structural getters (per-language bodies, because TypeScript splits type kinds across five node labels), graph queries (one Cypher template plus a P/N label-prefix pair, because PY_DDG/TS_DDG/J_DDG are structurally identical), and resolve() (per-language).
  • API signatures backwards compatible, enforced per leg — which does not extend to graph generation; see the release plan.
  • Drop C entirely. libclang, syntactic-only, no v2 graph, no analyzer, no deployment selects it.
  • Neo4j substrate for this cut, attached read-only, so nothing is materialised.
  • An ambiguous empty is a defect. Closed selector vocabulary, first-class diagnostics, strict=True, and a graph_schema_mismatch probe on attach.

Explicitly out of scope: deriving edges no analyzer emitted (the shared-field join belongs upstream, to be raised against the analyzers); collapsing the per-language facades into one; the entrypoint vocabulary; unprefixing the Neo4j labels.

Release plan

Leg Carries Ships
1 Python — v2 models, ABC conformance, locate/locate_many, bodies, artifacts, entrypoints, external symbols, graph_schema_mismatch; drop C and four dead dependencies 2.0.0-rc.1
2 Java — v2 models, ABC conformance, locate, bodies, artifacts; JGraphEdges and _CALLABLES_LOOKUP_TABLE retired 2.0.0-rc.2
3 TypeScript + JavaScript — v2 models, prefixed (TS_/JS_) vocabulary, eager-init fix, locate, bodies, artifacts 2.0.0-rc.3
4 Query sweep — selectors, diagnostics, backward_cone, slicing, taint, across all three at once 2.0.0

Three concrete legs validate the ABC before anything shared is built on it; a query layer built against one backend would be an abstraction designed from one instance. The sweep is cheap because it comes last.

Signature compatibility is not graph compatibility. Each leg moves its language's Neo4j backend to the vocabulary the current analyzer emits, so a graph built by a matching generation starts working and one built by an older generation stops. That mismatch is silent today. The SDK will probe the attached graph on attach and raise graph_schema_mismatch naming expected labels, found labels and the generation each implies.

Definition of done

  • All four legs shipped; 2.0.0 released.
  • Each leg's facade passes its existing tests unmodified — the signature-stability gate.
  • The twelve named tests in the spec's § 10 pass, each reproducing a recorded field failure: module-scope positions, unanalysed files, gaps between callable spans, untruncated bodies, external callee resolution, graph_schema_mismatch, selector typos, unresolved-dispatch ledgers, and level_too_low.
  • pyproject.toml pins codeanalyzer-python 1.4.0 and codeanalyzer-typescript 1.2.0, bundles the codeanalyzer-java 3.0.1 JAR, and carries none of clang, libclang, tree-sitter-c, tree-sitter-go.
  • CLAUDE.md's supported-languages table drops C and describes the three families.
  • A compatibility matrix — SDK version to analyzer generation to graph vocabulary — in each rc's release notes.

Caveats

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    EpicCross-repo coordination epic

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions