Spec: codellm-devkit/.github → docs/design/specs/can-uri-service-segment.md
Coordination: codellm-devkit/.github#39
Problem
codeanalyzer/schema/ids.py hard-codes the language into the scheme constant and builds the
application root beneath it:
_SCHEME = "can://python"
def application_id(app_name: str) -> str:
return f"{_SCHEME}/{app_name}"
So every durable id is can://python/<app>/<file>/<type>/<sig>, with no deployment dimension.
A polyglot microservice has no single id root, and cocoa has had to invent one outside the
schema (cocoa/system/facts.py:39 wraps our id as fn:<service>/can://python/…).
Per the spec, <service> becomes the outermost segment and <app> collapses into it:
before can://python/<app>/<file>/<type>/<sig>
after can://<service>/python/<file>/<type>/<sig>
Because the service must sit above the language, _SCHEME can no longer be a literal prefix —
it becomes can://{service}/python. Everything below application_id composes from it unchanged
(module_id, child_id, callable_sig_segment, ordinal_id are pure and parent-relative).
Scope boundary
This repo only. Does not add cross-service edges (no RPC/HTTP call family — Epic E), does not
touch the keystone grammar docs in cldk-devtools/cldk-skillset, does not touch cocoa, and
does not touch codeanalyzer-typescript (its own issue). The can:// scheme name is settled.
Does not change the Neo4j property-graph version in codeanalyzer/dataflow/builder.py:741
(schema_version="1.0.0") — that is a separate contract from the analysis.json envelope.
Goals
Caveats and known risks
- The version number understates the break. 2.1.0 is MINOR for a change that rewrites every
durable id and the Neo4j MERGE key. Deliberate erratum, valid only because no consumer holds a
can:// id today (python-sdk is pre-v2 and addresses by signature). Not precedent later.
- Persisted Neo4j graphs need a full re-ingest, not an incremental
MERGE — every node key
changes, so merging against a 2.0.0 database builds a parallel graph.
- Shared libraries are analyzed once per service by design: one run per service, so a
libs/
imported by six services yields six id sets for the same source. Accepted (spec D5).
- Renaming
--app-name is a CLI break for any existing caller. Whether to keep it as a
deprecated alias is an open call — the spec does not decide it.
- Must land in lockstep with
codeanalyzer-typescript's equivalent change; cocoa is gated on
both.
Definition of done
Spec:
codellm-devkit/.github→docs/design/specs/can-uri-service-segment.mdCoordination: codellm-devkit/.github#39
Problem
codeanalyzer/schema/ids.pyhard-codes the language into the scheme constant and builds theapplication root beneath it:
So every durable id is
can://python/<app>/<file>/<type>/<sig>, with no deployment dimension.A polyglot microservice has no single id root, and
cocoahas had to invent one outside theschema (
cocoa/system/facts.py:39wraps our id asfn:<service>/can://python/…).Per the spec,
<service>becomes the outermost segment and<app>collapses into it:Because the service must sit above the language,
_SCHEMEcan no longer be a literal prefix —it becomes
can://{service}/python. Everything belowapplication_idcomposes from it unchanged(
module_id,child_id,callable_sig_segment,ordinal_idare pure and parent-relative).Scope boundary
This repo only. Does not add cross-service edges (no RPC/HTTP call family — Epic E), does not
touch the keystone grammar docs in
cldk-devtools/cldk-skillset, does not touchcocoa, anddoes not touch
codeanalyzer-typescript(its own issue). Thecan://scheme name is settled.Does not change the Neo4j property-graph version in
codeanalyzer/dataflow/builder.py:741(
schema_version="1.0.0") — that is a separate contract from theanalysis.jsonenvelope.Goals
schema/ids.py— service becomes the outermost segment;<app>removed--app-name→--service, same default (input directory name)schema/py_schema.py—Analysis.schema_versiondefault →"2.1.0"core.py:728cache freshness gate →!= "2.1.0"(auto-invalidates every 2.0.0 cache)neo4j/project.py,neo4j/schema.py) — ids andSCHEMA_VERSIONfollowtest_v2_ids.py,test_v2_conformance.py,test_v2_cache.py,conftest_v2.py,test_cli.py).claude/SCHEMA_DECISIONS.md— record the five locked decisions from the specCaveats and known risks
durable id and the Neo4j MERGE key. Deliberate erratum, valid only because no consumer holds a
can://id today (python-sdkis pre-v2 and addresses by signature). Not precedent later.MERGE— every node keychanges, so merging against a 2.0.0 database builds a parallel graph.
libs/imported by six services yields six id sets for the same source. Accepted (spec D5).
--app-nameis a CLI break for any existing caller. Whether to keep it as adeprecated alias is an open call — the spec does not decide it.
codeanalyzer-typescript's equivalent change;cocoais gated onboth.
Definition of done
canpy -i ./python-sdkwith no flags emitscan://python-sdk/python/cldk/core.py/CLDK/…— verified on a real run, not an asserted shape
canpy -i ./svc --service emailserviceemitscan://emailservice/python/…<app>segment distinct from the servicecall_graph,param_in/param_out, backfilledcallee—with zero dangling references on an L4 run of a real repo
<callable-id>@<line>:<col>,@entry,@formal_in:0,@16:2/actual_in:0)schema_versionreads"2.1.0"; a cached 2.0.0analysis.jsonis rejected and rebuiltL1 ⊆ L2 ⊆ L3 ⊆ L4monotonicity holds on a real repo — theprov:["ssa"]DDG set isidentical at L3 and L4, with L4 adding only
points-toandreaching-defs--emit neo4jproduces a graph whose node keys are the new ids and whose declared schemamatches the emitter (existing conformance test still passes)