Skip to content

Epic: repository-artifact layer — artifacts, dependencies, config, config-uses (schema + 3 analyzers + SDK) #45

Description

@georgesafta

Spec

docs/design/specs/repository-artifact-layer.md (committed in codellm-devkit/.github)

Summary

Adds a repository-artifact layer to canonical schema v2 across the three analyzers: the producer-side evidence a cross-service / whole-application analysis needs but no analyzer emits today. Schema impact is additive — three new application-contained node kinds (artifact, dependency, config_key), one new typed edge (config_use / *_USES_CONFIG), their Neo4j projections, and one shared scope enum with its ecosystem mapping table. Nothing existing is renamed or repurposed. This is roadmap group D (candidates 15–18); it lands into the v2 catalog group A stands up, so group A gates it.

Affected repos

  • codeanalyzer-schema — v2 model definitions (three node kinds + config_use edge + scope mapping table), Neo4j contract projection. Gates all analyzers.
  • codeanalyzer-python:PyArtifact / :PyDependency / :PyConfigKey emitter, non-source discovery walk, pyproject.toml / requirements* / lockfile parsing, PY_USES_CONFIG
  • codeanalyzer-typescript:TSArtifact / :TSDependency / :TSConfigKey, package.json (incl. dev/peer/optional) + lockfiles, tsconfig.json JSONC tolerance, TS_USES_CONFIG
  • codeanalyzer-java:JArtifact / :JDependency / :JConfigKey, pom.xml / Gradle coordinate parsing (net-new), config_use in analysis.json (Neo4j J_USES_CONFIG deferred)
  • python-sdk — v2 Pydantic model mirror (co-evolution rule); facade surface exposure is later, out of scope here

