Skip to content

feat(code-analysis): add Java support - #1075

Merged
yijiez666-alt merged 17 commits into
tt-a1i:labs/code-analysisfrom
CheeseMiao0520:feat/java-support
Oct 25, 2026
Merged

yijiez666-alt merged 17 commits into
tt-a1i:labs/code-analysisfrom
CheeseMiao0520:feat/java-support

Conversation

@CheeseMiao0520

@CheeseMiao0520 CheeseMiao0520 commented Oct 24, 2026 •

Copy link
Copy Markdown

Problem and value

Add Java support to Code Analysis. On base 4553a4e, a Java-only repository stops with extract/adapter-undetected. It can now produce source dependencies, complexity findings, cycles, hubs, the analysis page and AI tasks through the existing pipeline, without a JDK or project build.

This PR targets labs/code-analysis, the module branch tracked by #998, which integrates Code Analysis into dev.

AI assistance and accountability

  • AI use: implementation, investigation, tests, review and documentation assistance.
  • Tools and models: Claude Opus 5.5
  • Accountable contributor: @CheeseMiao0520 — led the contribution, reviewed the feature design, and directed implementation and validation.
  • Problem origin: requested feature — Java support for Code Analysis.
  • I reviewed the submitted changes and explanation, understand their impact, and can answer questions about them. Validation results and gaps are recorded below.

Implementation and stability impact

  • Impact: contract and shared behavior. Add the Java adapter, parser dependency, language selection, default scan patterns and a Java module-grouping default; reuse the existing graph, rules, page and task contracts.
  • Pin java-parser@3.0.1. Resolve supported import and type-reference forms only when the source index identifies one unambiguous declaration; uncertain names stay unresolved and are counted. Unsupported files remain visible in parse_errors.
  • Class bodies with extends/implements resolve when every supertype, transitively, is one analyzed declaration or a listed JDK type named by a single-type import (import java.io.Serializable;). The list and each type's member types are in JAVA.md; of them only java.util.Map contributes one (Entry), which stays unresolved. Implicit java.lang names, on-demand imports and fully qualified supertypes keep the conservative guard, as do external, duplicate or cyclic supertypes. A name matching an inherited member type stays unknown.
  • Scope rules follow javac on the covered forms: own members are out of scope in a type's extends/implements clause (record components see them), member types shadow single static imports, enum bodies leave EnumDesc unresolved, and qualified names never start at a subpackage. A parser misreading of member records whose body is also a valid block is rewritten into the record, nested types included.
  • Measure methods, constructors, initializer blocks and lambda fields. Parameter signatures distinguish overloads. Page titles retain JS/Python () and display Java signatures as-is.
  • Java file and module cycles are informational structural dependencies, without class-initialization claims.
  • Module grouping (Java only): inferred from build-file locations and source directories; Maven/Gradle models are not evaluated. The nearest directory with pom.xml or build.gradle(.kts) owns a source at any depth. When at most one build module owns analyzed sources, modules are packages within each src/<set>/java root. modules.groups still win; modules.depth or modules.packageBoundaries: false restores directory-depth grouping. The strategy is reported as grouping.java. Adding a build module or moving source roots can change grouping, and with it module cycles and hubs, without any dependency change; JAVA.md gives an explicit-group configuration for longitudinal comparison. Python and JS/TS grouping is unchanged.
  • Language selection: a JS/TS or Python tree that also holds .java files keeps its language without --language, and coverage lists the Java files left out. A root with pom.xml, build.gradle(.kts) or settings.gradle(.kts) beside JS/TS or Python sources requires --language, following the existing ambiguity policy.
  • Defaults add **/*.java and exclude **/target/**/*.java. Python/JS files under target/ retain their existing scan behavior. The changed default config digest makes the first comparison after upgrading report config-changed.
  • Comparisons across this PR's revisions: the analyzer version (0.1.0-dev.1) and adapter id (java-parser@3.0.1) did not change while Java resolution and grouping did, so comparability() does not flag a Java result from an earlier revision of this PR. Base cannot produce Java results, so this affects only results produced from this PR's own earlier revisions. They are not intended as baselines; see the reply below for the options.
  • CI: full regression shard 1 now runs the Code Analysis module suite (npm --prefix archify run test:code-analysis), pinned by the compatibility gate.
  • The coverage line for Java says "dependency records could not be resolved", since Java counts type references as well as imports.
  • Module grouping and language selection are defaults chosen for this PR; both are isolated (graphs/module.mjs, extract/index.mjs) and can be changed without touching the adapter.
  • Remaining limits include external supertype chains, local/anonymous scopes, names shadowed by a local type anywhere in the file, wildcard/classpath lookup, generated types and reflection. See archify/modules/code-analysis/docs/JAVA.md.
  • New tests cover the adapter contracts, module grouping and language selection; browser regressions check callback/overload titles, Java cycle details and the Java coverage wording. Graph, rule and task behavior stay in the shared pipeline.

