Receiver retirement: independent review and native preparation
Production reviewed: 66f953b98e634105c806452d05f26b411928f705 against
f8598b086475fd28250726607bafea4fa2a9ffe8. Only
lib/process-send-broker.js and lib/guest-thread-host.js change production
behavior. No blocking correctness finding in this bounded independent review.
The broker subscribes to the exact receiver link before queuing. Synchronous retirement during subscription removes the subscription, and settlement removes queued requests while readiness scans use a snapshot. An executing adapter retains stack ownership; if its receipt is deferred after retirement, the new check settles receiver-gone rather than requeueing permanently. Active native callback unwind remains owned by the host adapter.
Worker transport failure rejects each pending RPC through the existing wrapper,
which clears its timer, and refuses later RPC admission. Normal stop sets
_stopped before terminating the Worker, so its expected exit does not become
an unexpected transport failure. Unexpected error followed by exit is harmless
for empty pending/listener collections. Raw transport failure never creates a
scoped-loader retirement receipt or clears loader pins. If scoped loading was
used and no receipt exists, later stop rejects and ownership remains retained;
this is intentionally not successful native loader cleanup. Browser error
delivery was inspected in source only.
Prepared native control (subsequently executed below)
test/test-receiver-retirement-native.js preserves all prior native admission
cases, then adds actual native-PE deferred receiver stop, raw process exit and
uncaught Worker error. Deferred admission is observed from the real broker
queue while the real native receiver is in ordinary code. No readiness signal
or fake dispatch result is supplied. Raw failures also reject an unanswered RPC
and leave no pending host request. Normal stop must not record transport failure.
Additional native SendMessageA callbacks stop/exit/error while their external
reply is held: stack/depth must remain owned until release and adapter unwind.
These cases assert empty loader gate, no fabricated retirement receipt, empty
queue/listeners and completed Worker termination. They do not load a DLL or
claim native raw-failure recovery of retained DLL pins.
The Worker wrapper only adds explicit process-exit/uncaught-error messages; it requires the actual production guest Worker. The existing scoped endpoint context gains an optional Worker URL argument; existing callers are unchanged. Temporary wrapper bytes are removed after cleanup. Each new context closes its Workers and retires its broker endpoint before another context starts.
tools/run-receiver-retirement-native.js runs candidate/old/restored serially,
with 60-second inner and 90-second external guards plus five-second kill grace
per arm. Candidate production is pinned to 66f953b; old replaces both changed
production files with f859 bytes. Old must first pass every prior admission
case, then exit 1 with RECEIVER_RETIREMENT_DID_NOT_SETTLE at the first deferred
receiver stop, without printing its success marker. It is not enough for an
unrelated test to fail. Positive/restored must exit 0. Every arm verifies
physical host/src/lib and fixture bytes, donor bytes, canonical module, and
empty process inventory. Exact original production bytes are durably saved
in the evidence directory before any swap, restored in finally, and checked.
The canonical module stays read-only at
/home/user/wa-iw-native-validation/build/wine-assembly.wasm, SHA-256
a948a826f71c1935c4d00f7054decdaf4d1e8115314d8779727b7e30069f5b48.
No module build is required. Source parses and whitespace checks pass; no
native/runtime/build/browser/remote action has been performed for this change.
After explicit root review and sole-lane grant, stage a fresh committed checkout through Boat APIs with all tracked host/src/lib files and the controller's four fixture files. Do not mutate the donor. Run:
WA_RECEIVER_EVIDENCE=/home/user/iw-receiver-evidence-UNIQUE node tools/run-receiver-retirement-native.js
Use an outer 300-second bound. Independently verify final source/module hashes
and empty process inventory before lane release; archive and recover evidence
through Boat APIs. If an outer kill bypasses finally, restore both .original
files to their matching lib/ paths and verify against 66f953b before release.
Existing native admission evidence remains separate and predates this fix.
No original Invisible War, browser, pointer-marshalling, full-chain integration,
or deployment claim follows from this source preparation.
Admitted native result
Root reviewed a8e288dde5d6343f67d4ca957e9dee294fc9ca37 and explicitly
granted the lane after Outlaws PID 216369's independent release. A fresh sparse
checkout at /home/user/wa-iw-receiver-native was created via Boat APIs, leaving
the donor unchanged. Preflight at 15:54:22.093 UTC verified no active runtime,
clean exact HEAD, required dependencies and canonical a948; sandbox TTL was
16:39:17.890 UTC. Controller PID 218603 started 15:54:29.757 and exited 0 at
15:54:42.050 on 2026-10-15.
| Arm | Exit | Elapsed | Result |
|---|---|---|---|
| Candidate 66 production | 0 | 749 ms | All prior and new native cases pass |
| Both production files at f859 | 1 | 1871 ms | Exact deferred receiver-stop failure |
| Restored 66 production | 0 | 748 ms | All cases pass again |
Both positive logs show independent real x86 A/B SendMessageA/register
restoration, actual round/serial slice yield 17, nested unwind, window lifetime,
same-TID replacement, and retired sender exclusion. Both also show all six new
deferred/active receiver stop, raw process exit, and uncaught-error cases pass.
The old control passed every preceding admission case, then failed the expected
receiver-gone rejection because its 1500 ms guard raised
RECEIVER_RETIREMENT_DID_NOT_SETTLE; it never printed deferred-stop success.
This is the intended regression, not an unrelated runtime error.
Independent postflight at 15:54:57.260 reread all 270 recorded source, fixture
and donor hashes, confirmed canonical a948 unchanged, and found active: [].
The lane was explicitly released to root then. All three arm logs/statuses and
source snapshots plus independent postflight were recovered and inspected.
Archive scratch/runs/20261015-iw-receiver-native/attempt1.tar.gz is 143255
bytes, SHA-256
5fa94ff85bd3e54d362d2939e8c26c639ee29bf21d62a6b2429a0e6aad5588f5.
The sibling source.bundle, stage.js, stage.json, terminal.json, and
postflight.js retain source recovery and API receipts. The archive includes
durable exact production rollback bytes and independent process inventory;
the module is retained at the read-only donor path, not duplicated locally.
Native validation is now complete for this bounded fixture. Empty-loader-gate limitations above remain: this does not prove crash recovery of live DLL pins, native guest ExitThread/trap handling beyond raw transport failure, original Invisible War progress, browser behavior, pointer marshalling or full-chain merge readiness. No build, browser, original game or deployment occurred.