Conversation
…ecked out
A module id gains a segment naming the module as its own manifest declares
it -- Maven's <artifactId>, Gradle's rootProject.name -- between the
language segment and the path within that module:
can://daytrader/java/daytrader-web-service/src/main/java/.../Bean.java
So pointing --input at one service repo names that service in the id, with
no staging directory and without pointing --input at a parent that sweeps
in sibling repos. The coordinate is read from the manifest rather than
taken from the directory name because a declared name survives a rename, a
relocated checkout and a differently-laid-out CI workspace, where a
path-derived segment would make every id a function of where the tree
happens to sit on disk.
The symbol_table key does NOT change. It stays the real --input-relative
path: uniqueness is the key's job, a path is unique by construction where a
coordinate is not, and with no path field on the module object the key is
also the only thing that still locates a file on disk. The key is therefore
no longer the tail of the id, and nothing may assume it is --
L3WalaOverlays recovered the application id by stripping the key off a
module id, so it is passed the real one instead.
The same non-uniqueness reaches the id, which is the join key for
call_graph endpoints. A coordinate claimed by two module directories is
applied to neither, with a warning naming the competitors; vendored
duplicates are exactly where this bites, and applying the prefix there
would merge two distinct callables onto one node. Without that rule the
change would regress "ids are distinct by construction", the property it
was chosen for.
A module with no readable coordinate keeps precisely the id it had before,
so a loose source tree is unaffected.
Spec: codellm-devkit/.github docs/design/specs/2026-09-17-manifest-derived-module-prefix.md
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.
Spec:
2026-09-17-manifest-derived-module-prefix.md· Tracking: codellm-devkit/.github#92 · Spec review: codellm-devkit/.github#91A module id gains a segment naming the module as its own manifest declares it, between the language segment and the path within that module:
No staging directory, no
-f, and no pointing--inputat a parent that sweeps in sibling repos.The coordinate comes from the manifest rather than the directory name so it survives a rename, a relocated checkout, or a differently-laid-out CI workspace. The gate fixture demonstrates the difference: its directory is
mvnw-working-testbut its pom declaresmy-javaee-mvn, and the id carries the latter.Two rules that are load-bearing, not incidental
The
symbol_tablekey does not change. It stays the real--input-relative path. Uniqueness is the key's job and a path is unique by construction where anartifactIdis not; with no path field on the module object, the key is also the only thing that still locates a file on disk. Consequence: the key is no longer the tail of the id.L3WalaOverlaysrecovered the application id by stripping the key off a module id, so it is now passed the real one and that derivation is deleted.A contested coordinate is applied to neither claimant. The id is the join key for
call_graphendpoints, so a coordinate claimed by two module directories would merge two distinct callables onto one node. Vendored duplicates are exactly where this bites — several services each building one shared internal library under the same module name. Verified:Without that rule the change would regress "ids are distinct by construction" — the property it was chosen for.
A module with no readable coordinate keeps exactly the id it had before, so a loose source tree is unaffected:
can://bare/java/src/main/java/com/foo/Bare.java.Tests
16 new, written failing first:
ModuleCoordinatesTest(7),ModulePrefixesTest(7),L1BuildContextTest(2). They pin the parent/dependencyartifactIddistinction, the DOCTYPE refusal, nearest-enclosing-manifest for reactor children, the--inputsearch bound, the contested-coordinate fallback, and the key/id separation.L3WalaOverlaysTestis deleted — its only test reflected into the derivation this PR removes.L2CallGraphGateTest's expected ids are updated; that fixture declaresrootProject.name = 'call-graph-test', so it is also a live example of the documented app-name/coordinate redundancy (can://call-graph-test/java/call-graph-test/...), which the spec §5 accepts rather than special-cases.Verification
./gradlew testCodeAnalyzerIntegrationTestTestcontainers/DockerinitializationError-a 1/2/3/4all validate againstanalysis.v2.schema.jsonjson(-a 1) ⊆ (-a 2) ⊆ (-a 3) ⊆ (-a 4)-a 4 --eagerruns byte-identicalReviewer note: the warning is invisible by default
Log.toConsolegates every level behindisVerbose()(Log.java:128), and--verbosedefaults to false, so without-vthe analyzer prints noWARNand noERRORat all. The contested-coordinate warning above only appears under-v.That is pre-existing and wider than this PR — it also makes the README's "it degrades and says so on stderr" guidance false by default — but it means this PR's warning does not currently reach anyone who does not already pass
-v. Worth fixing separately; flagging it rather than silently relying on a warning nobody sees.Does not fix
#269 — a single analysis spanning source roots that declare the same fully-qualified type binds every caller to the last root, silently. This makes a bundled tree addressable, not correctly resolved.