Tests run

Base: 4553a4e. Head: b21b90b.

CI

  • CI run on this head: all checks passed (deploy jobs skipped).
  • The Code Analysis module step in full regression shard 1: 209/209 passed; the rest of that shard passed 1,937 with 74 skipped and 0 failed.

Local (Windows 11, Node 24.15.0, Python through BAUIFY_PYTHON, local Chrome; package build and smoke on Node 22.23.2). The contributor ran the three test commands. The package build, repository analyses and javac check were run with Claude Code. One JavaScript test unchanged by this PR ("function-scope import() and require are deferred" in import-semantics.test.mjs) fails only when run in an interactive color terminal: its child process then prints ANSI color codes. It passes with piped output and in CI, and behaves the same at 241bc52.

Check Result
node --test archify/modules/code-analysis/test/*.test.mjs 209/209 passed
node --test test/core-smoke.test.mjs test/compatibility-gate.test.mjs test/start.test.mjs 24/24 passed
node --test test/code-analysis-browser.test.mjs test/code-analysis-complexity-browser.test.mjs 5/5 passed
node scripts/package-smoke.mjs <extracted-zip>/archify Passed
javac differential check (harness and output) 0 defects at this head; see below

Human inspection

Human inspection by @CheeseMiao0520 at b21b90b (Windows): opened the Code Analysis page for Spring PetClinic 500158f with a hand-written five-component IR and an explicit --map.

  1. All five components were mapped (0 not on this diagram), and the overview reported 0 findings.
  2. The IR declares no connections, so the diagram draws no arrows; module dependencies were read from the component panels and matched the module graph: Owners & pets in 0 / out 8 (to Shared model), Vets in 1 / out 5, PetClinic app in 0 / out 3 across 2 modules.
  3. A first attempt used a map that named module paths instead of module ids; every component then showed as unmapped, and the page did not report that the map named no existing module.
  4. Not checked: file-level edges inside a module (such as Owner → Pet), the coverage details, and perceptual design quality.

Independent reproduction by @yijiez666-alt at 5a65da8 (Linux, Node 22.23, Chrome): module suite 208/208, core-smoke 19/19, browser files 5/5; Commons Collections numbers matched; Python/TS outputs byte-identical apart from the config digest. MyBatis 3 and Spring PetClinic also analyzed cleanly.

Java repositories. Default scan/role configuration with --language java: node archify/bin/archify.mjs code-analysis check <repo-root> --language java --out <out-dir> --fail-on never --json. Unique file edges count distinct resolved file pairs across all extracted roles; product edges are those between source-role files. Cycle and hub findings exclude test/generated roles. Source only: no Maven/Gradle build or project code was run.

Apache Dubbo 3.3.6 (f1585880) Commons Collections (892bdc8f)
Java files (product / test) 3,977 (2,390 / 1,587) 678 (367 / 311)
Parse errors 0 3 (test files with \uXXXX)
Functions measured 28,323 8,635
Dependency records 76,020 10,789
… resolved 53,242 5,303
… unresolved external 16,506 3,609
… unresolved unknown 6,272 1,877
Unique file edges (product → product) 19,946 (13,212) 2,037 (1,030)
Modules / module edges (grouping.java) 78 / 273 (build-modules) 19 / 77 (packages)
Import cycles / module cycles / hubs 42 / 0 / 7 4 / 1 / 4
Extraction / peak RSS 71 s / 1.5 GB 14 s / 0.7 GB

Reading the unresolved counts. Java records every type reference, not only import declarations, so one dependency usually has several records. external is JDK and third-party imports. unknown is project names the conservative rules leave unresolved, mostly inside bodies whose supertype chain includes an external type.

Compared with 241bc52 (the first version of this PR), same inputs:

  • No previously resolved record changed target or became unresolved, on either repository.
  • Unique file edges: Dubbo 19,026 → 19,946, Commons Collections 1,739 → 2,037; none removed.
  • unknown records: Dubbo 22,434 → 6,272, Commons Collections 5,362 → 1,877.
  • Import cycles: Dubbo 36 → 42, Commons Collections 4 → 4. The added cycles come from added edges, not from a code change in Dubbo.
  • Module counts and module cycles are not compared: the default grouping changed between the two revisions (directory depth → build modules / packages), so a module cycle that disappears is a grouping effect, not a fix.

javac differential check. A synthetic probe: generated programs full of name collisions, compiled by javac 17, with every type reference and import javac binds compared against the adapter's records. At this head, 2,051 programs (59,204 references, 56,185 imports) give 0 defects, while 4,461 references to analyzed files stay unresolved and are counted as unknown. 241bc52 gives 1,113 wrong edges and 342 uncounted drops on the same programs; these programs are adversarial, so the counts are not a real-world error rate. Denominator, categories, seeds, discarded programs and limits are in the harness README.

Python/JS. On py-basic, py-tasks, ts-basic, the Code Analysis module and the archify/ package (both --language ts), raw facts, module graphs and review are byte-identical between base 4553a4e and 5a65da8. Findings differ only in analysis.configDigest, and overview only in the listed exclude patterns (the added **/target/**/*.java). Commits after 5a65da8 change only Java resolution, the Java grouping default when modules.groups claim every nested file, documentation, CI and the package.

  • Outcome: no blocking defect was found within the tested scope.
  • Not run: root test:full, the full Node compatibility matrix and macOS. Linux ran only in the reproduction above, at 5a65da8.

