---
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 (~93–95%). 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 39–41: 4155609 (Lua/Luau require resolver), 2f57119 (Liquid Shopify-JSON sections), 2f30a3b (Delphi .dfm form pairing).
- Commits 1–20 (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 21–31 (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 (95–100%) 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.
