Repository navigation
Description
Plan (optional)
docs/design/plans/2026-09-07-can-uri-app-outermost.md. Spec: codellm-devkit/.github → docs/design/specs/2026-09-07-can-uri-app-outermost.md (.github#68).
Problem
Two identity defects, fixed together because they share one migration.
The deployment unit is not part of the id. can://<lang>/<app>/… buries the app inside the id, so for a multi-service application the graph is queryable but not addressable, and one unit spanning two languages fragments into unrelated id roots.
The graph's application root has no id at all. analysis.json emits application.id = can://java/daytrader8, and V2GraphProjector drops it: :JApplication merges on name, the raw --app-name. Consequences, all live:
- Two services analyzed under one
--app-namesilently merge into one node. - The root cannot be reached by id, though every other v2 node is keyed on one.
RowBuilderattaches the:JCanNodeindex anchor only to values starting withcan://, so the root never gets it — it is the one node the prefix-scoped destructive statements structurally cannot reach.
Scope boundary
In scope: codeanalyzer-java only, shipping in 3.1.1.
Out of scope, by decision: codeanalyzer-python, codeanalyzer-typescript, python-sdk, docs. The maintainer's call, recorded in spec §6 with its costs — chiefly that the grammar fragments until the siblings follow, so a cross-language database will hold two id shapes and the cross-language delete scope will not yet work.
Goals
-
CanIdmintscan://<app>/java/…;can://<app>/artifact/…;can://<app>/java/@external/… -
L2CallGraph:267andL3WalaOverlays:375stop matching ids by their language prefix -
:JApplicationdeclares and merges onid, keepsnameas a display property, and gains the:JCanNodeanchor - The uniqueness constraint moves from
j_application_nametoj_application_id -
analysis.v2.schema.jsonadmits only the new shape - 92 fixture references migrated, composing ids rather than hard-coding them
-
schema.neo4j.jsonregenerated; README §4 updated
Caveats and known risks
V2SchemaCatalog.SCHEMA_VERSIONstays2.0.0. docs(schema): stop calling the graph contract 2.2.0 #238 recorded "do not bump this to advertise them"; the graph re-baseline is Scope every destructive Neo4j statement on thecan://id prefix; retire_module.github#50's.- The payload
schema_versionis an open decision (plan Task 7). Leaving it at2.0.0makes an incompatible id change undetectable; moving it to3.0.0re-creates the unilateral drift that was undone once already. The plan recommends moving the payload version only, and says to confirm before implementing. - Old and new ids do not collide, so re-pushing an existing database produces a second disconnected copy rather than an update — and the prefix-scoped delete cannot remove the old copy, because it scopes on the new prefix. Pre-3.1.1 roots have no
idat all, so neither prefix match reaches them. The plan carries the three Cypher statements needed. - A breaking id change ships under a PATCH version. Accepted:
python-sdk release/2.0pinscodeanalyzer-java==3.1.0exactly, so it is insulated until deliberately bumped — and that bump is a fixture migration, not a version edit. L1Cachealready discards on analyzer-version change, so 3.1.0 → 3.1.1 covers cache invalidation.
Definition of done
- On daytrader8 at
-a 1:application.idiscan://daytrader8, and everysymbol_tablemodule id starts withcan://daytrader8/java/. - No emitted id matches
^can://(java|artifact)/— the old shape is gone, not supplemented. - Every projected
can://-keyed node is the root or sits undercan://<app>/, checked by query. - Two applications projected under different names produce two distinct
:JApplicationnodes; the same name twice raises a uniqueness violation rather than merging. - Conformance green at every level;
--emit schemaoutput matches the checked-in snapshot.
Activity