Visual evidence

  • Reproduction: the two browser test files listed above, using their fixed JS, Java and Python fixtures.
  • Checked surfaces: overview and finding-detail text, including repeated JS callbacks, Java overloads and Python function titles; Java file/module cycle wording, the file-cycle risk explanation and the Java coverage wording are also checked.
  • Automated Chrome checks: 5 passed, 0 failed, 0 skipped.
  • Human inspection: the Spring PetClinic analysis page, by the contributor; see Tests run.

Generated artifacts

  • archify.zip: built with official Node 22.23.2 / zlib 1.3.1-e00f703; two builds are byte-identical. The extracted final archive passes package smoke outside the repository.
  • test/fixtures/tasks/*: default config digest updates only.

Add java-parser 3.0.1 (Apache-2.0) for the Java adapter. It is pinned
because the parser identity is recorded in raw facts and decides run
comparability. It brings chevrotain and lodash transitively; no JVM or
native binaries are needed.
Parse captured UTF-8 source with java-parser. Broken, non-UTF-8 and
Unicode-escape files stay visible in parse_errors without partial facts;
Java expands \uXXXX before lexing and the parser does not. Empty member
records, which java-parser 3.0.1 reads as methods returning `record`, are
restored as record declarations; no such method compiles.

Methods, constructors, compact constructors, initializer blocks and lambda
fields emit the shared functions contract with the existing complexity and
nesting rules. Code outside methods is measured on its own, as module-level
callbacks are in JS/TS. Names carry parameter types so overload ids do not
depend on source order.
Map declared type names to files, independent of directory layout, and
record an edge only when one declaration in one file matches. Covered:
single-type and static imports (including explicitly and implicitly static
member types), type positions, instance creation, constructor references and
class literals, bound in Java's lexical order.

Duplicate declarations and members of ambiguous enclosing types, shadowed
names, types local to any block, wildcard imports, qualified creation,
other method references and references inside bodies of types with
supertypes stay unresolved instead of guessed.

Unresolved counters keep the shared meaning: imports of packages no analyzed
file declares are external, analyzed names that cannot be resolved are
unknown, and references that cannot denote an analyzed type are not recorded.
Register Java language selection and default scan patterns while retaining
Python/JS target-directory scans. Extend the shared raw-facts contract
without changing the meanings of the unresolved counters.

Report Java cycles as informational structural dependencies, with explicit
class-initialization assessment notes in their evidence and detail views.
Reuse the graph, rules and task pipeline with Java source labels.

Preserve JS/Python complexity titles and display Java overload signatures
once. Browser regressions cover callback/overload titles and both Java
cycle detail views. Task fixtures change only by the default config digest.
Describe recorded imports and type references, conservative resolution
guards, unresolved counters and dependency-record weights. Document
complexity measurements, overload names and parser boundaries.

Explain language selection in terms of the configured scan and its upgrade
impact. Link Java guidance from the module architecture, README, usage
guide and technical guide.
Build archify.zip from the tracked Skill sources using scripts/build-zip.sh
with official Node 22.23.2 and bundled zlib 1.3.1-e00f703.

Verify byte-identical repeated builds and package smoke after extraction
on Windows.
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review: 1
  • ^dev$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…types

The body guard skipped every reference inside a type that extends or
implements anything, because an inherited member type can shadow an
imported or same-package name.

Bodies now open when every supertype, transitively, resolves to one
analyzed declaration. Supertype names are bound in the scope of their
header, including member types inherited by enclosing types. A body name
matching an analyzed inherited member type stays unresolved and counts as
unknown, even over an external import; an external, duplicate or cyclic
supertype keeps the body unresolved. Enum bodies leave EnumDesc, the
member type every enum inherits from java.lang.Enum, unresolved.

Scope fixes the body resolution depends on:
- A member record whose body holds only member types is rewritten from
  the parser's method misreading, so those types are indexed.
- Enum and record members are out of scope in their own implements
  clause; record components still see them.
- Member types in scope shadow single static imports.
- A qualified name never starts at a subpackage of the current package,
  and one whose first segment is a type imported on demand stays
  unresolved.
Directory depth 2 folded a standard single-module Maven tree into one
src/main module, so module cycles and hubs could never fire, and merged
nested Maven modules in multi-module trees.

For Java, unless modules.depth or packageBoundaries: false is set, the
nearest directory with pom.xml or build.gradle(.kts) owns a source at any
depth. A tree where at most one build module owns analyzed sources is
split by package within each src/<set>/java root instead. Files outside
those, and files claimed by modules.groups, keep the existing rules. The
chosen strategy is reported as grouping.java. Python and JS/TS grouping
is unchanged.
…their language

Adding **/*.java to the default scan made a JS/TS or Python tree that
also holds Java sources, such as a React Native android/ folder, stop
with extract/adapter-ambiguous unless --language was given.

