Repository navigation
Description
Summary
Implement the repository-artifact layer in codeanalyzer-java — the producer-side evidence a cross-service / whole-application analysis needs but no analyzer emits today. Contract of record: docs/design/specs/repository-artifact-layer.md (committed in codellm-devkit/.github), tracked by epic codellm-devkit/.github#45.
This is the first PR for the Java train, the application-anchored slice, into the canonical schema v2 model (--schema v2, package com.ibm.cldk.schema.*):
artifactnodes (JArtifact) — a non-source file inventory with raw-text capturedependencynodes (JDependency) — normalized deps parsed frompom.xml/ Gradle coordinates (net-new — today deps are only downloaded as jars, never modeled)config_keynodes (JConfigKey) — normalized keys from structured config files
All three anchor on the application node (stable, no body-node risk). The shared vocabulary (scope enum, artifact_kind enum, @artifact/ id grammar) is coined verbatim per the spec's parity clause, identical to the python/typescript trains.
Two Java-specific divergences (found by inspecting the code, not just the spec)
-
Neo4j is now available for this layer — the premise this issue was written on is obsolete. When this issue was filed,
analyzeV2()rejected--emit neo4jand the Neo4j projector ran entirely off the legacy v1 IR, so a v2 addition could reachanalysis.jsonbut never the graph. That is no longer true: PR feat(l4): interprocedural SDG — param_in/param_out, semantic DDG, summary edges, graph 2.1.0 #203 shipped the canonical v2 graph projection (V2GraphProjector,V2SchemaCatalog, graph contract2.0.0, since bumped to2.1.0by the L4 overlay), removed the guard, and made--schema v2the default. "Roadmap candidate 9", the v1→v2 Neo4j migration this issue deferred to, is done. Whoever picks this up should decide afresh whether to project the artifact layer into the graph in the same PR — the epic'sJ_USES_CONFIG-only deferral and this issue's broader whole-layer deferral were both consequences of a blocker that no longer exists. Note thatV2SchemaCatalogalready reserves un-prefixedArtifactandPackagelabels as cross-language merge targets, declared but not emitted, so the graph vocabulary is partly staked out already. -
Artifact discovery needs a new repo-wide walk. The v2 walk (
L1Extractor.extractAll) is source-root scoped — it only enumerates.javafiles under JavaParser source roots and never sees a repo-rootpom.xml/application.yml/Dockerfile. The inventory needs its ownFiles.walkof the project root, homed alongsideutils/ProjectDirectoryScanner.java, not hooked intoL1Extractor.
Scope
In (this PR):
JArtifact/JDependency/JConfigKeyLombok@DataPOJOs +JApplication.artifactsmap (com.ibm.cldk.schema.*)CanId.artifactId/dependencyId/configKeyIdhelpers (@artifact/marker keeps them out of thesignatureOfid space)- Repo-wide inventory + classification walk (new
com.ibm.cldk.artifactspackage);othercatch-all — a file is never dropped - Raw-text capture (utf-8 / binary / over-cap truncation),
--artifact-text/--no-artifact-text(default on) +--artifact-text-max-bytes(default 262144); parsing reads a decode buffer independent of capture so the flag drops only the raw-text payload - Dependency parse:
pom.xml(JDK DOM, on the classpath) withgroupId:artifactIdidentity,version_spec,<scope>/<optional>→ sharedscope;build.gradle/.ktsshallow regex reader;gradle.lockfile→resolved_version;ecosystem="maven" - Config parse (overlay — failure never drops the artifact):
.properties(java.util.Properties),application.yml/.yaml(needs a new YAML dep — see open decision), Jakarta EE.xmldescriptors; dotted keys,value,references[](${VAR}→env:VAR) - Extend the local conformance schema
src/test/resources/schema/analysis.v2.schema.json($defs+ enum constraints +application.artifacts) — the L1 conformance gate validates against it withadditionalProperties:false - Decision-log entry
Out (follow-ups / other trains):
config_use/J_USES_CONFIGedge (follow-up PR; Java is well-positioned —JBodyNodealready carriesreceiverExpr/argumentExprat L1, soSystem.getenvreads are visible without aPyCallArgument.value-style extension;@Value/@ConfigurationPropertiesreads anchor onJDecorator)All Neo4j projection of the artifact layer — rides candidate 9— obsolete: candidate 9 landed in PR feat(l4): interprocedural SDG — param_in/param_out, semantic DDG, summary edges, graph 2.1.0 #203. Re-scope when picked up.- python / typescript analyzers; python-sdk mirror
Open decisions (resolve before/at review)
- YAML dependency: add SnakeYAML to
build.gradlenow (enablesapplication.ymlconfig-key extraction; needs areflect-config.jsonentry for native-image), or ship.ymlinventoried-but-unparsed this PR. Recommendation: add now —application.ymlis the dominant Spring Boot config surface. - Gradle depth: shallow regex reader misses version-catalog (
libs.*) / dynamic deps. Accept as a documented, logged limitation for PR 1 (Mavenpom.xmlis the complete path), or scope Gradle out of PR 1.
Acceptance
application.artifactsemitted as a contained named map ofartifactnodes with stablecan://…/@artifact/…ids; unrecognized filesartifact_kind:"other", never droppedpom.xml(and best-effort Gradle) parsed intodependencynodes withgroupId:artifactIdidentity and correctscope;gradle.lockfilepinsresolved_version- Structured config exposes
config_keynodes with dotted keys and recognizedreferences[] - Text-decodable artifacts carry verbatim
text+text_encoding/text_truncated; binary/over-cap remain inventoried withpath+content_hash --no-artifact-textdrops only the text payload; inventory + deps + config overlay unchangedartifactssection byte-identical across-a 1|2|3(ungated / all-levels)L1ConformanceGateTestgreen (local v2 schema extended); nonullleavesNeo4j untouched:— obsolete. The v1 catalog is no longer whatNeo4jSchemaConformanceTestgreen,schema_version 1.0.0unchanged,--schema v2 --emit neo4jstill errors, no artifact labels in the graph catalog--emit schemaemits (it now always emits the v2 catalog, currently2.1.0), and--schema v2 --emit neo4jis the supported default path. Replace with whatever the re-scoping decides.- Existing v2
symbol_table/call_graphoutput byte-identical
Part of epic codellm-devkit/.github#45. Sibling: codeanalyzer-python#152.
Update (2026-08-29): the Neo4j premise above was invalidated by PR #203 (v2 graph projection) and PR #205 (@entry defines the formals). Struck text is left visible rather than deleted so the original reasoning stays auditable. The rest of the scope — application-anchored JArtifact/JDependency/JConfigKey, pom.xml/Gradle coordinate parsing, config-key extraction, and the repo-wide walk L1Extractor cannot do — is unaffected and still accurate.
Activity
rahlk commentedon Aug 30, 2026
The python train is complete and released (codeanalyzer-python 1.3.0, 2026-08-29): artifacts + dependencies (#157/#160), ConfigKey family (#152/#163), the level-graded
config_useedge (#162/#164), and deployment-env namespaces (#165/#168). The epic (codellm-devkit/.github#45) now carries a "Contract as shipped" section — where this issue's original text follows the epic's older sketch, the shipped contract supersedes it. The load-bearing deltas:Artifact/Package/ConfigKey(shared MERGE targets across analyzers) — notJArtifact/TSArtifact-style triplets. Language-specific edges keep the prefix: your*_PROVIDES,*_UNRESOLVED_IMPORT,*_USES_CONFIG.can://artifact/<app>/<path>(neutral namespace); packages by purl (pkg:maven/…/pkg:npm/…); config keys<artifact-id>@key/<dotted.key>.--app-nameagreement is the join precondition.application.artifactssibling map, config keys nested per artifact; the code tree stays code-only.roles: string[](never-drop inventory: unmatched decodable →unknown, binaries hash-only), not a closedartifact_kindenum.kind{runtime, dev, optional, build},prov{declared, lockfile, installed-metadata, heuristic},direct: falsefor lockfile-only transitives,locked_version.--artifact-text/--no-artifact-text,--artifact-text-max-bytes; extraction parses full on-disk text, never the stored copy.ENV, compose/k8senv) mint bindableenvkeys, DockerfileARGa non-bindabledockerfilenamespace.SCHEMA_VERSIONstays 2.0.0 until the layer settles across all analyzers.Copy-from reference: the three specs in codeanalyzer-python
docs/design/specs/(2026-08-27 artifacts, 2026-08-28 config-key family, 2026-08-28 config-use edge) and the v1.3.0 release assets —schema.json+schema.cypherare the machine-readable contract;analyzing-canpy-graphs-skill.tar.gzdocuments the query surface the layer should support.Java-specific notes:
pom.xml/Gradle parsing maps onto the same record shape (purlpkg:maven/<group>/<artifact>; Maven scopes → the sharedkindvalues per the epic's mapping duty, now expressed againstkindnotscope). TheJ_USES_CONFIG-deferred-to-candidate-9 boundary is unchanged —analysis.jsoncarriesconfig_usesfacts; the Neo4j edge rides the v2 migration.docs(plan): repository-artifact layer for java, emulating codeanalyze…
rahlk commentedon Aug 30, 2026
Implementation plan committed: docs/design/plans/2026-08-29-repository-artifact-layer.md (
5ef4ad4onmain), seven tasks.It emulates
codeanalyzer-pythonv1.3.0 rather than the spec, on explicit instruction. Read codellm-devkit/.github#48 alongside it: the spec mandates anartifact_kindenum, ascopeenum, and an ecosystem mapping table that the reference implementation never built — the whole python package contains exactly oneEnum, and it is unrelated. So this issue's line about coining that vocabulary "verbatim per the spec's parity clause, identical to the python/typescript trains" cannot be satisfied as written; the two clauses contradict each other. The plan follows the trains.What that means concretely: bare
format(one value) plus many-valuedroles[]instead ofartifact_kind;kindwith python's four values instead of the spec's six;ecosystemas a hardcoded"maven"literal; artifacts as sibling maps on the application withsymbol_tableleft strictly code-keyed; and lockfile-only transitive dependencies emitted withdirect=false, which the spec scopes out but python ships.Four places the plan decides rather than transcribes, because Java's ecosystem differs: Maven
provided/systemscope has no slot in python's four-valuekind(mapped tobuild, documented);purlbecomes two-segmentpkg:maven/<group>/<artifact>, soJDependencycarriesgroupadditively; PEP 503 normalization is not ported; andprovides_imports/unresolved_importsare not implemented at all — they exist in python because a PyPI distribution name is not its import name, which is not a Java problem, and doing it properly means reading package indexes out of resolved jars.Task 7 is the Neo4j projection that this issue originally deferred, now unblocked. One new dependency:
org.yaml:snakeyaml, needed forapplication.yml; it is currently only transitively present.Out of scope and worth their own issues:
config_use/J_USES_CONFIG(python's detector-rule mechanism is call-shaped and has no purchase on Spring's@Valueannotation injection), POM parent and BOM-import inheritance, and transitive Maven resolution beyond what a lockfile states.feat(artifacts): repository-artifact layer — artifacts, dependencies,…