Files
codegraph/.claude/handoffs/cross-language-impact-coverage-2026-06-04.md
T
07af3db6c7 feat(impact): cross-language blast-radius coverage (22 languages + 14 frameworks) (#708)
Completes the cross-file dependency graph behind impact / affected / explore across all 22 supported languages and 14 web frameworks, validated on real-world repos (measured fair-coverage table added to the README). Per-language resolution + framework resolvers/synthesizers (Lua/Luau require, Shopify OS 2.0 Liquid sections, Delphi forms, Rust cross-module + Rocket macros, Swift Fluent, SvelteKit/Nuxt loader/component conventions, RN/Expo bridges). 0 cross-family false edges, full suite green (1187 passed). See #708.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 11:02:59 -04:00

317 lines
83 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: cross-language-impact-coverage-2026-06-04
date: 2026-06-05 14:57
project: codegraph
branch: feat/cross-language-impact-coverage
summary: Per-language coverage DONE (all 15 README langs + static-member pass + RN/Expo 95%+). Since then the live front is ROUTE FRAMEWORKS: import/aggregator style at ceiling (Express/FastAPI/Flask/requests/NestJS/Gin/Axum 95%+, Vapor/vapor-til 100%, SvelteKit 100% fair [component core + loader→page synthesizer], React Router 100% [config-based, no miss], Nuxt 93.5% fair [+ nested-component fix]; Axum 100% via a Rust self-relative submodule-call fix), convention/reflection + actor style at an honest sub-95% ceiling (ASP.NET 83.9%, Spring 83.3%, Drupal 78.9%, Django 74.1%, actix 65.4% [actor message dispatch] — all measured). **SWEEP COMPLETE — every README framework measured;** Rocket lifted 62.5%→**93.8%** via a new `routes![]`/`catchers![]` macro extractor (only the crate-root lib.rs remains). Six engine improvements en route (3 Rust: self-relative + multi-segment `a::b::c()` module calls + Rocket route-macro extraction; 1 Swift: Fluent `@Siblings(through: Pivot.self)` metatype refs; 1 SvelteKit: `+page.server.js` `load`→`+page.svelte` synthesizer; 1 Nuxt: nested auto-imported `<MediaCard>`→`media/Card.vue` resolution). The static-member-pass-for-TS/JS/Python lever was **measured & rejected** (e7b86df — 0 coverage gain, the import edge already covers it, + graph noise). **Then the 4 niche README languages were knocked out**: Lua 31.6%→**84.2%** + Luau 12.5%→**92.2%** (new require resolver, 4155609); Liquid 39.1%→**73.8%** (Shopify OS 2.0 JSON-template section parsing, 2f57119); Pascal 73%→**75.7%** (.dfm/.fmx form↔code-behind pairing, 2f30a3b) — so **ALL 22 README "Full support" languages + all 14 README frameworks now have coverage validation (full README parity).** Branch 41 commits ahead of main, 3 behind, not merged.
---
# Handoff: Cross-language impact/blast-radius coverage campaign
## Resume here — read this first
**Current state:** Branch `feat/cross-language-impact-coverage` — now **41 commits ahead of `main`, 3 behind** (behind = 3 README-waitlist doc commits on main), tip `2f30a3b`, all pushed to `origin`=colbymchenry/codegraph. NOT merged (review branch). **FULL README PARITY: all 22 "Full support" languages + all 14 frameworks now have coverage validation.** Two arcs are done: (1) **per-language** cross-file coverage for ALL 15 README "Full support" langs + the static-member pass; (2) **cross-language RN/Expo** at 95%+ FAIR (gate hole fixed 082353e; same-dir include + KMP import 529d822). **Since the 2026-06-04 save a 12-commit ROUTE-FRAMEWORK front landed** (`61a993a``a3f59fb`) — see the "Route-framework headroom map" below; that map is the live edge. Working tree: clean except one unrelated untracked file (`assets/generate-waitlist.py`, README-waitlist tooling, not this campaign). Full suite green (**1187 passed | 2 skipped**, 59 files).
**Immediate next step:** **THE README SWEEP IS COMPLETE — all 22 "Full support" languages + all 14 frameworks have coverage validation.** Per-language DONE, RN/Expo DONE, route-framework + component-node front DONE, the 4 niche langs (Lua/Luau/Liquid/Pascal) DONE; **10 engine improvements** shipped this run (3 Rust + 1 Swift + 1 SvelteKit + 1 Nuxt + 1 Lua/Luau require resolver + 1 Liquid Shopify-JSON + 1 Delphi form pairing; the static-member-for-TS/JS/Python lever was measured & rejected). **Recommended: (A) ship it** — open the PR to `main` for the 41 commits; everything actionable is done. Optional follow-ups only: (B) build a convention/reflection lever if desired (Drupal `services.yml` DI, ASP.NET/Spring markup/reflection modeling) — each a large per-framework feature. **The convention/reflection frameworks (ASP.NET 83.9%, Spring 83.3%, Drupal 78.9%, Django 74.1%, actix 65.4%) sit at a genuine sub-95% static-analysis ceiling — don't chase 95% there without large reflection/markup modeling (metric-gaming otherwise). The TS/JS/Python static-member pass is measured-&-rejected (e7b86df), don't re-try.**
> Suggested next message: "Open the PR to main for the cross-language-impact-coverage branch (41 commits — full README parity: all 22 'Full support' languages + all 14 frameworks now have coverage validation)."
## DONE: coverage bump to 95%+ (commit 529d822) — RN/Expo multi-platform repos
**Goal (from user):** bump async-storage (75.0%) and rn-device-info (72.4%) to 95%+. Two parts — real engine fixes + an honest fair-metric (the original 75/72 counted generated/build/config/entry files as if they were source).
**Engine fixes (real coverage, generalizable):**
1. **Same-dir C/C++ `#include`**`#include "Foo.h"` had no directory awareness, so on a module with a same-named header per platform (windows/code/RNCAsyncStorage.h vs apple/) the includer landed on an arbitrary one (then the 082353e gate nulled the wrong-family match → real local header had 0 deps). Fixed C's quoted-include rule: resolve relative to the including file's OWN dir FIRST (`resolveViaImport` C/C++ branch in import-resolver.ts), plus a same-dir/proximity preference in `matchByFilePath`'s basename fallback (`pickClosestFileNode`).
2. **KMP commonMain import** — an `expect` decl + its `actual`s share one FQN across source sets; `resolveJvmImport` took `candidates[0]`, so one platform `actual` absorbed every common-side import and the `expect` looked unused. Now the same-FQN candidate CLOSEST to the importer (shared dir prefix, `expect` tiebreak) wins (`pickClosestJvmCandidate`). Both are the same "prefer the closest declaration on a name collision" principle as 082353e.
**Honest fair metric** (`/tmp/faircov.cjs`, prints every exclusion): denominator = authored source that *can* have an in-repo dependent. Excludes (per methodology, all auditable): structural (generated `.g.h`/codegen, `pch.*`, `*.gradle*`, `CMakeLists`, eslint/jest/babel config), see-through barrels (0 real symbols — web re-export files + umbrella/SDK headers ONLY; a 0-symbol *source impl* is counted as a real frontier zero, never hidden), and entry points (package `src/index`, platform `web`/`windows` entries, RN `ReactPackageProvider`).
**Before/after:**
| Repo | FAIR coverage before→after | residual zero (frontier) |
|---|---|---|
| async-storage | 75.0% → **97.4% (37/38)** | DatabaseFiles.kt (KMP expect-decl side, no in-repo caller) |
| rn-device-info | 72.4% → **95.2% (20/21)** | RNDeviceInfoCPP.cpp (`REACT_METHOD` macro methods not extracted) |
No regression (same metric, before→after): okhttp 75.9%→76.4%, kotlinx.coroutines 89.7% (neutral), leveldb 78.0% (neutral), redis 89.7%→89.9%, fmt 77.3% (neutral); cross-family false edges still 0 everywhere. 2 regression tests in `extraction.test.ts` ("Same-directory include + KMP import resolution"), both fail without the fix. Full suite 1169.
## DONE: gate hole (commit 082353e) — cross-family references/imports
**Symptom (was):** in `react-native-async-storage`, a TS `type TestRunner` and a Kotlin `class TestRunner` collided — TS `references`/`imports` resolved onto the Kotlin class (web→jvm false match). Plus `import React`↔Swift `React` and a C++ `#include "RNCAsyncStorage.h"`↔iOS ObjC header (basename collision).
**Root cause:** the false edges came from the FRAMEWORK strategy — React's `resolveComponent` (frameworks/react.ts) name-matches `getNodesByName` with NO language check; its COMPONENT_KINDS includes `class`, so it returned the Kotlin `class` @0.8 (the TS `type_alias` filtered out), outranking the cross-lang-penalized (0.5) TS name-match. AND `imports` were never gated (only `references` was). NOTE: `this.frameworks` in resolveOne is NOT language-filtered per-ref (`getApplicableFrameworks` is unused there), so react.resolve runs for EVERY ref — its `languages` field is dead in that path.
**Fix:** new `crossesKnownFamily(a,b)` (both in a known family jvm/apple/web/c AND different) wired into `gateFrameworkLanguage` (NEW — gates the framework strategy, refs+imports), `gateLanguage` (extended to also gate `imports`), and `applyLanguageGate` (name-match candidate filter — re-points instead of dropping). KEY RULE (non-obvious): the `references` gate stays STRICT (`!sameLanguageFamily`); `imports` + the framework gate use the WEAKER both-known rule, so config↔code bridges (yaml/blade side not a known family) and `.vue`/`.svelte``.ts` imports survive. `calls` bridges are never gated.
**Before/after — precision fix (coverage HELD/up, false edges → 0):**
| Repo | FAIR coverage before→after | cross-known-family false refs/imports |
|---|---|---|
| async-storage | 75.0% (39/52) → **75.0% (39/52)** | **22 → 0** |
| rn-device-info (control) | 69.0% (20/29) → **72.4% (21/29)** | **5 → 0** |
Coverage held on async-storage (no recall lost) and ROSE on rn-device-info (re-pointing gave a real same-family file a correct dependent). Legit JS↔native `calls` bridges intact (rn-device-info: 91 JS→Java, 37 JS→ObjC, full Java↔ObjC↔C++ pairing). 2 regression tests in `extraction.test.ts` ("Cross-language type/import gate"), both fail without the fix. Full suite 1167. Measure: `/tmp/faircov.cjs <repo>` (fair coverage + false-edge count) and `/tmp/xlang.cjs <repo>` (cross-lang edges by src→tgt × kind).
### Framework phase round 2 (commits d06a5ec, 74b599c, 2026-06-04)
(1) RCT_EXPORT_METHOD EXTRACTION (d06a5ec): RN bridge resolver now implements `extract()` for .m/.mm (added 'objc' to languages), reuses parseObjcRNExports to emit a method node per RCT_EXPORT_METHOD/REMAP (id `rn-export:`, named the JS-visible name). The macro parsed as ERROR before → iOS methods invisible. rn-device-info JS→objc 7→37, java↔objc pairs 22→29. (2) RN EVENT WRAPPER (74b599c): RN_NATIVE_SENDEVENT_RE catches `sendEvent(ctx,"X",body)` wrappers (inner `.emit` uses a variable) → native java/swift events now connect to JS hooks. Synth tag is `rn-event-channel`. VALIDATED async-storage (pairing + JS→native work; found the precision bug above).
### Classic RN cross-platform pairing (commit 4a64ca5, 2026-06-04)
`rnCrossPlatformEdges` (callback-synth): a native method (java/kotlin/objc/cpp) with a JS-side `calls` edge = confirmed bridge method → link to same-norm-name native method in another language (`getFreeDiskStorage:``getFreeDiskStorage`, first selector keyword), both directions. Skip RN_INFRA names (addListener/getConstants/getName/…). rn-device-info: 152 pairs (Java↔ObjC↔C++). FOLLOW-UP: RCT_EXPORT_METHOD isn't a node (macro/ERROR parse) → only regular `- (void)` ObjC methods pair today.
### Cross-language framework phase — round 1 (commit dbc4862, 2026-06-04)
NEW direction: RN/Expo repos where JS↔native crosses LANGUAGE boundaries. Existing bridge support is RICH (legacy NativeModules, TurboModule, Expo Modules extractor `expo-module:`-prefixed nodes, Fabric, rnEvents, swift-objc) — don't rebuild; validate + extend. Classic RN bridge WORKS (rn-device-info: 118 JS→Java + JS→ObjC calls). THREE Expo gaps fixed: (1) generic `AsyncFunction<Float>("x")` — regex didn't allow `<…>` so all Android Expo methods dropped; (2) cross-platform pairing — `expoCrossPlatformEdges` links Swift↔Kotlin impls of the same JS method (JS resolves to one platform only); (3) cross-lang type-ref precision — gated `references` edges to same language-family (name-matcher.ts `applyLanguageGate`/`sameLanguageFamily` + index.ts `gateLanguage`), so native `BatteryManager.EXTRA_LEVEL` doesn't falsely match a TS `BatteryManager`; framework resolvers NOT gated (keep config↔code bridges). Measure: `/tmp/xlang.cjs`. Detail in memory.
### Objective-C result (commit 33ce431, 2026-06-04)
WORST README language at baseline. FOUR fixes (3 in tree-sitter.ts, 1 in name-matcher.ts): (1) SINGLE-ARG SELECTOR — `[c storeImage:k]` was named `storeImage` (no colon) at the call site, never matching `storeImage:`; add `:` when the message has a `:` token. (2) CLASS-MESSAGE RECEIVER REF — `[Foo sharedCache]`/`[[Foo alloc] init]` now emits a `references` edge to the capitalized class (covers the header). (3) #IMPORT BASENAME — `#import "Foo.h"` resolves to the header via matchByFilePath relaxed to accept bare filenames w/ short ext. (4) CLASS-METHOD COLON — `Foo.storeImage:` now resolves (broadened matchMethodCall method regex to allow colon selectors). AFNetworking 50%→**90%**, SDWebImage Core 33.8%→**91.6%**. GOTCHA: SDWebImage `include/SDWebImage/*.h` are SYMLINKS to `Core/` — measure Core/ only. Residual = public-API category methods called by app code (frontier). Detail in memory.
### Dart result (commit 9487954, 2026-06-04)
Dart was in TYPE_ANNOTATION_LANGUAGES but produced ZERO `references` edges, AND mixins were dropped. (NOTE: dio raw 67.8% was example-dir pollution — real 86.4%.) Two gaps, gated `language==='dart'`: (1) MIXINS — `with` mixins live in a `mixins` CHILD of `superclass`; generic path read namedChild(0) as base + dropped mixins (and `class C with M` misread mixins as superclass). Dart branch in extractInheritance: `extends` base + `implements` per mixin. (2) METHOD TYPE REFS — `method_signature` wraps the real `function_signature` (params/return there) + return is a bare `type_identifier` not a `type` field. Dart branch in extractTypeAnnotations: descend to inner signature → extractTypeRefsFromSubtree. flutter/packages 88.8%→**92.4%**, dio 86.4%→**87.9%**. Residual = export barrels + platform-conditional files + enum-value access (`Enum.value` — value-read frontier; a Dart `Capitalized.member`→ref pass would be precise, the top follow-up). Detail in memory `impact-coverage-findings.md`.
### Static-member / value-read pass (commit 857baf7, 2026-06-04)
The deferred cross-language lever, now DONE. A type used only via a static member / enum VALUE (`MediaKind.video`, `Colors.red`, `JsonScope.NAME`, `Foo::BAR`) recorded no edge (body walker only did CALLS + `new`). `extractStaticMemberRef` (tree-sitter.ts, in visitFunctionBody) emits a `references` edge to the CAPITALIZED receiver of a member-access value read (per-lang node in MEMBER_ACCESS_TYPES: field_access Java / member_access_expression C# / navigation_expression Kotlin+Swift / field_expression Scala / class_constant_access_expression+scoped_property_access_expression PHP / qualified_identifier C++; Dart = identifier + sibling value-read selector). Skips call callees; gated to STATIC_MEMBER_LANGS={java,csharp,kotlin,swift,scala,dart,php,cpp} — TS/JS/Python EXCLUDED (high coverage + retrieval-perf-sensitive). flutter/packages 92.4%→93.2%; additive elsewhere; nodes stable. Detail in memory.
### C/C++ result (commit ec8fe3f, 2026-06-04)
C/C++ were already HIGH (name-matching resolves cross-file calls across the .h/.c split). NOT an import gap. The systematic gap was a C++ EXTRACTION BUG in languages/c-cpp.ts: `extractCppQualifiedMethodName`/`extractCppReceiverType` BFS'd the whole declarator INCLUDING `parameter_list` + `trailing_return_type` for a `qualified_identifier` → a free function `std::string TableFileName(const std::string& dbname)` was named **`string`** (from the param type), `auto f() -> std::string` named `string` (trailing return). Calls never resolved; defining file looked dependent-less. Fix: shared `findDeclaratorQualifiedId` skips `parameter_list` + `trailing_return_type`; plain names fall back to default extraction. leveldb 91.7%→**94.8%**, fmt 32 mis-named→1, redis (C, unaffected) 92.2% at ceiling. Residual = generated tables, macro-reached, function-pointer dispatch (`MAKE_CMD(...,sortCommand,...)` — deferred, broad/risky), C++ namespaces (deferred). Detail in memory `impact-coverage-findings.md`.
### Ruby result (commits 44fb978 + 5bccab6, 2026-06-04)
TWO gaps. (1) MIXINS (44fb978): `include`/`extend`/`prepend Mod` parsed as a bare `call` to method `include` → ZERO edges. Fix in languages/ruby.ts visitNode: detect bare include/extend/prepend (guard `!receiver` so `arr.include?(x)` is safe) → emit `implements` edge class/module→module. (2) REQUIRE RESOLUTION (5bccab6, bigger than expected): `require "lib/foo"` → emit `imports` ref `lib/foo.rb` (load-path, suffix-matched by matchByFilePath); `require_relative "../foo"` → resolve vs requiring file's dir (`path.posix.normalize`); bare `require "json"` skipped. Resolves to the FILE node. **sidekiq 71%→76.8% (mixins)→100% (requires); activerecord 84.8%→93% (mixins)→96.8% (requires)** — Rails autoloads but still has explicit requires for sub-components. Residual = `constantize` class-string instantiation (associations/arel), generators, version files. Detail in memory `impact-coverage-findings.md`.
### PHP result (commit acfb444, 2026-06-04)
ROOT CAUSE: PHP ignored NAMESPACES — every class qn was the bare simple name, so laravel's 7+ same-named `Factory` interfaces across namespaces collapsed to one arbitrary match, and `use` imports never resolved. Fixes (gated `language==='php'`): (1) **namespace capture**`packageTypes:['namespace_definition']`+`extractPackage` in languages/php.ts → classes scoped to `Foo\Bar::Class`; (2) **use-import resolution**`emitPhpUseRefs` emits an `imports` ref in `Foo\Bar::Baz` form, matched precisely by the resolver's `resolveQualifiedName` (THE big lever, 80.5%→94.9%); (3) **type-hint refs** — PHP-aware `extractPhpTypeRefs` (PHP types are `named_type`/`union_type` wrapping `name`, not `type_identifier`). guzzle 95.2%→**100%**, laravel 80.5%→**94.9%**. Residual = class-string/reflection wiring (service providers, facades, middleware) — genuine frontier. Detail in memory `impact-coverage-findings.md`.
### Scala result (commit b5489d9, 2026-06-04)
Scala was the WORST starting point — extraction made nodes but almost NO edges for typeclass code (cats 1.66 edges/node). Not one gap but a family, all gated to `language==='scala'` in `extraction/tree-sitter.ts` (+ `languages/scala.ts`): (1) **parameterized extends**`extends A[X] with B` packed all supertypes in one `extends_clause`; generic path took only namedChild(0) w/ full text `A[X]` so no typeclass matched → new shared `scalaBaseTypeName` unwraps `generic_type`, iterate all supertypes (cats 48.9%→77.2% from THIS alone); (2) **type refs** (Scala had ZERO `references`) — added scala to TYPE_ANNOTATION_LANGUAGES + walk EVERY curried `parameters` list (trailing `(implicit M: TC[A])`!) + `type_parameters` context bounds (`[A: Monoid]`) + val/var types from scala.ts (77.2%→89.2%); (3) **instantiation** `new T[...]` = `instance_expression`. cats 48.9%→**89.2% fair** (82.1% raw — scalafix/bench excluded), gatling 76.3%→**91.2%**. Residual = cross-build variants/laws/wildcard-barrels (frontiers). Detail in memory `impact-coverage-findings.md`.
### Kotlin result (commit d8a2e91, 2026-06-04)
Systematic Kotlin gap = **Kotlin Multiplatform `expect`/`actual`** (the only Kotlin-unique construct). OkHttp (the README Kotlin benchmark) was ALREADY 96.2% out of the box; kotlinx.coroutines (KMP) was 76.8% → **93.5%**. Fix: new generic `extractModifiers` hook captures `expect`/`actual` (from `modifiers > platform_modifier`) onto the node's `decorators` list (wired once in `createNode`); `kotlinExpectActualEdges` in callback-synthesizer.ts links common decl → each platform `actual` as a heuristic `calls` edge (matched by qualified_name + the `actual` marker; decl side = non-`actual` same-qn node, which also gates out plain overloads; kind-widened so `expect class``actual typealias` links). Node count stable. Residual = genuine frontiers (expect-decl sides, ServiceLoader/agent SPI, test infra). Full detail in memory `impact-coverage-findings.md`.
## Goal
Make the engine's cross-file dependency graph complete for **every README "Full support" language**, so impact/`affected`/callers/callees/explore all see real dependencies. Definition of done per language: a real repo's symbol-bearing files mostly have correct dependents; residual is only genuine frontiers (no-symbol files, entry points, value-reads, macros). Each language: audit → fix → validate → commit to the branch.
## Methodology (apply per language — this is the loop)
1. Clone 1 benchmark + 1 clean repo to `/tmp`. Index with `CodeGraph.initSync(repo,{config:{include:['**/*.<ext>'],exclude:[]}})` + `indexAll()` + `resolveReferences()` via a `node -e` against `dist/index.js`.
2. Measure **fair coverage** = % of *symbol-bearing* source files with ≥1 cross-file dependent. SQL: a file is a dependent target if it's the `target` of a non-`contains` edge whose `source` is in another file. **EXCLUDE from the denominator:** files with no non-`file` node (package-info.java, doc.rs, `__init__` umbrellas), tests, entry points (main/bin/examples/benches/fuzz/samples), and miscounted other-language files (e.g. `.kt` under a Java repo).
3. Audit the 0-dependent files → classify real-miss vs frontier. Controlled probe (2 tiny files) to isolate the exact gap.
4. Fix extraction/resolution. Re-measure. **Verify node count stays stable** (edges added, not nodes — except real new symbols like interface/record nodes).
5. `npm run build` (tsc must pass) → `npm test` (expect ~1151 passing) → add a test in `__tests__/extraction.test.ts` → CHANGELOG `[Unreleased] → Fixes` bullet.
6. `git add <files>` + commit (Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>) + `git push origin feat/cross-language-impact-coverage`. Do NOT commit `.claude/handoffs/*`.
## Key findings — the recurring gap shapes & where they're fixed
- **Foundation (16b5633):** `imports` edges are same-file (file→local import node), so the old `getFileDependents` returned 0 for every file. Added `getDependentFilePaths`/`getDependencyFilePaths` in `src/db/queries.ts` (indexed JOIN, all kinds except `contains`); `src/graph/queries.ts` delegates.
- **Import/binding linking** (per-lang emit* in `src/extraction/tree-sitter.ts`): `emitImportBindingRefs` (TS/JS named/default/namespace), `emitReExportRefs` (TS `export {X} from`), `emitPyFromImportRefs` (Python `from m import X`), `emitRustUseBindingRefs` (Rust `use`/`pub use`, emits FULL path). All gated by language in `extractImport`.
- **Module-path resolution** (`src/resolution/import-resolver.ts`): `resolvePythonModuleMember` + `resolveModuleImportToFile` (Python+TS namespace), `resolveGoCrossPackageReference` (Go, pre-existing), `resolveRustPathReference`+`resolveRustModuleFile`+`rustCrateRootDir`/`rustSelfModuleDir` (Rust `crate::`/`self::`/`super::`). Resolve a path's module PREFIX to a file, find the leaf there — fixes common-name collisions.
- **Instantiation:** `INSTANTIATION_KINDS` in tree-sitter.ts now includes `composite_literal` (Go) + `struct_expression` (Rust). `extractInstantiation` keeps the package qualifier for Go (cross-pkg resolve); strips for others. Also normalizes parenthesized type conversions `(*T)(x)`.
- **Interface/trait dispatch (#584):** `IFACE_OVERRIDE_LANGS` in `src/resolution/callback-synthesizer.ts` now includes `go` and `rust`. Needs the interface/trait's METHODS extracted: Go via `extractGoInterfaceMethods` (tree-sitter.ts), Rust via adding `function_signature_item` to rust.ts function/methodTypes. `goImplementsEdges` synthesizes Go implicit `implements` edges (method-set match) and must `insertEdges` FIRST in `synthesizeCallbackEdges`.
- **Annotations / attributes / property wrappers (UNIFIED via `extractDecoratorsFor`):** it now (a) descends into `modifiers` nodes (Java/Kotlin/C#), (b) recognizes Swift `attribute` + `user_type`. Java needed `annotation_type_declaration` added to `interfaceTypes` (java.ts). C# needed `record_declaration`/`record_struct_declaration` (csharp.ts). Swift needed a dispatcher branch running `extractDecoratorsFor`+`extractVariableTypeAnnotation` on `property_declaration` inside a type (Swift instance props aren't nodes).
- **In-body type annotations (TS):** `visitFunctionBody` now extracts `variable_declarator` type annotations (`const x: Foo`).
## Per-language results — file-dependent coverage (% of symbol-bearing source files with ≥1 cross-file dependent)
| Language | Repo | Before | After | Key fix |
|---|---|---|---|---|
| TypeScript/JS | codegraph (this repo) | 62.5% | **95.8%** | import + re-export + namespace linking; in-body type annotations |
| Python | requests | 54.1% | **100.0%** | `from x import` linking; `from . import sub` + `sub.f()` module-member resolution; relative-dot path fix |
| Python | flask (src) | 66.7% | **87.5%** (true ceiling — residual all correct-0) | (same) |
| Go | gin | 62.7% | **96.6%** | composite literals → instantiates; package-level var registries; `(*T)(x)` conversions; implicit interface satisfaction (#584) |
| C# | MediatR (library) | 81.5% | **85.2%** | `record` / `record struct` indexed (#237) |
| Rust | ripgrep | 63.4% | **86.7%** | struct literals; trait dispatch (trait methods + #584); `use`/`pub use` linking; module-path resolution for `pub use self::x::y` |
| Rust | tokio (src) | 70.0% | **81.9%** | (same — number is honest/precise; earlier leaf-only match had inflated it) |
| Java | gson | 78.2% | **85.1%** (raw) · **93.3% fair** | annotations: index `@interface` defs + link `@Foo` usages (in `modifiers`) |
| Java | retrofit | 80.5% (raw) | **94.9% fair** | (same) |
| Swift | Alamofire | 93.0% | **95.3%** | property wrappers / attributes (`@Argument`/`@Published`/`@objc`) |
| Swift | swift-argument-parser | 84.6% | **96.2%** | (same) |
| Kotlin | OkHttp | 96.2% | **96.2%** | already at ceiling (JVM, barely uses KMP) — no change needed |
| Kotlin | kotlinx.coroutines | 76.8% | **93.5%** | Kotlin Multiplatform `expect`/`actual` linking (incl. `actual typealias`) |
| Scala | typelevel/cats | 48.9% | **89.2% fair** (82.1% raw) | parameterized extends + type refs (implicit/context-bound) + `new` |
| Scala | gatling | 76.3% | **91.2%** | (same) |
| PHP | guzzle | 95.2% | **100.0%** | namespace capture + `use`-import resolution |
| PHP | laravel/framework | 80.5% | **94.9%** | namespace capture (disambiguates same-named contracts) + use-imports + type-hints |
| Ruby | rails/activerecord | 84.8% | **96.8%** | mixin edges (`include`/`extend`/`prepend`) + require resolution |
| Ruby | sidekiq | 71.0% | **100.0%** | mixins + `require`/`require_relative` → file resolution |
| C++ | google/leveldb | 91.7% | **94.8%** | fix free-function name extraction (was named after param/return type) |
| C | redis | 92.2% | **92.2%** | already at ceiling (C unaffected; residual = generated/macro/fn-ptr) |
| Dart | flutter/packages | 88.8% | **92.4%** | `with` mixins + method type references |
| Dart | dio | 86.4% | **87.9%** | (same; raw 67.8% was example-dir pollution) |
| Obj-C | AFNetworking | 50.0% | **90.0%** | single-arg selectors + class-receiver refs + #import + class-method resolution |
| Obj-C | SDWebImage (Core) | 33.8% | **91.6%** | (same; `include/` dirs are symlink dups — measure Core/) |
| Lua | nvim-telescope | 31.6% | **84.2%** | `require()` module resolution: dotted `a.b.c``a/b/c.lua` + instance-path leaf, path-suffix match w/ same-dir preference (4155609). Residual = telescope's dynamic `setmetatable` lazy-require pickers (frontier). |
| Luau | dphfox/Fusion | 12.5% | **92.2%** | (same require resolver — `require(script.Memory.deriveScope)`) |
| Liquid | Shopify/dawn | 39.1% | **73.8%** | Shopify OS 2.0 JSON-template section parsing: index `templates/*.json` + section-group `sections/*.json` (incl. nested `templates/customers/`), link each `"type"``sections/X.liquid` (2f57119). Snippet `{% render %}` already worked. Residual = preset/theme-editor sections (no static ref) + dynamic `{% render block %}`. |
| Pascal/Delphi | PascalCoin | 73.0% | **75.7%** | `uses`-resolution ALREADY worked (73%); + `.dfm`/`.fmx` form ↔ same-basename `.pas` code-behind pairing (2f30a3b). Residual = vendored third-party libs (`src/libraries/*` internal units, ≈node_modules) + dynamic form/RPC instantiation. |
**"raw" vs "fair":** "fair" excludes files that *structurally can't* have dependents (no-symbol files like `package-info.java`/doc-only, entry points, tests, and other-language files miscounted by the include glob). For Java the raw numbers were heavily polluted (gson had many `package-info.java`; retrofit had `.kt` + samples), so the fair number is the real one (~9395%). The other languages' numbers above are already on symbol-bearing source files (effectively "fair"). C# MediatR's 85.2% is the library-only figure; a package-info-excluded "fair" wasn't separately computed but is higher.
## Per-framework results — cross-language file-dependent coverage (RN/Expo, multi-platform JS↔native)
| Framework | Repo | Before | After | Key fix |
|---|---|---|---|---|
| React Native / Expo | react-native-async-storage | 75.0% | **97.4% fair** (37/38) | cross-family gate (082353e) + same-dir C/C++ `#include` + KMP commonMain import (529d822) |
| React Native | react-native-device-info | 72.4% | **95.2% fair** (20/21) | cross-family gate (082353e) + honest fair metric (its 529d822 engine-fix targets are excluded entry files) |
**Metric note (read before trusting the "Before"):** the "Before" 75.0%/72.4% used an **under-exclusive** denominator — it counted generated codegen (`.g.h`), build scripts (`pch.*`, `*.gradle.kts`), tooling config (eslint/jest/yarn), and platform/registration **entry points** as if they were source. The "After" uses the **honest fair metric** the per-language table uses: excl. structural (generated/build/config/test), see-through barrels (web re-export files + umbrella/SDK headers — but NOT a 0-symbol source impl, which is a real frontier), and entry points (package `src/index`, platform `web`/`windows` entries, RN `ReactPackageProvider`). **Apples-to-apples** (fair metric held constant, isolating just the 529d822 engine fixes): async-storage **92.1% → 97.4%** (+RNCAsyncStorage.h via same-dir include, +Platform.kt via KMP import); rn-device-info **95.2% → 95.2%** (neutral — its same-dir/KMP targets are excluded entry headers, so its lift to 95.2% was the metric correction + the 082353e gate). Residual zeros (real frontiers): async-storage `DatabaseFiles.kt` (KMP `expect`-decl side, no in-repo caller); rn-device-info `RNDeviceInfoCPP.cpp` (`REACT_METHOD` macro methods not extracted). Measure with `/tmp/faircov.cjs <repo> --list`. No regression on controls: okhttp 75.9→76.4, kotlinx.coroutines 89.7 (neutral), leveldb 78.0 (neutral), redis 89.7→89.9, fmt 77.3 (neutral); cross-family false edges 0 everywhere.
## Route-framework headroom map (canonical app per README framework, FAIR coverage)
Measured 2026-06-04 (commit 61a993a) on a canonical real app for each README route framework. This is the active front of the campaign — the unmeasured frameworks have the real headroom.
| Framework | App repo | FAIR coverage | Status / next |
|---|---|---|---|
| Express (TS) | express-realworld | 70.4% → **100%** ✅ | DONE (2a0b6e0): renamed default-import → module file (route controllers `export default router`). |
| FastAPI (Py) | fastapi-realworld | 78.6% → **98.0%** ✅ | DONE (2835623): source-aware `from pkg import submodule` (router aggregator). 1 residual = aliased sub-aggregator. |
| Flask (Py) | flask (lib) | **100.0%** ✅ | DONE (entries/barrels excluded) |
| requests (Py) | requests (lib) | **100.0%** ✅ | DONE |
| NestJS (TS) | nestjs-realworld | 93.8% → **96.8%** ✅ | DONE (main.ts entry excluded) |
| Gin (Go) | gin (lib) | **96.5%** ✅ | DONE (faircov Go `_test.go` exclusion) |
| Laravel (PHP) | laravel (lib) | 92.0% | done (per-language; app not separately measured) |
| Rails (Ruby) | rails (lib) | 89.6% | done (per-language) |
| Django (Py) | django-realworld | 45.9% → **74.1%** | PARTIAL (58dc463): abs-module-import + `include('app.urls')` done. Ceiling ~83% w/ entries excluded. FRONTIERS: signals via in-body `ready(): import myapp.signals` (Python in-body imports NOT extracted — visitFunctionBody walks calls but not import_statement); DRF/string-config exception classes (`EXCEPTION_HANDLER: '...'`). |
| ASP.NET (C#) | eShopOnWeb | 59.3% → **83.9%** | chained extension calls (4c14413) + framework-entry exclusions (b) + Razor/Blazor markup parser (59b8de2 tags/@model + 90c5f39 @code) + **C# namespaces (dc7d033) + Razor `@using` disambiguation (9e5a951)** — DTOs now resolve to `BlazorShared.Models::CatalogBrand` not the same-named entity. C# constructor DI / interface→impl ALREADY worked. Residual ~24 = reflection/proxy (AutoMapper profiles / Swagger filters / middleware / health checks — invoked by reflection, a separate modeling feature) + a few C# static-const reads (`Constants.X` — extend the static-member pass to C#). |
| Spring (Java) | spring-petclinic | 65.2% → **83.3%** ✅ | DONE — convention/reflection ceiling, on par with ASP.NET. 30 main java = 15 covered + 7 entry (1 `*Application` main + 6 `*Controller`) + 5 barrel (`package-info.java`) + 3 zero. 3 residual zeros all reflection-registered: `CacheConfiguration`/`WebConfiguration` (`@Configuration`, component-scanned — `WebConfiguration` is even `@SuppressWarnings("unused")`) + `PetClinicRuntimeHints` (`@ImportRuntimeHints(X.class)` class-literal = the documented `Foo.class``Class` frontier). faircov fix = barrel-exclude `package-info.java`/`module-info.java` shells. **No `samples`-dir exclusion ever existed in the script** — that handoff note was inaccurate. |
| Axum (Rust) | realworld-axum-sqlx | 72.7% → **100%** ✅ | DONE (a3f59fb) — import/aggregator style, confirmed in Rust. REAL engine miss found+fixed: bare Rust `submodule::fn()` calls (the `X::router()` router-assembly pattern) resolved crate-relative only → now self-relative FIRST (current module), then crate fallback. + `main.rs`/`src/bin/*.rs` faircov entry exclusion. Clean A/B (same faircov, engine ±fix): axum +2 → 100%, ripgrep neutral (66/79), tokio +3 (260/321); cross-family false edges 0. Regression test fails without the fix. |
| Rocket (Rust) | TatriX/realworld-rust-rocket | 62.5% → 68.8% → **93.8%** ✅ | DONE — TWO engine wins. (1) 7bb958b: a 3-segment `database::profiles::find()` call (inside `db.run(\|c\| …)`) was dropped by the resolver PRE-FILTER (leaf never checked) → +database/profiles.rs. (2) 6d214cd: NEW `routes![]`/`catchers![]` macro extractor — the handler paths live in a raw token-tree, so the 4 `routes/*.rs` handler modules (mounted at runtime) looked uncalled; now reconstructed from the macro + resolved via the Rust path resolver → all 4 covered. Only `lib.rs` (crate-root/launch hub, see-through) remains. No control regression; false edges 0; nodes unchanged (edges only). |
| actix-web (Rust) | fairingrey/actix-realworld-example-app | **65.4%** | DONE (genuine ceiling). actix **1.x ACTOR style**: routing resolves fine (`.route(web::post().to(users::register))` → handlers covered), but the DB layer is reached via actor message dispatch (`db.send(RegisterUser)``impl Handler<RegisterUser> for DbExecutor`) — a DYNAMIC runtime boundary, no static edge (the actix analog of the MediatR/reactive runtimes the engine deliberately leaves uncovered). 9 zeros = 5 `db/*.rs` (actor dispatch) + `models/mod`/`utils/mod`/`prelude` (glob `pub use self::x::*` re-export roots) + `utils/hasher` (reached only via that glob). Bridging needs an actor-message synthesizer (dynamic-dispatch feature), not a static-resolution fix. |
| Vapor (Swift) | vapor-til (canonical) | 85.7% → **100%** ✅ | DONE — import/aggregator confirmed in Swift. Controllers covered via `app.register(collection: X())`, models via controller queries + migrations. REAL miss found+fixed (bb7659e): the many-to-many pivot model was referenced ONLY via `@Siblings(through: Pivot.self)` — a metatype arg inside a property-wrapper attribute that wasn't walked → now routed through `extractStaticMemberRef` → pivot covered. + faircov excl `Package.swift` (SPM manifest) & `Public/` (static assets). A/B (same faircov): vapor-til +1 → 100%, Alamofire neutral (44/50); false edges 0. |
| Vapor (Swift) | penny-bot (real prod, 166 files) | **73.2%** | SECONDARY — a serverless/DI architecture, NOT the canonical route→controller→model shape. 37 zeros = AWS Lambda handlers (`*Handler`/`*Lambda`, invoked by the AWS runtime → entry points, no in-repo caller) + `exports.swift` re-export barrels (see-through, like Rust mod.rs roots) + Swift extension files (`+X.swift`) + DTOs/services reached via DI / JSON-decoding (genuine frontiers). Excluding the Lambda entries + exports barrels (faircov gaps) → ~80%; genuine residual is the DI/serialization architecture. NOT chased — vapor-til is the canonical result. |
| SvelteKit (Svelte) | sveltejs/realworld (official) | 35.9% raw → **100% fair** ✅ | DONE — characterization, NO engine miss (first framework that needed none). The `.svelte` import resolution ALREADY connects the whole component graph (page→component→lib: ArticlePreview←ArticleList, Nav←+layout, ListErrors←4 pages — imports+references+calls all resolve). Low RAW = the file-convention shape: 25/39 files are SvelteKit convention ENTRIES (`+page`/`+layout`/`+error`/`+server`/`hooks` — loaded by the framework BY PATH, no in-repo caller = route leaves, like route handlers). Excluding those (new faircov ENTRY patterns) → 14/14 = 100% of the coverable core. ONE framework-mediated gap: `+page.server.js` `load``+page.svelte` `data` is NOT a static link (runtime `data` prop) — was bridged with a SvelteKit convention synthesizer (sibling files by path, like Rocket routes!) — impact-granularity, not coverage (both files are entries). **BUILT (3ea03e5): `svelteKitLoadEdges`** links each page component → its OWN-dir loader's `load`/`actions` (same for `+layout`); path-deterministic (19 links / **0 cross-dir mislinks** on realworld), provenance heuristic, nodes unchanged. `getImpactRadius(load)` now surfaces its page. |
| React Router (React) | gothinkster realworld | **100%** ✅ | DONE — characterization, NO engine miss. The CONFIG-based component-node sub-shape: route components are NAMED in source (`<Route path='/login' component={Login}/>` → App.js imports + references Login), so they resolve cleanly — verified Login/Editor/Profile all covered BY App.js's route config (imports+references). 29/29 (only `index.js` CRA entry + 8 anonymous-default-export reducers [0 named symbols → barrels] excluded). CONTRAST with SvelteKit (file-convention, needs entry-exclusion): the component-node category has TWO sub-shapes, both handled by the engine as-is. (App is old v4/v5 `component={X}`; modern v6/v7 `element={<X/>}` + data-router `{element, loader}` are also source-referenced → same conclusion.) No commit (nothing to fix). |
| Nuxt (Vue) | nuxt/movies (official demo) | 47.6% → **93.5% fair** ✅ | DONE — file-convention component framework (like SvelteKit) but with an AUTO-IMPORT twist that was a REAL miss (1dec765). Nuxt auto-imports a NESTED component by a DIR-PREFIXED name: `components/media/Card.vue``<MediaCard/>`, but the node is named `Card` → the PascalCase template usage never resolved (the synth only matched kebab tags; the extractor's PascalCase ref name-matched `MediaCard`→nothing). Fix in `vueTemplateEdges`: match PascalCase tags + a `nuxtComponentName` map (`media/Card.vue``MediaCard`, incl. Nuxt dir de-dup) → +9 nested components. + faircov Nuxt entry exclusions (pages/, app.vue, error.vue, layouts/, plugins/, middleware/, server/). 2 residual zeros = genuine frontiers (unused `<CarouselItems>` variant + unimported `constants/images.ts`). Composables auto-import (`useTmdb()`) already resolved by name-match. false edges 0, nodes unchanged. **vs SvelteKit:** SvelteKit needed NO component fix (svelte imports resolve); Nuxt DID (its auto-import dir-prefix naming is unique). |
| Drupal (PHP) | token (contrib module) | 57.1% raw → **78.9% fair** | DONE — the MOST reflection-heavy framework, a genuine sub-95% ceiling (≈ASP.NET 83.9%, Django 74.1%). Static parts WORK: `*.routing.yml`→controller (drupal.ts resolves it — `TokenTreeController` covered via the route ref), PHP class refs (namespaces/`use`/type-hints). 4 residual zeros all DI/reflection-wired: 2 services in `services.yml` (`TreeBuilder` = `@token.tree_builder`, `TokenFieldRender`), a DYNAMICALLY-routed controller (`TokenDevelController` via `RouteSubscriber`, not static YAML), a field-plugin class (`MenuLinkFieldItemList` list_class). + faircov excl JS libraries + convention entries (`.install`/`.module`, `src/Plugin`/`Element`, Drush, `*ServiceProvider`, `Routing`/`*Subscriber`). LEVERS (drupal.ts TODOs, large per-mechanism features, NOT chased): model `services.yml` DI (→ covers the 2 services), plugin annotations + modern `#[Hook]` attributes. No commit (no simple miss). |
**RESULT: all import/aggregator-style frameworks are at 95%+** (Express 100%, FastAPI 98%, Flask/requests 100%, NestJS 96.8%, Gin 96.5%, Axum 100%).
**Option (b) DONE (metric-only — framework-entry exclusions in `/tmp/faircov.cjs`):** added convention-entry patterns (`*Controller.cs/java`, `*.cshtml.cs`, `*Endpoint.cs`, EF `Data/Config/*.cs`, `Program/Startup.cs`, `*Application.java`, Django `admin.py`/`apps.py`). Result — convention frameworks rise but **still cap well below 95%**: ASP.NET 65.3% → **77.2%** (50 entries excluded), Spring petclinic 65.2% → **83.3%** (after the `package-info.java` shell-exclusion fix below), Django **74.1%**. The import-style frameworks are unaffected (Express 100%, FastAPI 98%, NestJS 96.8%, Gin 96.5% — the C#/Java/Django entry patterns don't touch them).
**WHY (b) doesn't reach 95% — the honest ceiling:** after excluding routed/reflection-registered entries, the residual zeros are **markup-driven** code-behind (Blazor `.razor` / Razor `.cshtml` / Thymeleaf reference the `.cs`/`.java`, but the markup isn't parsed → ViewModels, DTOs, components look unused) and **reflection/proxy** code (Spring Data repository proxies, AutoMapper profiles, Swagger filters, DI/middleware registration, Django signals/string-config). These are genuine static-analysis frontiers — reaching 95% needs (1) parsing template markup to link markup→code, or (2) per-framework reflection/proxy modeling — both large features. **Excluding markup-driven business code (DTOs/ViewModels) from the metric to fake 95% would be gaming — NOT done.** Note: business LOGIC (services, repos) IS covered in all three; the residual is leaf views/DTOs/configs whose impact is captured the other direction (route→handler).
**Generalizable engine fixes shipped this campaign (all benefit beyond their trigger framework):** Python absolute `import a.b.c` (61a993a); source-aware `from pkg import submodule` (2835623); Django `include('app.urls')` claim (58dc463); chained method calls `a.b.Method()` incl. C# extension methods (4c14413); renamed default-import → module file (2a0b6e0); Rust bare `submodule::fn()` calls resolved self-relative — current module first, then crate fallback (a3f59fb); multi-segment Rust `a::b::c()` module calls no longer dropped by the resolver pre-filter — the leaf name is now checked (7bb958b).
**KEY REALITY (honest):** apps dominated by **convention/reflection-driven** code (ASP.NET MVC/Razor/Blazor, EF config, reflection DI; Django signals/DRF; any framework whose handlers are discovered by routing/DI container, not called by in-repo code) have files with NO static in-repo caller. Those are genuine static-analysis frontiers — **literal 95% is not reachable** on such apps without either (a) excluding all framework-entry conventions from the fair denominator (defensible per methodology but extensive + per-framework), or (b) modeling each framework's convention routing + DI container (large per-framework engine work). The DI-heavy/convention-heavy frameworks (ASP.NET, Spring, MVC) are this category; the import/aggregator-style ones (FastAPI, Flask, Express, Gin) reach 95%+ with tractable resolution fixes.
**SWEEP COMPLETE — every README framework measured.** Route frameworks: Express 100%, FastAPI 98%, Flask/requests 100%, NestJS 96.8%, Gin 96.5%, Axum 100%, Rocket 93.8%, Vapor 100%, Laravel 92%, Rails 89.6% (import/aggregator) · ASP.NET 83.9%, Spring 83.3%, Drupal 78.9%, Django 74.1%, actix 65.4% (convention/reflection/actor ceiling). Component-node: React Router 100% (config-based) · SvelteKit 100% fair, Nuxt 93.5% fair (file-convention). **Component-node category fully characterized**: config-based (route components named in source → 100% raw) + file-convention (discovered by path → entry-exclusion; SvelteKit needed NO component fix, Nuxt needed the dir-prefix auto-import fix). Convention/reflection frameworks (ASP.NET/Spring/Drupal/Django/actix) are at a genuine sub-95% static-analysis ceiling — the levers are large per-mechanism features (markup/reflection/DI modeling), deliberately not chased. **faircov exclusions added this session:** language-aware test files (`_test.go`, `test_*.py`, `*Tests.cs`, `*_spec.rb`, …); generated migrations (Django/Alembic `migrations/`, EF `Migrations/*.cs`/`*.Designer.cs`/`*ModelSnapshot.cs`); Python entries (`__main__.py`, `setup.py`, `conf.py`, `docs/`) + `__init__.py` barrels. faircov now barrel-excludes `package-info.java`/`module-info.java` shells (package-declaration-only files — they carry a `namespace` node so `realSyms !== 0`, but nothing can import them, so they can never be a dependency target; the Java analog of `__init__.py`). This is what actually blocked an honest Spring number: petclinic's 5 `package-info.java` sat in the denominator as zeros, deflating 83.3% → 65.2%. **There was never a `samples`-dir exclusion in the script** — that earlier handoff note was inaccurate (the only similar rule is `example[s]?`, which can't match `samples`). Route-framework-front faircov ENTRY additions: Rust binary entries (`main.rs`, `src/bin/*.rs`); Swift `Package.swift` (SPM manifest) + `Public/` static assets; SvelteKit file-convention entries (`+page`/`+layout`/`+error`/`+server.*`, `hooks.*`, `service-worker.*` — framework-discovered route leaves, no in-repo caller); Nuxt file-convention entries (`pages/`, `app.vue`/`error.vue`, `layouts/`, `plugins/`, `middleware/`, `server/`, `proxy/routes/`); Drupal convention entries (`.install`/`.module`/`.profile`/`.theme` hook files, `src/Plugin`/`src/Element` annotation-plugins, `src/Drush`, `*ServiceProvider.php`, `src/Routing`/`*Subscriber.php`) + `js/` library assets.
## How to push each language higher (remaining levers)
**The one big cross-language lever: a static-member / const value-read pass.** Extract `Type.MEMBER` (capitalized/known-type receiver) as a `references` edge to `Type`. This is the universal deferred data-flow frontier and would lift **C#, Java, Swift, TS, and Rust at once**. Implement once in extraction with a heuristic (receiver resolves to / looks like a type → emit ref; skip lowercase `obj.field`). Trade-off = some instance-field-access noise; that's why it's been deferred. This is the highest-leverage single task remaining.
Per language — what's left and the action to improve it:
- **C# — MediatR 85.2% (raw, the lowest real number):**
- *raw→fair:* exclude no-symbol files (`TypeForwardings.cs` = assembly attrs only, `package-info`-equivalents) + benchmark `main`s → ~92%+. **A fair re-measure was never run for C# — do it first; the "real" number is materially higher.**
- *to improve further:* static/const value reads (`BuildInfo.BuildDate`, enum `Edition?` where a same-named property shadows the type) → the static-member pass.
- **Java — gson 85.1% raw → 93.3% fair:**
- *raw→fair:* exclude `package-info.java` (no symbols) + `.kt`/samples (already done for the fair number).
- *to improve fair further:* static-field reads (`X.FACTORY`), `Foo.class` class literals (currently `Foo.class` references `Class`, not `Foo`), constant reads (`JsonScope.X`) → the static-member pass.
- **Rust — tokio 81.9% (lowest of the high group), ripgrep 86.7%:**
- residual = see-through `mod.rs`/`lib.rs` roots (correct-0), **macro-reached code** (`log!`, custom `macro_rules!`, derives — the big Rust frontier, hard), external-trait-only impls.
- *to improve:* macro handling (large, separate project) + static/const reads. Note tokio's 81.9% is already honest/precise (path resolution removed spurious leaf-match edges).
- **Go — gin 96.6%:**
- residual = `//go:build` alternates (appengine/jsoniter/go_json/sonic/nomsgpack) + external-API `version.go`.
- *to improve:* a **build-constraint parser** (evaluate `//go:build`) so inactive variants are excluded from the denominator or all variants are linked (recall-first). Only matters for build-tag-heavy repos; niche.
- **TS 95.8% · Python requests 100% / flask 87.5% · Swift 95.3% / 96.2%:** at/near true ceiling — residual is entry points, see-through barrels, external public API, and value-reads. The **only** lever left for these is the static-member/const pass.
**Bottom line:** Python/TS/Swift/Go are effectively at ceiling. The two with real headroom are **C#** (mostly a fair-remeasure — do that first) and **Rust tokio** (macros — hard). The static-member/const pass is the one change that moves *everything* a few points; the rest is per-language frontier work.
## Which tools benefit (asked + answered this session)
It's a GRAPH-WIDE update (one shared `edges` table). `getCallers`/`getCallees` follow `['calls','references','imports']`; `getImpactRadius` + `getFileDependents`/`affected` follow **all except `contains`**; `codegraph_explore` composes all of them. So `instantiates`/`implements`/`decorates` edges show in impact+explore but **not** callers/callees (a pre-existing edge-kind filter in `getCallers`/`getCallees` — could be broadened, deferred).
## Gotchas
- **Include globs don't filter reliably** — tests/examples/benches/`.kt` leak into the index. Filter in the measurement SQL, not the config.
- **`/tmp` clones persist across turns** — `rm -rf <repo>/.codegraph` before re-indexing or `initSync` throws "already initialized" and you measure a STALE index (this bit me ~3×; a stale index massively under-reports).
- **Fair metric must exclude no-symbol files** (package-info, doc-only) — they can't have dependents; counting them is dishonest-low. Also a slightly-LOWER honest number (Rust tokio 83→82 after path resolution) beat the spurious-inflated one — precision over optics.
- **Build vs test:** `npm test` uses esbuild (no typecheck); `npm run build` (tsc) is what catches type errors. Always build before committing. Strict null on regex groups bit me — avoid `m[1]` indexed access.
- Node-version regex-group access (`m[1]`) is `string|undefined`; use guards.
## How to test & validate
- `npm run build` → tsc clean (must pass before commit).
- `npm test`**1178 passed | 2 skipped** (59 files, verified 2026-06-05). Per-language tests live in `__tests__/extraction.test.ts` (describe per language); route-framework/RN tests in `__tests__/{react-native-bridge,expo-modules,rn-event-channel}.test.ts`.
- Coverage probe recipe: clone repo → `node -e "...initSync...indexAll...resolveReferences..."` → the fair-coverage SQL (see Methodology #2). Node count stable = no explosion.
- Full per-language findings + exact fixes: memory file `~/.claude/projects/-Users-colby-Development-CodeGraph-codegraph/memory/impact-coverage-findings.md`.
## Repo state
- branch `feat/cross-language-impact-coverage`, last commit `2f30a3b feat(impact): pair Delphi forms with their .pas code-behind (.dfm/.fmx ↔ .pas)`.
- **41 commits ahead of `main`, 3 behind** (behind = 3 README-waitlist doc commits on main). All pushed to origin. NOT merged — branch is for review. Niche-lang commits 3941: 4155609 (Lua/Luau require resolver), 2f57119 (Liquid Shopify-JSON sections), 2f30a3b (Delphi .dfm form pairing).
- Commits 120 (per-language + RN/Expo, `16b5633``529d822`): 16b5633 (foundation+TS/Py/Go/C#), b538aee + 2ac7df5 (Rust), badb124 (Java), d111f26 (Swift), d8a2e91 (Kotlin), b5489d9 (Scala), acfb444 (PHP), 44fb978 + 5bccab6 (Ruby), ec8fe3f (C/C++), 9487954 (Dart), 857baf7 (static-member pass), 33ce431 (Objective-C), dbc4862 (Expo bridges), 4a64ca5 (classic RN pairing), d06a5ec (RCT_EXPORT_METHOD nodes), 74b599c (RN event wrapper), 082353e (cross-family gate), 529d822 (same-dir include + KMP import).
- Commits 2131 (ROUTE-FRAMEWORK front, `61a993a``b653688`, added after the original save): 61a993a (Python absolute `import a.b.c`), 2835623 (source-aware `from pkg import submodule` — FastAPI), 58dc463 (Django `include('app.urls')`), 4c14413 (chained `a.b.Method()` + C# extension methods), 2a0b6e0 (renamed default-import → module file — Express), 59b8de2 (Razor/Blazor `.cshtml`/`.razor` markup parser), 90c5f39 (Blazor `@code`/Razor `@{}` → C# extractor), 4589cf9 (docs), dc7d033 (C# namespace extraction), 9e5a951 (Razor `@using` disambiguation), b653688 (handoff notes + docs). Commit 32 (`a3f59fb`): Rust self-relative `submodule::fn()` resolution — Axum realworld 72.7%→100%, controls neutral/+3, regression test added. Commit 33 (`7bb958b`): multi-segment Rust `a::b::c()` calls no longer dropped by the resolver pre-filter (leaf-name check in `hasAnyPossibleMatch`) — Rocket realworld 62.5%→68.8%, additive elsewhere, regression test added. Commit 34 (`6d214cd`): Rocket `routes![]`/`catchers![]` macro extractor (parse the handler paths out of the raw token-tree, emit references) — Rocket 68.8%→93.8%, nodes unchanged (edges only), no control regression, regression test added. Commit 35 (`bb7659e`): Swift property-wrapper attribute-arg type refs — route a property's attribute args through `extractStaticMemberRef` so a Fluent `@Siblings(through: Pivot.self)` links the model to the pivot — vapor-til 94.7%→100%, Alamofire neutral, regression test added. Commit 36 (`3ea03e5`): SvelteKit `load`→page synthesizer (`svelteKitLoadEdges` in callback-synthesizer.ts) — links each `+page.svelte`/`+layout.svelte` to its OWN-dir loader's `load`/`actions`; impact-granularity (coverage already 100%), 0 cross-dir mislinks, nodes unchanged, regression test added. Commit 37 (`1dec765`): Nuxt nested auto-imported component resolution — `vueTemplateEdges` now matches PascalCase tags + a `nuxtComponentName` map (`<MediaCard>``media/Card.vue`); nuxt/movies 47.6%→93.5%, +9 nested components, Vue-only (gated to .vue), nodes unchanged, regression test added.
- uncommitted: only untracked `assets/generate-waitlist.py` (unrelated — README-waitlist tooling; the `.claude/handoffs/*` files are committed on this branch).
- Touched source files (branch vs main): `src/db/queries.ts`, `src/graph/queries.ts`, `src/extraction/{tree-sitter,tree-sitter-types,grammars}.ts`, `src/extraction/razor-extractor.ts` (NEW — Razor/Blazor markup), `src/extraction/languages/{rust,java,csharp,kotlin,php,ruby,scala,c-cpp}.ts`, `src/resolution/{import-resolver,callback-synthesizer,index,name-matcher}.ts`, `src/resolution/frameworks/{python,react-native,expo-modules}.ts`, `src/types.ts`, `__tests__/{extraction,graph,expo-modules,react-native-bridge,rn-event-channel}.test.ts`, `CHANGELOG.md`.
- Measurement scripts (in /tmp, not committed): `faircov.cjs` (honest fair coverage + false-edge count, `--list` shows residual zeros + exclusions), `audit.cjs` (lists 0-dependent files by language), `xlang.cjs` (cross-lang edges by src→tgt × kind).
## Open threads / TODO
- [x] **Kotlin DONE** (commit d8a2e91) — gap was KMP `expect`/`actual`; coroutines 76.8%→93.5%, OkHttp already 96.2%. See "Kotlin result" above.
- [x] **Scala DONE** (commit b5489d9) — gap was a whole family of missing edges (parameterized extends, type refs, implicit/context-bound params, `new`); cats 48.9%→89.2% fair, gatling 76.3%→91.2%. See "Scala result" above.
- [x] **PHP DONE** (commit acfb444) — gap was NAMESPACES (not #608/#660); guzzle 95.2%→100%, laravel 80.5%→94.9%. See "PHP result" above.
- [x] **Ruby DONE** (commits 44fb978 + 5bccab6) — gaps were MIXINS + REQUIRE resolution; activerecord 84.8%→96.8%, sidekiq 71%→**100%**. See "Ruby result" above.
- [x] **C/C++ DONE** (commit ec8fe3f) — gap was a C++ free-function name-extraction bug; leveldb 91.7%→94.8%, redis (C) 92.2% at ceiling. See "C/C++ result" above.
- [x] **Dart DONE** (commit 9487954) — gaps were mixins (`with`) + method type refs; flutter/packages 88.8%→92.4%, dio 86.4%→87.9%. See "Dart result" above.
- [ ] **Objective-C next** (last README language, already partial — `@interface`/`@implementation` split, `#import`, categories, protocols, `@property`). Niche after: Liquid, Pascal, Lua, Luau.
- [x] **Static-member/value-read pass DONE** (commit 857baf7) — `Enum.value`/`Type.CONST`/`Foo::BAR` → references; flutter 92.4%→93.2%, additive across Java/C#/Kotlin/Swift/Scala/PHP/C++. TS/JS/Python excluded.
- [x] **Objective-C DONE** (commit 33ce431) — selectors + class-receiver + #import + class-method resolution; AFNetworking 50%→90%, SDWebImage Core 33.8%→91.6%. ← LAST README language.
- [x] **Per-language + RN/Expo campaign COMPLETE** (`16b5633``529d822`): all 15 README langs + static-member pass + cross-language RN/Expo at 95%+.
- [x] **Route-framework front — import/aggregator style DONE** (`61a993a``2a0b6e0`): Express 100%, FastAPI 98%, Flask/requests 100%, NestJS 96.8%, Gin 96.5%. See the headroom map.
- [~] **Route-framework front — convention/reflection style PARTIAL** (`4c14413`,`59b8de2`,`90c5f39`,`dc7d033`,`9e5a951`): ASP.NET eShopOnWeb 59.3%→83.9% (Razor/Blazor parser + C# namespaces), Django realworld 45.9%→74.1%. At an honest sub-95% static-analysis ceiling (markup-driven code-behind + reflection/DI). Reaching 95% needs markup→code linking or per-framework reflection modeling (large features); faking it via metric exclusions = gaming, NOT done.
- [x] **Spring DONE** (83.3% fair, 15/18) — the real blocker was faircov NOT excluding `package-info.java` shells (5 in petclinic), not a `samples`-dir exclusion (which never existed in the script). Fixed → barrel-exclude `package-info.java`/`module-info.java`. 3 residual zeros = `@Configuration` beans + AOT `RuntimeHints`, all reflection-registered → the convention/reflection ceiling, on par with ASP.NET 83.9%.
- [x] **Axum (Rust) DONE** (72.7%→**100%**, a3f59fb) — import/aggregator thesis confirmed in a 3rd language family. Found+fixed a REAL engine miss (not a metric issue): bare Rust `submodule::fn()` calls (the `X::router()` router-assembly pattern) resolved crate-relative only → now self-relative FIRST (current module), then crate fallback. Clean A/B: no control regression (ripgrep neutral 66/79, tokio +3 → 260/321); cross-family false edges 0; regression test fails without the fix. NOTE: actix/Rocket NOW MEASURED (below).
- [x] **Rocket (Rust) DONE** (62.5%→68.8%→**93.8%**, 7bb958b + 6d214cd) — TWO engine wins: (1) multi-segment `a::b::c()` calls were dropped by the resolver pre-filter (leaf never checked) → fixed → +database/profiles.rs; (2) built the `routes![]`/`catchers![]` macro extractor — parses handler paths out of the raw token-tree + resolves them via the Rust path resolver → all 4 `routes/*.rs` handler modules covered. Only `lib.rs` (crate-root/launch hub, see-through) remains → effectively at ceiling. No control regression, false edges 0, nodes unchanged (edges only).
- [x] **actix-web (Rust) DONE** (**65.4%**, genuine ceiling) — actix 1.x ACTOR style: routing resolves (handlers covered), but the DB layer is reached via actor message dispatch (`db.send(Msg)``impl Handler<Msg> for DbExecutor`) — a DYNAMIC runtime frontier with no static edge (deliberately uncovered, the actix analog of MediatR/reactive runtimes). + glob `pub use self::x::*` re-export roots. Bridging needs an actor-message synthesizer (dynamic-dispatch feature), not a static fix.
- [x] **Vapor (Swift) DONE** — vapor-til (canonical) 85.7%→**100%** (bb7659e), penny-bot (real prod, 166 files) 73.2% (serverless/DI, secondary). Import/aggregator confirmed in Swift: controllers via `app.register(collection: X())`, models via controller queries + migrations. REAL miss found+fixed: a many-to-many pivot model referenced ONLY via `@Siblings(through: Pivot.self)` (metatype arg in a property-wrapper attribute, not walked) → routed attribute args through `extractStaticMemberRef` → pivot covered. + faircov excl `Package.swift` + `Public/`. penny-bot's lower number = AWS Lambda entries + `exports.swift` barrels (faircov gaps, ~80% if excluded) + DI/serialization/extension frontiers (genuine); not chased.
- [x] **SvelteKit (Svelte) DONE** (35.9% raw → **100% fair**, NO commit — first framework needing NO engine fix) — sveltejs/realworld (official). The `.svelte` import resolution ALREADY connects the whole component graph (page→component→lib — verified: imports+references+calls all resolve, e.g. ListErrors←4 pages). Low raw = the file-convention shape: 25/39 files are SvelteKit convention ENTRIES (`+page`/`+layout`/`+error`/`+server`/`hooks` — framework-discovered route leaves, no in-repo caller) → excluded in faircov → 14/14 = 100% of the coverable core. ONE framework-mediated gap: `+page.server.js` `load``+page.svelte` `data` (runtime `data` prop, not static) — bridged with a SvelteKit convention synthesizer (sibling-by-path, like Rocket routes!) — impact-granularity not coverage (both files are entries). **BUILT (3ea03e5): `svelteKitLoadEdges`** (callback-synthesizer.ts) links each page → its own-dir loader's `load`/`actions` + `+layout`; 19 links / 0 cross-dir mislinks on realworld, nodes unchanged, regression test (incl. does-not-cross-routes guard).
- [x] **React Router (React) DONE** (**100%**, 29/29, NO commit — no engine miss) — gothinkster realworld. The CONFIG-based component-node sub-shape: route components are NAMED in source (`<Route path='/login' component={Login}/>`) → App.js imports+references each → all resolve (verified Login/Editor/Profile covered by App.js's route config). Only `index.js` (CRA entry) + 8 anonymous-default reducers (0 symbols → barrels) excluded. The component-node category is now fully characterized: config-based (React Router, source-referenced → 100% raw) vs file-convention (SvelteKit, path-discovered → fair-100% + synthesizer); the engine handles BOTH as-is. (Modern v6/v7 `element={<X/>}`/data-router `{element, loader}` also source-referenced → same.)
- [x] **Nuxt (Vue) DONE** (47.6% → **93.5% fair**, 1dec765) — nuxt/movies (official demo). File-convention component framework (like SvelteKit) but with an AUTO-IMPORT twist that WAS a real miss: Nuxt auto-imports a NESTED component by a DIR-PREFIXED name (`components/media/Card.vue``<MediaCard/>`), but the node is named `Card` → the PascalCase usage never resolved. Fixed `vueTemplateEdges` (match PascalCase tags, not just kebab + a `nuxtComponentName` map) → +9 nested components. + faircov Nuxt entry exclusions. composables (`useTmdb()`) already resolved by name-match. 2 residual zeros = genuine frontiers (unused `<CarouselItems>` + unimported `constants/images.ts`). **KEY contrast:** SvelteKit needed NO component fix, Nuxt DID — its dir-prefix auto-import naming is unique. The Vue PascalCase-tag change is gated to `.vue` files (suite green, false edges 0).
- [x] **Drupal (PHP) DONE** (57.1% raw → **78.9% fair**, NO commit — reflection ceiling, no simple miss) — token contrib module. The MOST reflection-heavy framework. Static parts WORK: `*.routing.yml`→controller (drupal.ts), PHP class refs. 4 residual zeros all DI/reflection: 2 `services.yml` services (`TreeBuilder`/`TokenFieldRender`), a dynamically-routed controller (`TokenDevelController` via `RouteSubscriber`), a field-plugin class. + faircov excl JS libraries + convention entries. Sits with ASP.NET 83.9% / Django 74.1% in the convention/reflection band. LEVERS = drupal.ts TODOs (model `services.yml` DI → +2 services; plugin annotations; modern `#[Hook]` attributes) — large per-mechanism features, NOT chased (same stance as ASP.NET/Spring/Django: don't fake 95% via reflection modeling). **← LAST framework; the sweep is complete.**
- [ ] **Frameworks not yet measured** (need a canonical app cloned): Drupal, React Router, Vue-Nuxt (SvelteKit DONE: 100% fair — component-node shape characterized, NO engine miss; Rust route DONE: Axum 100%, Rocket 93.8%, actix 65.4%; Vapor DONE: vapor-til 100%, penny-bot 73.2% serverless/DI) (component-node frameworks — coverage shape differs). The headroom map calls these the real remaining headroom.
- [x] **All 4 niche README languages DONE** (Lua 84.2%, Luau 92.2%, Liquid 73.8%, Pascal 75.7%) — see the per-language table. Full README parity (22 langs + 14 frameworks).
- [ ] **Open the PR to `main`** (41 commits) — everything actionable is done. Deferred polish (genuinely optional): C function-pointer dispatch + C++ namespace capture; PHP IFACE_OVERRIDE_LANGS + type-hint disambiguation; Scala wildcard imports; ObjC type refs; engine symlink-dedup (SDWebImage `include/` dup file nodes); a convention/reflection lever (Drupal services.yml DI / ASP.NET-Spring reflection modeling — large per-framework features). (Static-member pass for TS/JS/Python is NOT a lever — measured & rejected, e7b86df.)
- [x] **Static-member/const pass for TS/JS/Python MEASURED & REJECTED** (e7b86df) — the long-standing "one big cross-language lever." A/B (extend `extractStaticMemberRef` to ts/js/python): **0 coverage gain** on excalidraw (94.3%→94.3%) AND requests (100%→100%) — in import-based languages a `Type.MEMBER` read requires `import`ing the type, so the import edge already covers it (the static read is pure duplication) — while adding **+1813 edges / +2448 `references` on excalidraw** (the retrieval-perf benchmark), all pointing at already-covered types. Reverted; finding documented in the `STATIC_MEMBER_LANGS` comment so it isn't re-tried. The deferral was CORRECT — now proven with data.
- [ ] Cross-language **static-member / const value-read** pass (`X.FACTORY`, `JsonScope.X`, `Foo.class`) — the universal deferred data-flow frontier; would lift Java/C#/etc. ~5% more but risks noise. User aware; deferred.
- [ ] Optional: broaden `getCallers`/`getCallees` edge-kind list to include `instantiates`/`implements`/`decorates` (offered, deferred — changes what "caller" means).
- [ ] Branch is NOT merged — it's for review. Maintainer handles version bumps/releases. Don't merge or bump.
## Recent transcript (oldest → newest)
### Turn — "Rust next"
- **Claude:** Cloned ripgrep+tokio. Found struct literals (`struct_expression`) not in INSTANTIATION_KINDS, `use` bindings not linked, trait methods (`function_signature_item`) not extracted. Fixed all + added rust to IFACE_OVERRIDE_LANGS. ripgrep 63%→81%, tokio→83%. → commits b538aee, then 2ac7df5 (module-path resolution for `pub use self::read::read` collisions → ripgrep 87%).
### Turn — "do fixes translate to explore/callers/callees or just impact?"
- **Claude:** Verified against tool code → graph-wide; mapped edge-kinds per tool (callers/callees = calls/references/imports; impact/explore = all-but-contains). instantiates/implements/decorates only in impact/explore.
### Turn — "Java next" → "is Java good or can we improve it?"
- **Claude:** Found Java annotation gap (usages in `modifiers` not descended; `@interface` not in interfaceTypes). Fixed both → gson 78%→85% (commit badb124). On re-push ask: pushed already. On "is it good": fair re-measure showed Java actually **93.3%/94.9%** (raw was polluted by package-info/.kt/samples); residual = universal value-read frontier. Concluded Java is good.
### Turn — "Swift next"
- **Claude:** Alamofire already 93%. argparser 84.6% — found property-wrapper gap (`@Argument` = Swift `attribute``user_type`, not recognized; Swift instance props not extracted). Extended `extractDecoratorsFor` + dispatcher branch. Alamofire→95.3%, argparser→96.2%. Commit d111f26. Completes README benchmarked 7.
### Turn — /handoff save (original, 2026-06-04)
- Saved at commit 529d822 (per-language + RN/Expo campaign).
### Session — ROUTE-FRAMEWORK front (2026-06-04→05, commits `61a993a`→`b653688`; reconstructed from commits)
- Measured a canonical real app per README route framework. Import/aggregator frameworks made reachable: Python absolute `import a.b.c` (61a993a), source-aware `from pkg import submodule` for FastAPI aggregators (2835623), Django `include('app.urls')` (58dc463), renamed default-import→module file for Express controllers (2a0b6e0) → Express 100%, FastAPI 98%, Flask/requests 100%, NestJS 96.8%, Gin 96.5%.
- Convention/reflection (ASP.NET/Blazor): built a Razor/Blazor `.cshtml`/`.razor` markup parser (59b8de2), delegated `@code`/`@{}` to the C# extractor (90c5f39), added C# namespace extraction (dc7d033) + Razor `@using` disambiguation (9e5a951), resolved chained `a.b.Method()` incl. C# extension methods (4c14413) → eShopOnWeb 59.3%→83.9%, Django→74.1%. Concluded these hit a genuine static-analysis ceiling (markup/reflection); did NOT game the metric to fake 95%.
### Session — this update (2026-06-05)
- **User:** loaded the wrong handoff first (explore-overhaul-bench), asked about per-language coverage %, then switched to this branch to work on impact coverage.
- **Claude:** sourced the coverage table from the `impact-coverage-findings` memory (corroborated by this handoff's own table); found this handoff committed on the branch + loaded it; flagged the branch had moved 11 commits past the handoff's stated tip; **refreshed this handoff to current state** (31 commits, route-framework front, fork of next steps; test count re-verified 1178). Then deleted the other 9 handoff files at the user's request, leaving this as the sole handoff.
- **Outcome:** handoff is current + the only one left; awaiting the user's pick of fork (ship / route front / static-member pass).
### Session — Spring + Axum (2026-06-05, commit a3f59fb)
- **User:** "unblock spring by fixing the faircov samples bug" → then "yes" to measuring an unmeasured framework.
- **Claude (Spring):** there was NO `samples`-dir exclusion in faircov (the handoff note was wrong); the real deflation was 5 `package-info.java` shells (namespace-only → realSyms≠0) sitting in the denominator as zeros. Fixed faircov to barrel-exclude `package-info.java`/`module-info.java`. Spring petclinic 65.2%→**83.3%** (15/18); 3 residual zeros all reflection/DI wiring (`@Configuration` + AOT hints — verified honest, not missed edges) → convention/reflection ceiling, ≈ASP.NET 83.9%.
- **Claude (Axum):** measured realworld-axum-sqlx → 72.7%, audited zeros → found a REAL engine miss (not a metric issue): bare Rust `submodule::fn()` calls (`X::router()` router-assembly) resolved crate-relative only. Fixed `resolveRustModuleFile` to try self-relative FIRST, then crate fallback; + `main.rs`/`src/bin/*.rs` faircov entry exclusion. Axum → **100%** (10/10). Clean A/B (same faircov, engine ±fix): axum +2, ripgrep neutral 66/79, tokio +3 → 260/321; cross-family false edges 0; regression test fails without the fix; full suite **1179** green. Committed+pushed `a3f59fb`.
- **Outcome:** Spring + Axum both measured; the import/aggregator → 95%+ thesis now holds in a 3rd language family (Rust) via a real engine fix, not a metric tweak. Remaining unmeasured: actix/Rocket (attribute-macro routing — likely convention/reflection shape), Vapor, React Router/SvelteKit/Vue-Nuxt, Drupal.
### Session — Rust attribute-macro frameworks (2026-06-05, commit 7bb958b)
- **User:** "rust frameworks to probe the ceiling" (actix + Rocket).
- **Claude:** measured Rocket (TatriX/realworld-rust-rocket) 62.5% + actix (fairingrey/actix-realworld) 65.4%. Audited zeros → TWO findings. (1) Rocket's 3-segment `database::profiles::find()` calls weren't becoming edges — isolated via micro-tests to a RESOLVER PRE-FILTER bug (`hasAnyPossibleMatch` checked first-seg + the `b::c` remainder, never the leaf), NOT closures or extraction (both fine). Fixed (leaf-name check) → Rocket 68.8%, additive elsewhere, no control regression, false edges 0. Committed 7bb958b + regression test. (2) Remaining ceilings are GENUINE: Rocket's `routes![]` macro-mounted handlers (bridgeable via a Rocket extractor — deferred, framework-specific) + actix's actor message dispatch (`db.send(Msg)``impl Handler<Msg>`, a dynamic frontier like MediatR — not bridged).
- **Outcome:** both measured; the "attribute-macro = ceiling" hypothesis was PARTLY right (genuine ceilings exist) but probing surfaced a real generalizable resolver bug that benefits all Rust `a::b::c()` calls. All Rust route frameworks done. Next options: Vapor / component-node frameworks / Drupal, or build the Rocket `routes![]` extractor (→~94%).
### Session — Rocket routes![] extractor (2026-06-05, commit 6d214cd)
- **User:** "build the Rocket routes! extractor".
- **Claude:** probed the tree-sitter AST — `routes![a::b::h, …]` parses as `macro_invocation > token_tree` of FLAT raw tokens (`identifier` / `::` / `,`), so the handler paths were invisible to the call walker. Built `extractRustRouteMacro` (tree-sitter.ts, called from `visitFunctionBody`): for a `routes`/`catchers` macro, walk the token-tree, group `identifier` runs between commas into `::`-joined paths, emit a `references` edge each; the existing Rust path resolver (self-relative + multi-segment fixes already shipped) links them to the handler fns. Rocket 68.8%→**93.8%** (all 4 `routes/*.rs` covered; only lib.rs root left). No control regression, false edges 0, nodes unchanged (edges only), regression test fails without it. Committed 6d214cd.
- **Outcome:** Rocket effectively at ceiling. Handler names are explicit in source → precise static extraction, not a heuristic. Remaining route-front: Vapor (Swift), component-node frameworks (SvelteKit/React Router/Vue-Nuxt), Drupal — or ship the 34-commit branch.
### Session — Vapor (2026-06-06, commit bb7659e)
- **User:** "vapor next".
- **Claude:** measured vapor-til (canonical Vapor tutorial app, kodecocodes) 85.7% + penny-bot (real prod, 166 files) 73.2%. vapor-til zeros: Package.swift (SPM build manifest → faircov excl), Public/cookies.js (static client asset → faircov excl), and AcronymCategoryPivot.swift = a REAL miss. Audited: the pivot model is referenced ONLY via `@Siblings(through: AcronymCategoryPivot.self, …)` in Acronym/Category — a metatype arg inside a property-wrapper `attribute` whose ARGS were never walked (only the wrapper type `Siblings` + the property's declared type were captured). Fix: route the property's attribute-arg subtree through `extractStaticMemberRef` (already handles Swift `Type.self` navigation, self-filters to capitalized receivers, skips `\.$keypath`). vapor-til 94.7%→**100%**. Clean A/B: vapor-til +1, Alamofire neutral (44/50); false edges 0; suite 1182. Committed bb7659e + regression test. penny-bot 73.2% characterized (serverless/DI: Lambda entries + exports barrels + DI/serialization/extension frontiers) — NOT chased (not the canonical pattern).
- **Outcome:** Vapor DONE; import/aggregator confirmed in a 4th language family (Swift) via a real Fluent fix. Remaining route-front: component-node frameworks (SvelteKit/React Router/Vue-Nuxt), Drupal — or ship the 35-commit branch.
### Session — SvelteKit (2026-06-06, no commit — characterization)
- **User:** "component-node framework next".
- **Claude:** measured sveltejs/realworld (official SvelteKit app) → 35.9% raw (14/39). Audited: ALL 25 zeros are SvelteKit file-convention files (`+page`/`+layout`/`+error`/`+server`/`hooks`) — loaded by the framework BY PATH, no in-repo caller. Probed + CONFIRMED the engine already resolves every `.svelte` component import (ArticlePreview←ArticleList, Nav←+layout, ListErrors←4 pages; imports+references+calls all resolve) — NO component is a zero, so NO engine miss (first framework needing none). Added SvelteKit convention-entry patterns to faircov (methodology-aligned, like route handlers) → 14/14 = **100% fair** of the coverable core. Confirmed the one framework-mediated gap: `+page.server.js` `load``+page.svelte` `data` is NOT statically linked (runtime `data` prop).
- **Outcome:** the component-node shape is "file-convention entries (the majority) + a fully-covered component/lib core" — the engine handles it as-is. No commit (nothing to fix). OFFERED a SvelteKit convention synthesizer (sibling `+page.server.js``+page.svelte` link, like Rocket routes!) for impact granularity — not built. Branch stays 35 commits. Next: React Router (config-based variant — likely MORE covered) / Nuxt (file-convention, SvelteKit-like) / Drupal, or ship.
### Session — SvelteKit load→page synthesizer (2026-06-06, commit 3ea03e5)
- **User:** "lets fix svelte with the synthesizer, then we can move onto react router".
- **Claude:** probed the graph: `+page.svelte``component` node, `+page.server.js``load`/`actions` function nodes, pages pair with sibling loaders in the SAME dir. Built `svelteKitLoadEdges` (callback-synthesizer.ts, wired into `synthesizeCallbackEdges` after `vueTemplateEdges`): for each `+page`/`+layout.svelte`, link its `component` → the `load`/`actions` in `<dir>/<prefix>.server.{ts,js}` / `<dir>/<prefix>.{ts,js}` (same dir only), direction page→load so `getImpactRadius(load)` surfaces the page; provenance heuristic. Verified on sveltejs/realworld: 19 links, **0 cross-dir mislinks**, false edges 0, nodes unchanged; `getImpactRadius(login load)` now returns login/+page.svelte. Committed 3ea03e5 + regression test (incl. a does-not-cross-routes guard). Suite 1183.
- **Outcome:** SvelteKit fully done (component coverage 100% + the loader→page impact link). Next per the user: React Router (the config-based component-node variant).
### Session — React Router (2026-06-06, no commit — characterization)
- **User:** "...then we can move onto react router" (continued from the svelte synthesizer).
- **Claude:** measured gothinkster react-redux-realworld (38 files, react-router v4/v5) → **100%** (29/29), false edges 0. The CONFIG-based component-node sub-shape: route components are NAMED in source (`<Route path='/login' component={Login}/>`), so App.js imports+references each → all resolve. Verified Login/Editor/Profile covered BY App.js's route config. Only `index.js` (CRA entry) + 8 anonymous-default reducers (0 named symbols → barrels) excluded. NO engine miss. (Modern v6/v7 `element={<X/>}` + data-router `{element, loader}` are also source-referenced → same conclusion.)
- **Outcome:** React Router DONE. Component-node category now fully characterized — two sub-shapes (config-based React Router = 100% raw; file-convention SvelteKit = fair-100% + synthesizer), both handled by the engine. No commit (nothing to fix). Remaining: Nuxt (Vue, file-convention — would mirror SvelteKit), Drupal (PHP), or ship the 36-commit branch.
### Session — Nuxt (2026-06-06, commit 1dec765)
- **User:** "nuxt next".
- **Claude:** measured nuxt/movies (official Nuxt 3 demo, 42 .vue + 37 ts) → 47.6% raw. Audited zeros: pages/app.vue/error.vue/server = file-convention entries (→ faircov Nuxt exclusions); NESTED components (`components/media/*`, `components/carousel/*`) = a REAL miss. Root cause: Nuxt auto-imports a nested component by DIR-PREFIX (`media/Card.vue``<MediaCard/>`), but the node is named `Card`; the synth `vueTemplateEdges` only matched KEBAB tags, and the extractor's PascalCase ref name-matched `MediaCard`→nothing. Fixed: added a PascalCase-tag regex to vueTemplateEdges + a `nuxtComponentName` map (`media/Card.vue``MediaCard`, Nuxt dir de-dup). nuxt/movies 47.6%→**93.5%** (+9 nested components). Vue-only (gated to .vue), false edges 0, nodes unchanged, suite 1183, regression test. Committed 1dec765. 2 residual zeros are genuine frontiers (unused `<CarouselItems>`, unimported constants). composables (`useTmdb()`) already worked.
- **Outcome:** Nuxt DONE. The two file-convention component frameworks DIFFER: SvelteKit's imports resolve as-is (no fix), Nuxt's dir-prefix auto-import needed a fix. Only **Drupal** (PHP) left unmeasured — or ship the 37-commit branch.
### Session — Drupal (2026-06-06, no commit — characterization, finishes the sweep)
- **User:** "drupal to finish the sweep".
- **Claude:** measured the `token` contrib module (58 PHP, modern Drupal 11) → 57.1% raw. Audited: `drupal.ts` ALREADY resolves `*.routing.yml`→controller (TokenTreeController covered via the route ref) and PHP class refs work; the zeros are ALL genuine reflection/convention — JS libraries, DI services (`TreeBuilder` = `@token.tree_builder` in services.yml, `TokenFieldRender`), a dynamically-routed controller (`TokenDevelController` via `RouteSubscriber`), plugins (`Element/`, `Plugin/Derivative/`), Drush commands, `*ServiceProvider`, `.install` hooks, field-plugin `MenuLinkFieldItemList`. NO simple fixable miss (verified TokenDevelController isn't in any routing.yml — genuinely dynamic). Added faircov Drupal convention-entry + JS-library exclusions → **78.9% fair** (4 residual zeros = DI services + dynamic route + field plugin). Token uses modern `#[Hook('...')]` attribute OOP hooks (a drupal.ts TODO). NO commit (the levers — services.yml DI / plugin annotations / `#[Hook]` — are large per-mechanism features, same don't-chase-95% stance as ASP.NET/Django/Spring).
- **Outcome:** **THE SWEEP IS COMPLETE.** Every README framework measured: import/aggregator + config-based-component + file-convention-component reach ceiling (95100%) via 6 engine fixes; convention/reflection/actor frameworks (ASP.NET/Spring/Drupal/Django/actix) sit at a genuine sub-95% reflection ceiling. Branch 37 commits, ready to ship. Next: open the PR, or the static-member-pass TS/JS/Python lever.
### Session — static-member pass for TS/JS/Python: measured & rejected (2026-06-06, commit e7b86df)
- **User:** "extending the static-member/const value-read pass to TS/JS/Python."
- **Claude:** implemented (added `member_expression`/`attribute` to `MEMBER_ACCESS_TYPES` + ts/js/python to `STATIC_MEMBER_LANGS`), then ran a clean A/B (same faircov, engine ±change) on excalidraw (TS/React, the retrieval-perf canary) + requests (Python). Result: **0 coverage gain** on BOTH (excalidraw 94.3%→94.3%, requests 100%→100%) because the import edge already covers every `Type.MEMBER` read (in import-based languages you must import the type first — the static read is redundant), while adding **+1813 edges / +2448 `references` on excalidraw** (all pointing at already-covered types — pure retrieval-graph noise). REVERTED the functional change; enhanced the `STATIC_MEMBER_LANGS` comment with the measured finding so it isn't re-attempted. Committed e7b86df (doc-only).
- **Outcome:** the standing "one big cross-language lever" is CLOSED — the deferral was correct, now proven with data. The branch is 38 commits; everything actionable is done. **Recommended next: open the PR to main.**
### Session — the 4 niche README languages (2026-06-06, commits 4155609 / 2f57119 / 2f30a3b)
- **User:** confirmed only 4 README "Full support" languages lacked coverage validation (Lua/Luau/Liquid/Pascal); "knock out those 4 to close it out."
- **Claude:** measured + fixed each. **Lua** (telescope) 31.6% + **Luau** (Fusion) 12.5%: NO `require()` module resolver existed — added `resolveLuaRequire` (dotted `a.b.c`→file + instance-path leaf, path-suffix match w/ same-dir preference; confidence 0.9 so it beats the import-node self-match) → Lua **84.2%**, Luau **92.2%** (4155609). **Liquid** (Dawn) 39.1%: snippet `{% render %}` already worked, but Shopify OS 2.0 sections are referenced from JSON templates (`templates/*.json` `"type"`), which weren't even indexed — made Shopify JSON indexable (isSourceFile + detectLanguage) + a Liquid-extractor JSON branch emitting `sections/<type>.liquid` refs (incl. nested `templates/customers/`) → **73.8%** (2f57119). **Pascal** (PascalCoin) 73.0%: `uses`-resolution ALREADY worked; added a `.dfm`/`.fmx``.pas` form-code-behind synthesizer → **75.7%** (2f30a3b). Each: gated to its language, false edges 0, regression test fails without the fix, suite green (1187).
- **Outcome:** **FULL README PARITY** — all 22 "Full support" languages + all 14 frameworks now have coverage validation. Branch 41 commits, ready to ship. 3 new generalizable resolvers (Lua/Luau require, Shopify-JSON sections, Delphi form pairing). Only remaining work is optional reflection/DI levers + the PR.