Java now leaves automatic selection to the other language unless the
root holds a Maven or Gradle build or settings file, which keeps the
ambiguity error for Java projects with a stray script. The left-out Java
files are reported in coverage as before.
Java counts unresolved type references as well as imports, so the
coverage line "N imports could not be resolved" overstated the imports.
Java runs now say "dependency records"; other languages are unchanged.

tt-a1i commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Thanks for the substantial update. I reviewed 5a65da8 from both product/maintenance and technical perspectives. The Java grouping, inherited-type handling and preservation of existing mixed-repository defaults are meaningful improvements; the earlier blanket limitations on those points no longer apply. I also verified the new CI run 40081479090 against the labs/code-analysis integration, rather than reusing the old head's result.

A few focused evidence/maintenance points would help before a directional decision:

  1. Please make the code-analysis module suite an explicit CI step. The root full-regression runner enumerates root test files; its green result and the passing Java browser fixtures do not independently cover the reported 208 module tests, including the new name-resolution/grouping cases.

  2. Please link the javac differential harness, generation configuration/seed and comparison output. The 1,204-program/41,954-reference result is promising, but I could not locate the generator/oracle in the PR. Please define the denominator and how unsupported forms, external/same-file references, compilation failures and intentional unknowns are counted. Zero uncounted drops should be distinguished from zero unresolved dependencies; this is a reproducibility request, not a claim that the result is wrong.

  3. Please keep the grouping contract explicit: this infers boundaries from build-file locations/source directories rather than evaluating Maven/Gradle models. Adding a second build module or changing directory prefixes can alter grouping and therefore cycle/hub counts without changing dependencies. Document a stable explicit-group configuration for longitudinal comparisons, and avoid comparing old/new grouping outputs as if a cycle had been fixed. Since this is still an experimental branch, the version/result-compatibility handling can be scoped accordingly.

