Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,132 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v2.0.0-rc.7] - 2026-09-11

The Java facade learns the view layer codeanalyzer-java 3.3.0–3.3.2 added, and the 2.0 line takes up
everything rc.6 shipped from `main` (the 3.2.0 text model and the sibling-analyzer pins) — this is
the first release cut from `release/2.0` that contains rc.6.

### Added

**Java view dispatches** (`python-sdk#404`, spec `codellm-devkit/.github`
`docs/design/specs/2026-09-11-java-view-templates-and-dispatch.md`). JSP, Facelets and Thymeleaf
templates are artifacts with `roles: ["view-template"]`, and the analyzer now says which code reaches
one. Three accessors on `JavaAnalysis`, both backends:

- `get_view_dispatches(view=None) -> List[JViewDispatch]` — every resolved dispatch: the body node
that hands the request over (a `forward` / `include` / `sendRedirect` call, a `ModelAndView`
construction or `setViewName`, or a Spring controller's `return`) and the artifact it reaches,
with `via` naming the mechanism and `prov` the tier. `literal` and `dataflow` mean exactly one
target; `table` (3.3.1) is a may-dispatch over a static string table, one edge per entry, from
the same site. `view` filters by a segment-aligned suffix of the artifact's path.
- `get_view_dispatchers(path) -> List[JCallableOverview]` — the same edges resolved to the callables
that own the dispatching sites.
- `get_unresolved_view_dispatches() -> List[JViewDispatchUnresolved]` — what closed on nothing (a
variable target, a servlet URL, a view name matching two templates), with its reason and the tiers
attempted. JSON-only: the graph has no node for a target that resolved to nothing, so the Neo4j
backend refuses this one rather than answering `[]`.

Two models, `JViewDispatch` and `JViewDispatchUnresolved`, on `JApplication.view_dispatches` /
`.view_dispatches_unresolved`; the Neo4j backend reads `J_DISPATCHES_TO` back into the former.

One rule stated because it is the only one of its kind: **this layer is gated on the analyzer
generation.** The 3.1.0 config trio is probed by an always-present sibling key; 3.3.x added none —
both lists are written only when non-empty and the edge type is declared only once an edge exists —
so a pre-3.3.0 analysis is refused from `analyzer.version` (wire) / `analyzer_version` (graph) instead,
because `[]` off a 3.2.0 analysis would say "reaches no view" where the truth is that nothing looked.

Measured on daytrader8 with 3.3.3: 37 edges at level 1–2 (3 `literal`, 34 `table`), 54 at level 4,
19 of 23 JSPs reached; the `TradeConfig.webUI` table's 17 on-disk pages all reached.

### Changed

- `codeanalyzer-java` pin `3.2.0` → **`3.3.3`** (the `java` extra and `[tool.backend-versions]`).
3.3.0 adds the view-template role, `J_DISPATCHES_TO`, and `argument_expr` on `return` body nodes;
3.3.1 the table tier; 3.3.2 narrows the Jakarta entrypoint finder to lifecycle methods, so
helpers taking servlet-typed parameters are no longer entrypoints and the interprocedural
config-use and view-dispatch tiers bind their parameters (daytrader8 entrypoint marks
135 → 196: 21 helpers dropped, 82 `init`/`destroy`/`doFilter`/`onMessage` gained); 3.3.3 fixes
the `NullPointerException` 3.3.0–3.3.2 threw on any unbuilt project with a dispatch call, which
this SDK's `test_java_degradation.py` was the first to hit (codeanalyzer-java#265).
- `release/2.0` absorbed `main` at rc.6.

### Verification

The Java suite offline on both backends over a1 with the layer injected (edges, suffix filter,
dispatcher resolution, the JSON-only refusal on the graph, the pre-3.3.0 refusal, the empty answer
on a 3.3.x payload that reaches nothing, and the answered-once check across backends); the public
surface pins the three signatures; e2e against the 3.3.3 wheel on daytrader8 at level 1 and 2
asserts the 37 edges, the 19 views, the three unresolved URLs and the 38 view-template artifacts.

## [v2.0.0-rc.6] - 2026-09-10

The Java Neo4j backend stops being the lossy one about source text. Until now a graph-backed Java
analysis could answer *where* a node is far better than *what it says*: text existed only as
`:JCallable.code`, so a callable came back as its whole declaration where the local backend returned
the body block, a body node had no text at all, and a module-scope `locate` returned `""`. The
analyzer closed that gap in 3.2.0; this release takes it up, and moves the sibling pins to the
releases that closed the same gap for Python and TypeScript.

### Breaking

**The Java Neo4j attach floor moves to codeanalyzer-java 3.2.0.** A graph emitted by 3.1.x — served
silently until now — is refused at attach, naming what was found and the floor.

The floor moves rather than degrading gracefully because the degradation would be silent and would
look like data. A 3.1.x graph has no `:JModule.source` and no byte offsets, so every text answer
would come back empty or as the old whole-declaration string, from accessors this release documents
as returning the same text as the local backend. An agent cannot tell "this node has no text" from
"this graph predates the property" — so the check belongs at attach, once, where it can say so.

**Migration:** re-emit with `codeanalyzer-java 3.2.0 --emit neo4j`. Wipe the database first when the
graph predates 3.1.1, which moved the `can://` grammar.

### Changed

**Text is the same on both Java backends.** `JCallable.code`, `get_source`, `LocateResult.source` and
`describe` answer the same slice of the same file at every granularity — a callable's body block, a
type's or field's or local's declaration, a single statement or call site, and a module's whole text at
module scope. Each is a byte slice of `:JModule.source` taken at the node's own offsets, so the live
suites assert **equality** rather than `endswith` or "the same length".

Three positions still have no text, on **both** backends and for the same reason each time — there is
nothing in the file to point at: an implicit compiler-generated `<init>()`, an `@external` callable, and
a `resolve_value` ref (a `formal_in` vertex is a dataflow position, not a region of the file).
`module_source_unavailable` survives as a **data** diagnostic: it now fires on a module whose text the
analyzer could not read or decode, and on nothing else.

Two removals fall out of it. `:JCallable.code` is no longer read — the slice replaces it — and
`body_end_byte` was never needed: across both committed fixtures, all 1,182 callables carrying both
spans end their body exactly where their declaration ends, because a Java declaration's last character
*is* its body's closing brace. That is asserted in the fixture builder rather than written down.

**Analyzer pins move to `codeanalyzer-python==1.5.2` and `codeanalyzer-typescript==1.6.0`**
(`codeanalyzer-java` is already at 3.2.0). Both are Neo4j-projection-only conformance fixes: python
1.5.2 adds `:PyModule.source`, all six span properties wherever a span is declared,
`:PyVariable.value_json`, `:PyBodyNode.callee_signature`, `PY_IMPORTS.positions_json` and a span on
`PY_DECORATED_BY`; typescript 1.6.0 adds `:TSModule.source` and the four new span properties across ten
labels. Neither changes `analysis.json` — the TypeScript fixtures regenerated with the 1.6.0 wheel are
byte-identical to the 1.5.3 generation at all four levels apart from the `analyzer.version` stamp.

**Neither sibling floor moves.** They stay 1.5.0 for Python and 1.5.2 for TypeScript: a floor move is
what would let the SDK *read* that new text, and that is uptake work the size of this release's Java
half, tracked in #396 and #391. The pin is what the SDK installs; the floor is what it requires of a
graph. Every sentence in the docs that read "the graph does not carry this" now says the SDK does not
read it, and names the issue that will.

One thing does improve for free on a re-emitted Python graph: a call site's columns. The
reconstruction already reads `start_column` with a `-1` default, so 1.5.2 supplies real values and an
older graph still answers `-1` — data-driven, no version branch.

### Verification

The mocked, backend-contract and E2E tiers are green, and the Java E2E ran against the real 3.2.0 jar.
The **live** Java tiers — the ones that assert byte-for-byte equality across the two backends on
daytrader8 — assert the new behaviour but have not been run against a 3.2.0-emitted graph: the
verification corpus holds a 3.1.1 graph, which this release's own floor now refuses. Re-emitting it is
the outstanding verification debt, recorded rather than glossed.

## [v2.0.0-rc.5] - 2026-09-10

Two changes, and they are the two halves of one question: what should happen when an analyzer ships
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ an optional read-only Neo4j backend — selected by the *type* of the `backend=`

| Language | Entry point | Local backend | Neo4j backend | Models |
|----------|-------------|---------------|---------------|--------|
| Java | `CLDK.java(...)` (needs the `cldk[java]` extra) | `JCodeanalyzer` (the `codeanalyzer-java` 3.1.1 wheel's jar on its bundled JVM, subprocess, `-a 1..4` — no jar in this repo, no JDK download) | `JNeo4jBackend` (graphs emitted by ≥ 3.1.1, probed at attach) | `cldk/models/java/` (schema v2 mirror) + `projections.py` |
| Java | `CLDK.java(...)` (needs the `cldk[java]` extra) | `JCodeanalyzer` (the `codeanalyzer-java` 3.2.0 wheel's jar on its bundled JVM, subprocess, `-a 1..4` — no jar in this repo, no JDK download) | `JNeo4jBackend` (graphs emitted by ≥ 3.2.0, probed at attach) | `cldk/models/java/` (schema v2 mirror) + `projections.py` |
| Python | `CLDK.python(...)` | `PyCodeanalyzer` (in-process `codeanalyzer-python`) | `PyNeo4jBackend` | re-exported from `codeanalyzer-python` |
| TypeScript (+ JavaScript modules) | `CLDK.typescript(...)` | `TSCodeanalyzer` (`codeanalyzer-typescript` 1.5.2 binary from the wheel, subprocess; `-a 1..4`, but `--emit neo4j` takes no `-a` and is always full depth) | `TSNeo4jBackend` (graphs emitted by ≥ 1.5.2; older refused at attach) | `cldk/models/typescript/` (schema v2 mirror) |
| TypeScript (+ JavaScript modules) | `CLDK.typescript(...)` | `TSCodeanalyzer` (`codeanalyzer-typescript` 1.6.0 binary from the wheel, subprocess; `-a 1..4`, but `--emit neo4j` takes no `-a` and is always full depth) | `TSNeo4jBackend` (graphs emitted by ≥ 1.5.2; older refused at attach) | `cldk/models/typescript/` (schema v2 mirror) |

**Java, since leg 3a (#310):** the models are an `extra="forbid"` mirror of canonical schema v2, so
a 1.x `analysis.json` (and a pre-3.0.1 Neo4j graph) is refused, not parsed; `get_call_graph()` keys
Expand Down
8 changes: 5 additions & 3 deletions cldk/analysis/commons/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,11 @@ class SliceNode(BaseModel):
defined_in: For a ``global``, the module it is defined in (``"payment"``) — the same
vocabulary as :attr:`ModuleRef.module_name`. ``None`` for everything else, whose
defining scope is the enclosing callable in :attr:`callable`.
source: The text, when the backend can produce it; ``None`` when it cannot. The Neo4j graph
carries no text below callable granularity (see :class:`LocateResult`), so it is
``None`` there for anything finer.
source: The text, when the backend can produce it; ``None`` when it cannot. Whether a
Neo4j graph carries text below callable granularity is the projection's choice, so it
depends on the language: Java's does since codeanalyzer-java 3.2.0 and answers the same
text as its local backend, Python's does not and is ``None`` there for anything finer
(see :class:`LocateResult`).
ref: The analyzer's own id for this node — **opaque**. Pass it back; do not parse it, and
do not build one. The one sanctioned use is
:meth:`~cldk.analysis.python.backend.PythonAnalysisBackend.get_source`, and that
Expand Down
Loading