You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/parity audit (run 2026-07-04) found 3 fixtures diverging between the WASM and native engines, all with the same root cause: for identifier.call(...), identifier.apply(...), and identifier.bind(...) call sites, the WASM engine now emits dyn=0 (plain static call, no dynamic tag) while the native (Rust) engine still emits dyn=1 with dynamic_kind: "reflection". Confidence is 1 (fully resolved) on both sides — only the dynamic/dynamic_kind metadata differs.
This predates the current Titan forge/grind run — confirmed by building both engines at the pre-run merge-base commit (597ed1c3, origin/main) and reproducing the identical divergence counts (6/8/12 edge diffs). Filing per the parity skill's pre-existing-finding rule (file an issue, don't expand scope) rather than fixing inline.
Root cause
PR #1693 (e2ec3fb2, "fix(wasm): emit dyn=0 for f.call/bind alias calls", closes #1687) changed extractMemberExprCallInfo in src/extractors/javascript.ts to unconditionally drop the dynamic/dynamicKind flag for .call()/.apply()/.bind() when the receiver is a plain identifier:
The PR's own comment claims this "keeps parity with the native Rust engine, which also resolves these as dyn=0" — but that is only true for the specific dedup scenario in issue #1687 (bind/bind.js: a direct f() call followed by f.call({}) to the same target, where native's seenEdges dedup naturally keeps the first dyn=0 edge). It is not true in general: the Rust extractor (crates/codegraph-core/src/extractors/javascript.rs:2546-2557) still unconditionally emits dynamic: Some(true), dynamic_kind: Some("reflection") for identifier.call/apply/bind, with no corresponding dedup collision:
So the WASM-only fix overcorrected: it silences the reflection tag for every identifier-based .call/.apply/.bind, not just the narrow double-edge-emission case from #1687.
Decide the correct semantic first, then make both engines match it:
If identifier.call/apply/bind should still be tagged dynamicKind: 'reflection' (informational, queryable via codegraph roles --dynamic, independent of confidence) — revert the WASM-side unconditional drop and instead fix the actual fix(parity): jelly-micro bind.js: WASM emits dyn=1 for f.call/bind aliases, native emits dyn=0 #1687 bug narrowly: only suppress the dynamic flag when the same (caller, callee) edge was already emitted as a direct dyn=0 call in the same scope (the dynZeroEdgeRows upgrade-path collision), not for every identifier receiver.
If plain-identifier .call/.apply/.bind should be treated as a fully static call with no reflection tag (current WASM behavior) — update the Rust extractor at crates/codegraph-core/src/extractors/javascript.rs:2546-2557 to match by dropping dynamic/dynamic_kind for the identifier-receiver branch, mirroring the TS side exactly.
Either way, add a fixture/unit test that pins a case with no prior direct call to the same target (like reflection.js above), so the dedup-collision fix and the general-case semantic don't get conflated again.
Verification
Confirmed pre-existing by building both engines (napi release build + codesign) at merge-base 597ed1c3 and re-running scripts/parity-compare.mjs --langs dynamic-javascript,javascript,jelly-micro — identical divergence (6/8/12 edge diffs) reproduces there, before any of the current Titan forge/grind commits.
Summary
/parityaudit (run 2026-07-04) found 3 fixtures diverging between the WASM and native engines, all with the same root cause: foridentifier.call(...),identifier.apply(...), andidentifier.bind(...)call sites, the WASM engine now emitsdyn=0(plain static call, no dynamic tag) while the native (Rust) engine still emitsdyn=1withdynamic_kind: "reflection". Confidence is 1 (fully resolved) on both sides — only thedynamic/dynamic_kindmetadata differs.This predates the current Titan forge/grind run — confirmed by building both engines at the pre-run merge-base commit (
597ed1c3,origin/main) and reproducing the identical divergence counts (6/8/12 edge diffs). Filing per the parity skill's pre-existing-finding rule (file an issue, don't expand scope) rather than fixing inline.Root cause
PR #1693 (
e2ec3fb2, "fix(wasm): emit dyn=0 for f.call/bind alias calls", closes #1687) changedextractMemberExprCallInfoinsrc/extractors/javascript.tsto unconditionally drop thedynamic/dynamicKindflag for.call()/.apply()/.bind()when the receiver is a plain identifier:The PR's own comment claims this "keeps parity with the native Rust engine, which also resolves these as dyn=0" — but that is only true for the specific dedup scenario in issue #1687 (
bind/bind.js: a directf()call followed byf.call({})to the same target, where native'sseenEdgesdedup naturally keeps the first dyn=0 edge). It is not true in general: the Rust extractor (crates/codegraph-core/src/extractors/javascript.rs:2546-2557) still unconditionally emitsdynamic: Some(true), dynamic_kind: Some("reflection")foridentifier.call/apply/bind, with no corresponding dedup collision:So the WASM-only fix overcorrected: it silences the reflection tag for every identifier-based
.call/.apply/.bind, not just the narrow double-edge-emission case from #1687.Reproduction
Minimal repro (no prior direct call to the same target — rules out the #1687 dedup scenario):
Suggested fix
Decide the correct semantic first, then make both engines match it:
identifier.call/apply/bindshould still be taggeddynamicKind: 'reflection'(informational, queryable viacodegraph roles --dynamic, independent of confidence) — revert the WASM-side unconditional drop and instead fix the actual fix(parity): jelly-micro bind.js: WASM emits dyn=1 for f.call/bind aliases, native emits dyn=0 #1687 bug narrowly: only suppress thedynamicflag when the same (caller, callee) edge was already emitted as a direct dyn=0 call in the same scope (thedynZeroEdgeRowsupgrade-path collision), not for every identifier receiver..call/.apply/.bindshould be treated as a fully static call with no reflection tag (current WASM behavior) — update the Rust extractor atcrates/codegraph-core/src/extractors/javascript.rs:2546-2557to match by droppingdynamic/dynamic_kindfor the identifier-receiver branch, mirroring the TS side exactly.Either way, add a fixture/unit test that pins a case with no prior direct call to the same target (like
reflection.jsabove), so the dedup-collision fix and the general-case semantic don't get conflated again.Verification
Confirmed pre-existing by building both engines (napi release build + codesign) at merge-base
597ed1c3and re-runningscripts/parity-compare.mjs --langs dynamic-javascript,javascript,jelly-micro— identical divergence (6/8/12 edge diffs) reproduces there, before any of the current Titan forge/grind commits.