The direction looks useful as conservative source-structure analysis, not full Java runtime or build-model analysis. The target remains labs/code-analysis; this review does not approve a merge or expansion into dev. Thanks for the clear limitations and continued work.

Copy link
Copy Markdown
Collaborator

Thanks for this — I ran 5a65da8 (base 4553a4e) locally to check the behavior on repositories outside the PR's test set. Summary: results reproduce, precision looks good, and one recall gap is worth documenting or narrowing.

Reproduced

  • Module suite 208/208, core-smoke 19/19, the two Code Analysis browser files 5/5 (Linux, Node 22.23, Chrome).
  • Commons Collections 892bdc8f: 3 parse errors, 2,024 unique file edges, 19 modules, 4 file / 1 module cycles — matches the table.
  • Python/TS (py-basic, Code Analysis module with --language ts): raw-facts, module-graph and review byte-identical between base and head; findings/overview differ only in the config digest and the added **/target/**/*.java exclude.
  • Two repositories not in the PR: MyBatis 3 0437d36 (1,396 Java files, 18 s) and Spring PetClinic 500158f (50 files, 2 s). Both analyze cleanly and the page renders (overview, hub, cycle and complexity details checked with a hand-written IR + --map).
  • Precision spot check: 300 random resolved records per repository (Commons Collections, MyBatis); in 0 of them was the target's simple name absent from the source file.

Recall gap: same-package references under an external supertype

When any type in a class's supertype chain is outside the analyzed sources, the body guard drops every reference that is not backed by an import — which includes all same-package references. This is the documented conservative behavior, but it hits the most common Spring/JPA shape: an in-repo base entity that implements Serializable.

In PetClinic, BaseEntity implements Serializable and Owner extends Person extends BaseEntity, so Owner → Pet, Owner → Visit, Pet → Visit and Pet → PetType (all in owner/, no imports needed) are not recorded. PetValidator implements Validator loses PetValidator → Pet the same way.

Rough size, using a heuristic (unresolved src/main record whose simple name matches exactly one in-repo src/main type that is in the same package or explicitly imported, and no resolved edge already covers that file pair):

Repository Resolved src/main file pairs Plausibly missed pairs
Spring PetClinic 33 7 (~17.5%)
Commons Collections 1,018 23 (~2.2%)
MyBatis 3 1,327 28 (~2.1%)

