Independent JS COM ownership review — 2026-10-20

Candidate: eda54d326474e79f5ed39cb8e223b3ff652c3361, task/js-com-callback-owner-oct20. Review starts from fetched main 1d58b6c5497d6315976ca9d506f66c84612681cc in an isolated sparse task/js-com-owner-review-oct20 worktree. Candidate cherry-picked as 845cd18934cf4fed57b062ed545a9dcde08e0bf8. No main integration.

Finding and correction

The candidate restores architectural EIP but omits the decoded stream $ip and $current_thunk_eip. These are distinct interpreter control state. $sync_depth_enter in src/04-cache.wat inspects $ip to decide whether the suspended frame needs a spill pin. Its comment explicitly requires consecutive nested runs to put the suspended stream's IP back before entering again. $run changes $ip; restoring EIP does not restore it. In the sequential DllGetClassObject → CreateInstance path, the second enter could therefore pin according to the previous callback's stream instead of the suspended caller. This is a source-demonstrated missing precondition, not a reproduced native spill corruption. $win32_dispatch also overwrites $current_thunk_eip; $icm_drv_call documents and restores that same parking-related control value.

The correction adds four dedicated i32 accessors and retains both values in the JS activation. They are restored only after the completed callback's expected-child leave succeeds, alongside existing parent restoration. Trap, stale, capacity, incomplete and instance/memory-replacement paths still do not pop or restore. No jump API, primitive ownership algorithm, or spill algorithm changed.

The regression interprets the actual accessor ASTs, changes both control values during completion, checks nested restoration and sequential entry IP, and rejects independent omissions of either restoration. The original candidate fails the enhanced suite with the changed IP/thunk still visible.

ABI and ownership assessment

Verification and census reconciliation

Self-contained evidence: scratch/runs/20261020-js-com-owner-review/. Run node scratch/runs/20261020-js-com-owner-review/replay.js. It requires Node only and does not build or execute the full VM.

Readiness and remaining evidence

Ready for subsequent integration review as a source correction, not native qualification or whole jump-system readiness. The fresh branch contains the candidate plus the narrow correction on census-fixed main. The shared worktree, HEAD/index, laptop Heroes II/Warcraft III work and unrelated changes are not part of this task.

The fixture models CPU run, memory translation, allocation and token primitives. Capacity/stale cases simulate primitive rejection; they do not exercise atomic serial exhaustion, 63 real children, retired-instance races, actual spill pins or reclamation. The wrapper interpreter is not proof of compiled i64↔BigInt ABI validity. Simulated completion overwrites ESP and then observes parent restoration, so it does not validate guest ret N cleanup or detect a callee with a wrong calling convention.

Required native qualification: canonical gated combined build; real x86 on all three JS COM routes (including sequential DLL/factory and nested registered factories); exact argument/return stack behavior and HRESULTs; parent control/SEH preservation; real stale epoch/thread-slot/root and token rejection; abort/yield/exit/budget behavior without parent resumption; genuine capacity and serial exhaustion controls; repeated callbacks with a suspended spill stream plus pressure/reclamation. Verify the host route actually uses the JS bridge rather than only the owner-side COM continuation path. Existing edit/WinHelp native runs on census-fixed main do not qualify these new JS paths. No runtime, full build, Boat, browser or additional worker was used for this review.