# M3.9 Phase 1A — Multi-Slot Async Readback Unlocks Mixed Path

## Context

Phase 0 showed the split-plan tax path needed evidence before changing
scheduler behavior. Follow-up data identified the load-bearing guard:
`deferred_decode_emit.is_none()` prevented long-context steady-state mixed
decode+prefill when a previous greedy sample D2H readback was still pending.
Removing that guard with the old single readback slot could silently lose
tokens because a second launch could overwrite `argmax_out` before snapshot.

## What Worked

- Replaced the Qwen3 single async readback buffer with a four-slot ring.
- Started the greedy D2H snapshot at sampling launch time and stored the async
  slot on `PendingDecode`.
- Made readback finish poll the recorded slot instead of the global singleton.
- Removed the mixed-path `deferred_decode_emit.is_none()` guard from legacy and
  logical lowering; the spec-decode guard remains unchanged.
- Added `async_readback_multi_slot_no_loss` to cover four consecutive launch
  snapshots while all slots remain in flight.

## Bench Status

Status: `pending-owner-run`.

The expected validation run is the M3.9 longctx 4k/c=4 shape that previously
showed ARLE TTFT around `4961 ms` and vLLM around `2367 ms`. After this patch,
the readout should show mixed execution rather than split fallback:

```text
prefill_path=ok_true:<N>,ok_false:<M>
```

Target: `ok_true / (ok_true + ok_false)` approaches `1.0` during steady-state
long-context mixed scheduling, and TTFT moves toward the vLLM control.

## Verification

Passed locally before review:

- `cargo fmt --all --check`
- `cargo check --release -p infer --features cuda`
- `cargo check --release -p infer --no-default-features --features metal,no-cuda`
- `cargo check --release -p infer --no-default-features --features no-cuda`
- `cargo clippy --release -p infer --features cuda -- -D warnings`
- `cargo test --release -p infer --features cuda async_readback_multi_slot_no_loss --lib -- --test-threads=1`
- `cargo test --release -p infer --features cuda mixed_token_upload_uses_sampled_handoff_for_decode_rows --lib -- --test-threads=1`
- `cargo test --release -p infer --features cuda --test e2e -- --test-threads=1`
- `cargo test --release -p infer --features cuda --test greedy_consistency -- --test-threads=1`

Review:

- Round 1: 0 actionable findings.
- Round 2: P1 finding confirmed that removing the mixed guard still needed
  mixed decode rows to consume GPU sampled-token handoff instead of stale CPU
  `generated_tokens`; fixed with D2D overwrite into mixed `token_ids_gpu`.
- Post-fix review: 0 actionable findings.

## Rule

Async CPU readback must snapshot GPU outputs before any later launch can reuse
the shared output buffers. If scheduler policy allows forward progress while
an emit is deferred, the readback substrate must carry independent ownership
metadata with the pending decode record.