So the module structure of the two libraries is barely affected, but domain-model code in typical application repos can lose its core relations. Two possible directions, either of which would be fine for this PR's scope:

  1. Document it explicitly in JAVA.md as "same-package references are dropped when the supertype chain leaves the repository", with an example like the one above, so readers don't treat missing edges as absent coupling.
  2. Narrow the guard for well-known JDK supertypes. Most of them (Serializable, Comparable, Cloneable, AutoCloseable, Iterable, Runnable) declare no member types, so a small table of known java.* member type names would keep the guard sound — note Map does have Map.Entry, so this can't be a blanket java.* exemption.

Smaller notes

  • On MyBatis, 18 of 21 modules form one module cycle. It is info, which seems right, but the evidence shown is two adjacent import lines from one file (annotations/CacheNamespace.java lines 24–25), which doesn't explain how the cycle closes. Showing one representative edge per hop of the cycle would make it more useful.
  • I can confirm @tt-a1i's point 1: nothing in .github/workflows or scripts/run-tests.mjs references archify/modules/code-analysis/test, so the 208 module tests only run locally today.

Runs and analysis above were done with Claude (Opus 5.5).


Generated by Claude Code

scripts/run-tests.mjs enumerates only the root test/ directory, so the
module's own suite under archify/modules/code-analysis/test (including
the Java name-resolution and grouping tests) never ran in CI. Run it once,
in the first regression shard, and pin the step in the CI gate test.
A body whose supertype chain left the analyzed sources kept the
inheritance guard, so every same-package reference in it went unrecorded.
That hit the most common application shape, an entity base class that
implements java.io.Serializable.

A small table of JDK supertypes and the member types a subclass inherits
from them, generated from JDK 17, now completes such chains. Only positive
evidence counts: a single-type import or a fully qualified name. Implicit
java.lang and on-demand bindings keep the guard, because an unanalyzed
same-package source could declare a type of that name. Map's Entry stays
unresolved.
Java modules are inferred from build-file locations and source
directories, not from Maven or Gradle models, so adding a build module or
moving sources can change module findings without any dependency change.
Document this and how to pin modules.groups for longitudinal comparison.
grouping.java is now omitted when explicit groups claim every nested file.

tt-a1i commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Thanks for the focused follow-up at 4fc2738. I verified the current integration run, including the explicit Code Analysis module step: 209 tests passed with no failures in https://github.com/tt-a1i/archify/actions/runs/40133358396/job/120989657855 . The missing module-CI coverage finding is closed. The new listed-JDK-supertype handling also narrows the previously reported Serializable case; I am not carrying forward that blanket limitation.

The updated JAVA.md usefully distinguishes missing edges from absent coupling and explains stable explicit grouping for comparisons. Please align the PR's validation section with the current head and retain the already-requested javac harness/configuration/output links. I have not found a change to result-version/comparison handling, so please clarify the intended treatment of comparisons across the changed analysis semantics within this experimental branch.

No broader implementation or benchmark expansion is requested. The target remains labs/code-analysis; this acknowledgment is not merge approval or approval to expand into dev. Thank you for addressing the concrete findings.

…mports

A fully qualified supertype such as java.io.Serializable is no longer
treated as the JDK type: an unanalyzed type named like the first segment
could obscure the package (JLS 6.4.2). Single-type imports outrank every
competing binding, so they remain the only evidence accepted.

The body-guard example in JAVA.md now uses a case that still applies:
PetClinic's PetValidator implements Spring's Validator, so its use of Pet
is not recorded, while Owner's use of Pet is now resolved.
@CheeseMiao0520

Copy link
Copy Markdown
Author

Thanks both. Head is now b21b90b; the PR description's validation section is rewritten for this head, with CI, local and human checks listed separately.

