Problem and reproduction
Kotlin's structural graph can produce an unsupported HIGH concern-overload finding when internal calls are not collected. This is a demonstrated coverage/calibration problem for the fixture, not evidence that the parser violates its documented import/inheritance contract or that the real application has only one responsibility.
Reproduction configuration: language=kotlin, algorithm=pkg, exclude_tests=true, use_cache=false, use_llm=false. With MCP, retrieve graph, architecture, smells and metrics through get_full_result in the same session.
Observed with v0.3.0 source on main 1b3532b:
- Three file imports become nine entity edges across a class and its two methods, including an empty method. These are file-import attributions, not nine resolved usages.
- A single-computation class has 41 methods and 40 explicit internal calls. The graph records none; its component receives HIGH with 42 entities and zero internal edges.
- Adding exactly those calls removes the finding.
- An independent source-import cycle remains detectable. This issue does not dispute the independently evidenced cycle or cover Listener/Domain hub findings.
MultiplayerArt Android at 31327fc159374b947822bff85d314626ba0d4d28 reproduces 1,793 entities / 27,909 edges: 27,842 import, 65 implements, 2 extends, no call edges. Mainactivity receives HIGH with 194 entities / 0 internal edges.
Draft reproduction PR #44 contains the standalone example and a deliberately failing regression test (no xfail). At publication: 3 characterization tests pass and the coverage-guard test fails; the focused suite has 129 passed / 1 deliberately failed. It is not a production fix.
Completed experiments
Semantic PoC source and instructions:
- Pinned Kotlin K2 compiler 2.0.21, JDK 17, and an IR compiler plugin collect calls before JVM lowering, including caller/callee, signatures, file, line and provenance.
- Separate bytecode inspection verifies emitted JVM calls; bytecode is not substituted for source semantics.
- Native JVM and CheerpJ produce identical 42 resolved source-call records, including 40 internal calls and correctly distinguished
Int/Long overloads.
- A negative fixture produces
COMPILATION_ERROR with unresolved-symbol diagnostics.
- Enriching the fixture's tree-sitter graph with actual compiler edges removes HIGH for the fixed Ui component. Component membership is held constant to isolate the detector from recovery heuristics.
| Measurement, two Ubuntu jobs |
Result |
| Native successful compile + semantic extraction |
2.95–3.00 s |
| CheerpJ successful compile + semantic extraction |
30.51–31.60 s |
| Entire job, including setup |
99–107 s |
| Downloaded compiler/dependency JAR sizes on disk |
65,514,736 bytes (~65.5 MB) |
| Sampled peak sum of harness/descendant RSS |
~1.7 GiB |
RSS sums shared pages potentially more than once, covers the harness rather than installation, and is not a separate native-versus-CheerpJ memory comparison. Compile timings exclude runtime initialization and the later negative compilation. Raw JSON and verification artifacts are attached to the run. No dependency cache was restored, but runners include preinstalled tooling.
Limits: builtins-only fixtures, explicit stdlib and -no-jdk; not Android/project-classpath proof. This is a compiler-plugin backend, not Kotlin Analysis API Standalone. Constructors, callable references, complete inline/lambda behavior, dynamic dispatch targets and Java–Kotlin interop remain unvalidated. No production graph integration has shipped.
Proposed direction
Keep tree-sitter as the default lightweight structural path. Add optional native-JVM semantic enrichment, preferably consuming an artifact from an existing Kotlin build so CI does not compile the project twice. CheerpJ works for the tested semantics but is about 10x slower for the successful compile stage here; do not make it the default CI backend.
Installing a JDK is necessary for compiler jobs but does not establish analysis correctness: compiler version, source sets, JDK/Android APIs and dependency classpath must also match. Structural-only jobs should not acquire JVM/browser dependencies.
Implementation plan — separate delivery PRs
1. Coverage contract and detector guard
2. Graph attribution and compatibility
3. Optional native semantic backend
4. CI and acceptance gates
Completion criteria
The unsupported HIGH regression is addressed with explicit coverage semantics; supported compiler edges are accurate and attributable; compile/classpath failures remain visible; structural-only analysis retains its lightweight dependency path. Java/interop support is claimed only after dedicated tests pass. Keep this issue open while implementation is pending; the successful PoC does not itself fix production analysis.
Problem and reproduction
Kotlin's structural graph can produce an unsupported HIGH concern-overload finding when internal calls are not collected. This is a demonstrated coverage/calibration problem for the fixture, not evidence that the parser violates its documented import/inheritance contract or that the real application has only one responsibility.
Reproduction configuration:
language=kotlin,algorithm=pkg,exclude_tests=true,use_cache=false,use_llm=false. With MCP, retrieve graph, architecture, smells and metrics throughget_full_resultin the same session.Observed with v0.3.0 source on main
1b3532b:MultiplayerArt Android at
31327fc159374b947822bff85d314626ba0d4d28reproduces 1,793 entities / 27,909 edges: 27,842 import, 65 implements, 2 extends, no call edges. Mainactivity receives HIGH with 194 entities / 0 internal edges.Draft reproduction PR #44 contains the standalone example and a deliberately failing regression test (no xfail). At publication: 3 characterization tests pass and the coverage-guard test fails; the focused suite has 129 passed / 1 deliberately failed. It is not a production fix.
Completed experiments
7a9189ebf725546270424c63fb4bdd78513fc3d2oncodex/cheerpj-ci-poc.Semantic PoC source and instructions:
Int/Longoverloads.COMPILATION_ERRORwith unresolved-symbol diagnostics.RSS sums shared pages potentially more than once, covers the harness rather than installation, and is not a separate native-versus-CheerpJ memory comparison. Compile timings exclude runtime initialization and the later negative compilation. Raw JSON and verification artifacts are attached to the run. No dependency cache was restored, but runners include preinstalled tooling.
Limits: builtins-only fixtures, explicit stdlib and
-no-jdk; not Android/project-classpath proof. This is a compiler-plugin backend, not Kotlin Analysis API Standalone. Constructors, callable references, complete inline/lambda behavior, dynamic dispatch targets and Java–Kotlin interop remain unvalidated. No production graph integration has shipped.Proposed direction
Keep tree-sitter as the default lightweight structural path. Add optional native-JVM semantic enrichment, preferably consuming an artifact from an existing Kotlin build so CI does not compile the project twice. CheerpJ works for the tested semantics but is about 10x slower for the successful compile stage here; do not make it the default CI backend.
Installing a JDK is necessary for compiler jobs but does not establish analysis correctness: compiler version, source sets, JDK/Android APIs and dependency classpath must also match. Structural-only jobs should not acquire JVM/browser dependencies.
Implementation plan — separate delivery PRs
1. Coverage contract and detector guard
insufficient coverageresult; an empty smell list alone must not imply a clean assessment.2. Graph attribution and compatibility
3. Optional native semantic backend
4. CI and acceptance gates
Completion criteria
The unsupported HIGH regression is addressed with explicit coverage semantics; supported compiler edges are accurate and attributable; compile/classpath failures remain visible; structural-only analysis retains its lightweight dependency path. Java/interop support is claimed only after dedicated tests pass. Keep this issue open while implementation is pending; the successful PoC does not itself fix production analysis.