Repository navigation
feat(ids): @external drops the language segment; document the polyglot rule - #248
Merged
Merged
Conversation
`@external` joins `artifact` as a language-NEUTRAL id family: it now sits in
the position the language occupies for code nodes, so sibling analyzers over
the same `<app>` mint the same id for a library symbol and it is one node in a
merged graph. Mirrors codeanalyzer-python and codeanalyzer-typescript.
can://<app>/java/@external/java.util.Map/get(java.lang.Object)
-> can://<app>/@external/java.util.Map/get(java.lang.Object)
The cost was accepted deliberately: two analyzers' notions of a library symbol
are not necessarily the same thing, and merging them says they are.
Schema: `externalCanId` loses `/java/`, so it no longer matches `canId`
(`^can://[^/]+/java/`) at all. Two sites legitimately hold either shape -- a
call edge's `dst` and a body node's `callee`, both of which carry an external
id when --external-calls homes a target out of project -- so they now `$ref` a
new `codeOrExternalCanId` union rather than `canId` being loosened to admit ids
that are not this analyzer's.
`:Artifact`, `:ConfigKey` and `@external` ids omit the `/java/` segment and are shared merge targets across sibling analyzers over the same app; code nodes carry it and stay this analyzer's. Names the consequence the prefix-scoped snapshot wipe already has for a cross-language edge into a shared `:Artifact`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes Java's convergence onto the id grammar codeanalyzer-python and codeanalyzer-typescript already use. Ships in 3.1.1, which is on
mainand still untagged.@external becomes language-neutral
@externalnow sits in the position the language occupies for code nodes — the same treatmentartifactalready had. A sibling analyzer over the same<app>mints a byte-identical id, so a library symbol is one node in a merged graph rather than a per-language duplicate. codeanalyzer-python's own docstring for this ends "TypeScript's form; java follows it"; Java was the last holdout.The cost is carried into the javadoc rather than left implicit: two analyzers' notions of a library symbol are not necessarily the same thing, and merging them says they are. That was accepted deliberately upstream.
The schema change this forced, and why it is a union not a widening
External ids no longer match
canId(^can://[^/]+/java/) — correct, they have no/java/. But two sites that$refcanIdlegitimately hold them:callEdge.dstandbodyNode.callee, both set fromCanId.externalIdwhen--external-callsis on (8,909 of 10,785 call-graph edges on daytrader8).Loosening
canIdwould have admitted external ids intomodule.id,type.id,field.idandcallable.id— exactly the malformed-node case that gate exists to catch. So instead:codeOrExternalCanId = anyOf[canId, externalCanId], applied to those two sites only. Review walked every$refand confirmed they are the only two that need it, and that nothing was over-widened:cfg/cdg/ddgendpoints staylocalId,configRead.calleeandexternal_symbolskeys stay strictexternalCanId, andcallEdge.srcstays strictcanId— verified in code (L2CallGraph.java:301,304: the RTA path returns early for non-app callers, so a library caller can never appear as a src).README: the polyglot rule
§4 now states plainly which id families are language-neutral shared merge targets (
:Artifact,:ConfigKey,@external) versus code nodes that keep/java/, and the write consequence: because the shared nodes sit insidecan://<app>/, a snapshot's prefix wipe rebuilds them, so a cross-language edge into a shared:Artifactis dropped by one analyzer's snapshot and restored on the other's next push.It also records what the app id does not do: it is derived from
--app-name, so two services sharing a name merge onto one root. Give each its own--app-name.Verification
CodeAnalyzerIntegrationTest, no Docker)-a 2 --external-callscanIdschema.neo4j.jsoncan://orpatternin it)Note for the 3.1.1 release notes
Schema version stays
2.0.0, so nothing signals to a consumer holding storedcan://<app>/java/@external/…ids that they will no longer join. Worth a line when the tag is cut.Not changed
docs/design/plans/2026-09-07-can-uri-app-outermost.mdstill shows the old external shape. That is committed design provenance describing what that plan shipped, so it is left as a historical record rather than rewritten.