Design decisions

  • Artifacts are first-class nodes in the containment tree, not top-level sibling maps. The canonical keystone allows one node vocabulary reachable from application by containment; sibling maps would introduce a second. This is a strict superset of the source proposal — the two string back-references (dependency→manifest, config→file) become containment edges, artifacts get durable can://…/@artifact/… ids, and the JSON matches the Neo4j graph the source doc already drew.
  • artifact_kind is a typed closed enum (build_manifest | dependency_lockfile | configuration | deployment_manifest | container | infrastructure | ci | script | documentation | data | other). other is the catch-all — coverage over classification; a file is never dropped for lack of a parser.
  • Raw text captured by default, 256 KiB cap, --artifact-text / --no-artifact-text flag; path + content_hash always present so a truncated/binary artifact still dereferences. Emit contract, not schema-versioned.
  • scope enum + ecosystem mapping table are coined once in the schema, not per-analyzer: runtime | development | test | build | optional | unknown, with the Maven/npm/pypi mapping in the contract.
  • config_use (candidate 18) is scoped in with a per-projection split, not deferred whole. The edge name and its (namespace, key) join are identical across all three languages and both projections. analysis.json carries it for all three; Neo4j carries PY_USES_CONFIG + TS_USES_CONFIG now.
  • Scope guard: J_USES_CONFIG (Java's Neo4j config-use edge) is explicitly OUT — Java's Neo4j projector has no body-node label until its v1→v2 migration (candidate 9). Obsolete as of 2026-08-29: candidate 9 landed — codeanalyzer-java#203 shipped the canonical v2 graph projection (V2GraphProjector, V2SchemaCatalog, graph contract 2.0.0, since 2.1.0), removed the --schema v2 --emit neo4j guard, made v2 the default schema, and codeanalyzer-java#205 followed. Java's projector does have body-node labels now. Whether J_USES_CONFIG is in or out is therefore a live decision for whoever picks up codeanalyzer-java#197, not a settled deferral. SDK facade/query surface for the artifact layer is OUT (later). This spec implements no analyzer — that is the codeanalyzer-backend rung, per repo, after the epic exists.

Release plan

  • codeanalyzer-schema lands first — the v2 models, config_use edge, scope table, Neo4j contract. Gates everything.
  • The three analyzers implement in parallel, each on its own release train — none gates another. Per analyzer, units (artifact inventory → dependencies → config definitions → config uses) may land as separate PRs.
  • python-sdk mirrors the v2 models after any ONE analyzer is conformant — it does not wait for all three.
  • J_USES_CONFIG rides Java's candidate-9 v1→v2 Neo4j migration, not this epic. Obsolete — that migration has landed (see the scope guard above). Re-decide with codeanalyzer-java#197 rather than inheriting the deferral.
  • Blocked by group A (the v2 catalog these node kinds land into must exist first).

Definition of done (epic-level)

  • Every sub-issue closed and its PR's gate green.
  • artifact / dependency / config_key node kinds and the config_use edge defined once in codeanalyzer-schema with identical field names, enum values, and edge payload across python / java / typescript (parity clause holds — no renamed/repurposed shared vocabulary).
  • The scope enum and its ecosystem mapping table are encoded in the schema, not per-analyzer.
  • Each analyzer's output validates against the v2 models; analysis.json round-trips through the schema for all three languages.
  • PY_USES_CONFIG and TS_USES_CONFIG present in Neo4j. J_USES_CONFIG is no longer automatically a gap — Java's v2 projector exists as of codeanalyzer-java#203, so either it ships too or its absence is a recorded decision, not an inherited blocker.
  • python-sdk v2 models mirror the three node kinds and the edge.
  • Existing source/program-graph behavior unchanged; docs / CHANGELOG updated.

Contract as shipped (python v1.3.0, 2026-08-29) — supersedes the decisions above where they differ

The python train implemented the full layer (issues codellm-devkit/codeanalyzer-python#152, #157, #162, #165; released as 1.3.0 on PyPI). Where implementation experience diverged from this epic's sketch, the shipped contract is the one siblings copy — rationale in the reconciliation and the settled-vocabulary comment below:

  • Neutral nouns, prefixed claims — graph labels Artifact / Package / ConfigKey carry no language prefix (a Dockerfile or a PyPI package is not a language concept; two analyzers over one repo MERGE onto one node). Language-specific edges keep the prefix: PY_PROVIDES, PY_UNRESOLVED_IMPORT, *_USES_CONFIG per language. This replaces PyArtifact/TSArtifact/JArtifact label triplets.
  • Ids: can://artifact/<app>/<path> (language-neutral namespace, not …/@artifact/… under the language); packages by purl (pkg:pypi/…, pkg:npm/…, pkg:maven/…); config keys <artifact-id>@key/<dotted.key>. Precondition for joins: agreed --app-name.
  • Placement: application.artifacts sibling map (config keys nested per artifact) rather than containment-tree nodes — the tree stays code-only.
  • Classification: open roles: string[] vocabulary (dependency-manifest, service-topology, container-image, ci, env, tool-config, packaging, script, docs, legal, iac, unknown) instead of the closed artifact_kind enum; never-drop still holds (unmatched decodable files → unknown, binaries inventoried hash-only).
  • Dependency fields: kind ∈ {runtime, dev, optional, build} (in place of the scope enum), prov ∈ {declared, lockfile, installed-metadata, heuristic}, direct: false for lockfile-only transitives, locked_version, provides_imports.
  • Text policy: capture-on by default, 256 KiB cap, --artifact-text/--no-artifact-text, --artifact-text-max-bytes; sha256/size always full-file; manifests/config parse from full on-disk text so extraction never depends on the stored (possibly truncated) copy.
  • config_use is level-graded and never guesses: literal tier at the call-graph level, dataflow tiers over each analyzer's own def-use substrate; unresolved reads are first-class records with reasons (non-literal / undefined-key). Deployment-env sources (Dockerfile ENV, compose/k8s env) mint bindable env-namespace keys; Dockerfile ARG gets a non-bindable dockerfile namespace.
  • Neo4j SCHEMA_VERSION holds at 2.0.0 until the layer settles across analyzers — one bump then.
  • Release-plan amendment: the codeanalyzer-schema-repo-first gate did not hold — python shipped in-repo models. Extracting shared models into codeanalyzer-schema remains open, now with a working reference to extract from.

Reference implementation to copy from: specs docs/design/specs/2026-08-27-artifacts-and-dependencies-design.md, 2026-08-28-config-key-family-design.md, 2026-08-28-config-use-edge-design.md (all in codeanalyzer-python); the v1.3.0 release assets include the machine-readable contract (schema.json, schema.cypher) and the query cookbook (analyzing-canpy-graphs-skill.tar.gz).

Metadata

Metadata

Assignees

No one assigned

    Labels

    EpicCross-repo coordination epic

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions