Summary
While fixing #2413 (a narrow 3-edge WASM-vs-native divergence on this repo's own src/), a much larger divergence surfaced once the repo/corpus grew: a full build now shows 167 wasm-only and 14 native-only calls edges (not the 3 originally reported) — same node counts (15370) on both engines, edge counts diverging (wasm 6989 vs native 6836 before #2413's fix, 6838 after).
This is unrelated to #2413's root cause (already fixed: a Rust flag-only-dynamic-kinds list omitted reflection). The remaining divergence is a completely different shape, concentrated around interface/base-typed method dispatch and CHA (class-hierarchy-analysis) fan-out.
Reproduction
node dist/cli.js build src --engine wasm --no-incremental # save src/.codegraph/graph.db
node dist/cli.js build src --engine native --no-incremental # compare against the saved copy
Compare calls edges by (source file:line:name -> target file:line:name, dynamic).
Sample divergent edges
wasm-only (a representative sample; ~167 total):
ast-analysis/rules/csharp.ts:191:extractParamName -> types.ts:1075:TreeSitterNode.childForFieldName
ast-analysis/visitor.ts:48:initVisitors -> types.ts:1159:Visitor.init
ast-analysis/visitor.ts:67:dispatchEnterFunction -> types.ts:1162:Visitor.enterFunction
db/connection.ts:190:flushDeferredClose -> types.ts:2685:BetterSqlite3Database.close
db/connection.ts:524:openRepoNative -> types.ts:3040:NativeDatabase.getBuildMeta
native-only (~14 total) — notably, native resolves to MULTIPLE concrete close overloads where WASM resolves to the interface-typed types.ts declaration only:
db/connection.ts:190:flushDeferredClose -> db/connection.ts:542:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:570:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:610:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:694:close
db/connection.ts:524:openRepoNative -> db/connection.ts:542:close (+ 3 more overloads)
db/connection.ts:542:close -> db/connection.ts:570:close (cross-overload edges)
The pattern: for a call through a value typed as an interface/type alias declared in types.ts (e.g. TreeSitterNode, Visitor, BetterSqlite3Database, NativeDatabase), WASM appears to resolve (or fall back) to the interface's own declaration site in types.ts, while native's CHA resolves to every concrete implementor it can find (sometimes several, including what look like unrelated close functions cross-referencing each other). Neither behavior is obviously "the accepted one" without deeper investigation — this needs a look at how each engine's method-hierarchy/CHA resolution handles a receiver typed as an interface declared in a .d.ts-style ambient/shared types file versus a receiver typed as a concrete class.
Scope
Per CLAUDE.md, a divergence means one engine has a bug — this is not a "parity gap" to document. Given the scale (~180 edges across dozens of call sites) and that it clusters around one specific mechanism (interface-typed receiver dispatch via types.ts), this is likely 1-3 root causes rather than 180 separate bugs, but needs investigation to confirm which engine is right (or whether both are partially wrong) before scoping a fix.
How it surfaced
Found while verifying the fix for #2413 against the current src/ tree — the original issue's exact 3-edge repro no longer matched (grew from 3 to ~180 edges), and after fixing #2413's actual root cause (a missing reflection entry in native's flag-only dynamic-kind list), this much larger, structurally different divergence remained.
Summary
While fixing #2413 (a narrow 3-edge WASM-vs-native divergence on this repo's own
src/), a much larger divergence surfaced once the repo/corpus grew: a full build now shows 167 wasm-only and 14 native-onlycallsedges (not the 3 originally reported) — same node counts (15370) on both engines, edge counts diverging (wasm 6989 vs native 6836 before #2413's fix, 6838 after).This is unrelated to #2413's root cause (already fixed: a Rust flag-only-dynamic-kinds list omitted
reflection). The remaining divergence is a completely different shape, concentrated around interface/base-typed method dispatch and CHA (class-hierarchy-analysis) fan-out.Reproduction
Compare
callsedges by(source file:line:name -> target file:line:name, dynamic).Sample divergent edges
wasm-only (a representative sample; ~167 total):
native-only (~14 total) — notably, native resolves to MULTIPLE concrete
closeoverloads where WASM resolves to the interface-typedtypes.tsdeclaration only:The pattern: for a call through a value typed as an interface/type alias declared in
types.ts(e.g.TreeSitterNode,Visitor,BetterSqlite3Database,NativeDatabase), WASM appears to resolve (or fall back) to the interface's own declaration site intypes.ts, while native's CHA resolves to every concrete implementor it can find (sometimes several, including what look like unrelatedclosefunctions cross-referencing each other). Neither behavior is obviously "the accepted one" without deeper investigation — this needs a look at how each engine's method-hierarchy/CHA resolution handles a receiver typed as an interface declared in a.d.ts-style ambient/shared types file versus a receiver typed as a concrete class.Scope
Per
CLAUDE.md, a divergence means one engine has a bug — this is not a "parity gap" to document. Given the scale (~180 edges across dozens of call sites) and that it clusters around one specific mechanism (interface-typed receiver dispatch viatypes.ts), this is likely 1-3 root causes rather than 180 separate bugs, but needs investigation to confirm which engine is right (or whether both are partially wrong) before scoping a fix.How it surfaced
Found while verifying the fix for #2413 against the current
src/tree — the original issue's exact 3-edge repro no longer matched (grew from 3 to ~180 edges), and after fixing #2413's actual root cause (a missingreflectionentry in native's flag-only dynamic-kind list), this much larger, structurally different divergence remained.