Part of #992
comp:engine · M · crates/engine/src/sync/drain.rs, crates/engine/src/facade.rs, crates/wasm/src/lib.rs
Found by the simplify, reuse and altitude gates on PR #1658.
Problem
The drain holds the queue head for three reasons, and each has its own cell: BlockedOp for the quota, SettingsHold for a placement refusal, and BinIndexHold for a bin index the pass cannot read. All three carry the same three fields — op_id, node, one reason payload — and all three are mutually exclusive. Nothing makes the exclusion structural. Each halt arm clears the other cells by hand, and the invariant lives only in a comment:
One pass raises one halt, and each hold's own gate is what lets go of it — so taking one drops the others rather than leaving two cells claiming the same head for different reasons.
PR #1658 added the third cell and six such clear calls. A fourth reason makes it twelve, and a missed clear leaves two cells that name one head.
Scope
Replace the three cells with one QueueHold { op_id, node, reason }, where the reason is Quota { needed_bytes }, Settings(SettingsRefusal) or BinIndex(DefaultsReason). One pre-pass gate dispatches on the reason, so the per-arm clears go away and exclusion holds by construction.
The change is mechanical but wide: about 74 references across drain.rs, facade.rs, crates/wasm/src/lib.rs, crates/engine/tests/write_plane.rs, crates/wasm/tests/boundary.rs and the desktop tests. It replaces three public SnapshotView and SessionStatus fields and three wasm-bound classes, so it touches the TypeScript-facing surface.
AC
Body check 2026-09-15
Re-read on main at 588d646, after #1842 changed drain.rs:
- The three cells are still there and no
QueueHold type exists. The scope stands.
drain.rs makes 14 calls to clear_block, clear_settings_hold and clear_bin_index_hold. Four of those sit inside the three halt arms of apply_valve, which is the hand-clearing this issue removes.
- The three names appear about 155 times in 18 files. Six are Rust:
crates/engine/src/sync/drain.rs, crates/engine/src/sync/mod.rs, crates/engine/src/lib.rs, crates/engine/src/facade.rs, crates/wasm/src/lib.rs and crates/wasm/tests/boundary.rs, plus crates/engine/tests/write_plane.rs. The desktop surface is crates/fuse/tests/fuse_op_core.rs, which reads SessionStatus::blocked at two places.
- The TypeScript surface is wider than the body states:
packages/client protocol, codec and testkit, and five apps/web files, one of which is the QueueHoldNotice component.
Part of #992
comp:engine· M ·crates/engine/src/sync/drain.rs,crates/engine/src/facade.rs,crates/wasm/src/lib.rsFound by the simplify, reuse and altitude gates on PR #1658.
Problem
The drain holds the queue head for three reasons, and each has its own cell:
BlockedOpfor the quota,SettingsHoldfor a placement refusal, andBinIndexHoldfor a bin index the pass cannot read. All three carry the same three fields —op_id,node, one reason payload — and all three are mutually exclusive. Nothing makes the exclusion structural. Each halt arm clears the other cells by hand, and the invariant lives only in a comment:PR #1658 added the third cell and six such clear calls. A fourth reason makes it twelve, and a missed clear leaves two cells that name one head.
Scope
Replace the three cells with one
QueueHold { op_id, node, reason }, where the reason isQuota { needed_bytes },Settings(SettingsRefusal)orBinIndex(DefaultsReason). One pre-pass gate dispatches on the reason, so the per-arm clears go away and exclusion holds by construction.The change is mechanical but wide: about 74 references across
drain.rs,facade.rs,crates/wasm/src/lib.rs,crates/engine/tests/write_plane.rs,crates/wasm/tests/boundary.rsand the desktop tests. It replaces three publicSnapshotViewandSessionStatusfields and three wasm-bound classes, so it touches the TypeScript-facing surface.AC
Body check 2026-09-15
Re-read on
mainat 588d646, after #1842 changeddrain.rs:QueueHoldtype exists. The scope stands.drain.rsmakes 14 calls toclear_block,clear_settings_holdandclear_bin_index_hold. Four of those sit inside the three halt arms ofapply_valve, which is the hand-clearing this issue removes.crates/engine/src/sync/drain.rs,crates/engine/src/sync/mod.rs,crates/engine/src/lib.rs,crates/engine/src/facade.rs,crates/wasm/src/lib.rsandcrates/wasm/tests/boundary.rs, pluscrates/engine/tests/write_plane.rs. The desktop surface iscrates/fuse/tests/fuse_op_core.rs, which readsSessionStatus::blockedat two places.packages/clientprotocol, codec and testkit, and fiveapps/webfiles, one of which is theQueueHoldNoticecomponent.