@tt-a1i

  • javac harness, configuration and output: https://gist.github.com/CheeseMiao0520/28260df853ea2e564f4f564d48740954. The README defines the denominator and every category: unsupported forms, external and same-file bindings, compilation failures (each discarded program is listed with its first error) and intentional unknowns. It also separates zero uncounted drops from zero unresolved dependencies. The 15 runs at this head have 0 in every defect category, and 4,461 references bound to another analyzed file stay unresolved and counted as unknown. Each summary records both commits and their changed files (none), the JDK and Node versions and the harness hash.

  • One narrowing since 4fc2738: a fully qualified supertype such as implements java.io.Serializable no longer counts as the listed JDK type, since an unseen type named like the first segment could obscure the package (JLS 6.4.2). Only single-type imports count. On Dubbo, Commons Collections, MyBatis and PetClinic this removes no edge; Dubbo gains 4 unknown records. The JAVA.md example now uses PetClinic's PetValidator, because Owner → Pet resolves at this head.

  • Comparisons across the changed analysis semantics: base cannot produce Java results, so only results from this PR's own earlier revisions are affected. Between those revisions the analyzer version (0.1.0-dev.1) and the adapter id (java-parser@3.0.1) did not change, so comparability() would call such a pair comparable although resolution and default grouping changed. They are not meant as baselines. There are two ways to flag this mechanically:

    1. bump the module version, so every comparison across the upgrade reports analyzer-changed; JS/TS/Python comparisons already report config-changed after upgrading;
    2. give the Java adapter id a resolution revision, so only Java comparisons report adapter-changed.

    I'd lean to the second, since only Java results changed and JS/TS/Python comparisons need not be invalidated. I can add it to this PR if you agree, or leave versioning to the module branch's integration.

@yijiez666-alt

  • Recall gap: I took direction 2 with a table of listed JDK supertypes and documented the remaining gap as in direction 1. Only a single-type import counts as evidence. An implicit java.lang name or an on-demand import could be claimed by an unanalyzed same-package source, so those keep the guard.
  • PetClinic at this head: Owner → Pet, Owner → Visit, Pet → Visit and Pet → PetType are recorded. PetValidator → Pet is not, because Validator is a Spring type, and JAVA.md now uses it as the example of a missing edge that is not absent coupling.
  • Product file pairs against your 5a65da8 baseline: PetClinic 33 → 38 and MyBatis 1,327 → 1,337, with no resolved record lost or retargeted. I did not rerun your heuristic.
  • Cycle evidence per hop: agreed, it would make the MyBatis cycle readable. The cycle finding's evidence is shared by all languages, so I'd propose it as a follow-up rather than widen this PR.

Copy link
Copy Markdown
Collaborator

@CheeseMiao0520 thanks — re-checked at b21b90b and my points are resolved.

  • Recall gap: confirmed. Module suite 209/209. Product file pairs vs my 5a65da8 run: PetClinic 33 → 38, Commons Collections 1,018 → 1,030, MyBatis 1,327 → 1,337, with 0 resolved records lost or retargeted. My heuristic's "plausibly missed" pairs drop 7 → 2, 23 → 11 and 28 → 18. What remains is the documented conservative cases (implicit java.lang supertypes such as Iterable/Cloneable, and unlisted external types like Spring's Validator or InvocationHandler), which is fine for this PR.
  • Cycle evidence per hop: agreed as a follow-up.

On comparisons across the changed Java semantics: I'd suggest not handling it in this PR. The same gap exists for every language today — the extractor ids only track the external tool (typescript@<version>, python@<version>, java-parser@3.0.1), so a change to our own resolution logic in any adapter doesn't produce adapter-changed, and no rule exports version yet. I plan to open a separate PR that adds an adapter revision for all three languages and makes the rule-version convention checked rather than documentation-only. From my side this item shouldn't block #1075; @tt-a1i, let me know if you'd rather see it handled here.

Runs and analysis above were done with Claude (Opus 5.5).


Generated by Claude Code

@yijiez666-alt
yijiez666-alt merged commit d013f79 into tt-a1i:labs/code-analysis Oct 25, 2026
22 checks passed
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.

3 participants