Skip to content

docs(spec): Java config-read edges and the entrypoint report - #63

Merged
rahlk merged 2 commits into
mainfrom
docs/java-config-reads-entrypoint-report-spec
Sep 7, 2026
Merged

docs(spec): Java config-read edges and the entrypoint report#63
rahlk merged 2 commits into
mainfrom
docs/java-config-reads-entrypoint-report-spec

Conversation

@rahlk

@rahlk rahlk commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Design record for the epic in #62, covering codeanalyzer-java#231 goals 1 and 2.

Adds application.config_uses[], application.config_reads_unresolved[] and application.entrypoint_report{} to analysis.json, plus J_USES_CONFIG, J_READS_CONFIG_UNRESOLVED and two :JApplication properties to the Neo4j projection.

Two deliberate divergences from codeanalyzer-python, each recorded with its cost: src widens to the annotated element (Java's dominant config-read idiom is @Value, which has no call site to anchor on), and the literal tier runs at L1 rather than L2 (Java's call body nodes already carry the argument text).

#231's third goal is closed by fact rather than by work: codeanalyzer-python does not project comment nodes either — it collapses them to a docstring property, exactly as Java does — so there is no parity gap and no term to match.

The graph contract stays at 2.0.0; a re-baseline is #50's decision, and the spec records what that costs a consumer in the meantime.

Design record for codeanalyzer-java#231, goals 1 and 2.

Adds `application.config_uses[]`, `application.config_reads_unresolved[]` and
`application.entrypoint_report{}` to `analysis.json`, plus `J_USES_CONFIG`,
`J_READS_CONFIG_UNRESOLVED` and two `:JApplication` properties to the Neo4j
projection.

Two deliberate divergences from codeanalyzer-python, both recorded with their
cost: `src` widens to the annotated element (Java's dominant config-read idiom
is `@Value`, which has no call site to anchor on), and the literal tier runs at
L1 rather than L2 (Java's call body nodes already carry the argument text).

Closes out #231's third goal by fact rather than by work: codeanalyzer-python
does not project comment nodes either — it collapses them to a `docstring`
property, exactly as Java does — so there is no parity gap and no term to match.

The graph contract stays at 2.0.0; a re-baseline is #50's decision.
… settled

Three amendments, all bringing the spec in line with what shipped.

D6 states the resolution rule the spec never had: one `J_USES_CONFIG` edge per
matching declared key, unresolved only when zero match. The tracking issue's
first draft said "exactly one", which is wrong — a key legitimately appears in
both application.properties and application-dev.properties, and a
`@ConfigurationProperties` prefix claims every key beneath it by design, so a
uniqueness rule would drop both on the floor.

D1 gains the two shapes it left open: an unresolved annotation read ghosts the
annotation type, and a prefix binding emits one edge per declared key beneath it.
D3 replaces the one-line dataflow sketch with the two tiers as built, plus the
three refusals that make them safe — including why the `ssa` filter is what keeps
`-a 3` a subset of `-a 4`.

D5a records the decision behind `frameworks_detected`: attribution lives on the
node, because a warm cache skips the build entirely and a tally kept during the
walk would under-report exactly the empty this record exists to prevent. D5's
`unresolved`/`errors` description is corrected to what the code does, with the
ceiling named.

Also: the decomposition table now carries the filed issue and PR numbers and the
branch stacking, and the verification section says what the corpus runs can and
cannot prove.
@rahlk

rahlk commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 487c056 — the spec now states the resolution rule and records what implementation settled.

One correction to my own earlier note: the "exactly one declared key" wording was never in this spec. It was in codeanalyzer-java#232's Goals checkbox, which I have also corrected. The spec's gap was different and worse — it never stated the resolution rule at all, so "exactly one" was a reasonable reading of the silence.

  • D6 (new) — one J_USES_CONFIG edge per matching declared key; a read becomes undefined-key only on a zero-match. Follows python's resolver (config_use.py:304). A uniqueness rule would have dropped both real shapes: one key declared in application.properties and application-dev.properties, and D1's prefix binding claiming every key beneath it.
  • D1 — the two shapes it left open, both settled during implementation: an unresolved annotation read ghosts the annotation type (@Value injection is a read, and the ghost names what performed it), and a @ConfigurationProperties prefix emits one edge per declared key beneath it.
  • D3 — the one-line dataflow sketch replaced with the two tiers as built, plus the three refusals that make them safe. Most of all: why consulting only ssa prov is what keeps -a 3 ⊆ -a 4 true, since admitting an alias edge would remove an edge L3 resolved cleanly.
  • D5a (new) — attribution lives on the node. L1Extractor reuses a cached module and skips the build, so a run-scoped tally would under-report frameworks_detected on a warm cache: exactly the ambiguous empty the report exists to prevent.
  • D5unresolved/errors corrected to what the code does (a finder that throws), with the ceiling named: the finders swallow their own resolution failures internally, so those near-misses are not counted yet.
  • §5/§7 — the decomposition table carries filed issue and PR numbers plus the branch stacking, and verification now says what the corpus runs can and cannot prove.

@rahlk
rahlk merged commit 9cb79b7 into main Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant