Summary
Cancelled async host-import settlements are checked for discard only after the facade has converted their values. Conversion can register resources or otherwise transfer ownership, even though the guest will never receive the result.
Priority: high. Reproduced on fd4266b516632e80943456a45aa64b95ea42f36f with real Wasm, Deno 2.9.5 / V8 15.0, Linux aarch64.
Reproduction
A component imports a host resource r and make: async func() -> own<r>. Its run export:
- Calls the async lower of
make with a result pointer.
- Extracts the subtask index from the packed result.
- Executes async
subtask.cancel, then subtask.drop.
- Returns the cancellation state.
The host implements make with a deferred Promise. run() returns 4 (CANCELLED_BEFORE_RETURNED). The resource registry is empty at this point. Resolve the Promise with a new host resource object and allow one event-loop turn.
Observed: { liveAfterDiscard: 1, disposed: 0 }. A resource registration now exists, but no guest handle owns it. The expected registry count is zero: discarded delivery must not perform ownership conversion.
The local review fixture is /tmp/opencode/facade-review/cancel-own.wat; the failing test is cancelled async own result must not register a rep in that directory's repro_test.ts (local review artifacts, not repository dependencies).
Cause and authority
- instantiate.ts:782–790 executes
ok(v) / fail(e, args) in the facade Promise reaction.
- boundary.ts:1852–1868 checks resolved/cancelled or poisoned recipients only in the reaction on that already-adapted Promise.
contracts/embedder-api.md:217–224 explicitly says discarded settlements are never lowered. Architecture §6 describes the same policy.
The existing low-level cancellation tests pass because they do not exercise facade ownership conversion before the boundary guard. Related policy: #241.
Acceptance criteria
- Make the discard decision before facade result conversion, including ComponentException payload conversion.
- A discarded result must not register resources, invalidate transferred guest wrappers, or start new producer pumps.
- Preserve borrow-scope cleanup and handling of rejected discarded Promises.
- Add facade-level cancellation/late-settlement coverage using an own-valued result; review the equivalent poisoned-recipient path. Run
just gates for the repair.
Summary
Cancelled async host-import settlements are checked for discard only after the facade has converted their values. Conversion can register resources or otherwise transfer ownership, even though the guest will never receive the result.
Priority: high. Reproduced on
fd4266b516632e80943456a45aa64b95ea42f36fwith real Wasm, Deno 2.9.5 / V8 15.0, Linux aarch64.Reproduction
A component imports a host resource
randmake: async func() -> own<r>. Itsrunexport:makewith a result pointer.subtask.cancel, thensubtask.drop.The host implements
makewith a deferred Promise.run()returns4(CANCELLED_BEFORE_RETURNED). The resource registry is empty at this point. Resolve the Promise with a new host resource object and allow one event-loop turn.Observed:
{ liveAfterDiscard: 1, disposed: 0 }. A resource registration now exists, but no guest handle owns it. The expected registry count is zero: discarded delivery must not perform ownership conversion.The local review fixture is
/tmp/opencode/facade-review/cancel-own.wat; the failing test iscancelled async own result must not register a repin that directory'srepro_test.ts(local review artifacts, not repository dependencies).Cause and authority
ok(v)/fail(e, args)in the facade Promise reaction.contracts/embedder-api.md:217–224explicitly says discarded settlements are never lowered. Architecture §6 describes the same policy.The existing low-level cancellation tests pass because they do not exercise facade ownership conversion before the boundary guard. Related policy: #241.
Acceptance criteria
just gatesfor the repair.