--- id: w2-spawn-return name: Wave 2 — spawn-and-return plumbing + exit/status reporting mechanics status: pending depends_on: [w2-dispatch-decline-core] scope: narrow risk: medium impact: phase level: implementation --- ## Description Land the structural constraint in code (ADR-006 decision 4): russh at this pin runs server handler callbacks **inline inside the session's packet loop** — a blocking handler suspends all packet routing for the connection and deadlocks against the peer's immediate follow-up bytes (POC-3 proved by construction with receive-pack). Therefore: - **Every serving path is `tokio::spawn`ed; handlers return fast.** The dispatch's routing stubs become spawned serving paths (still declining stubs — the seams are wave 3), so the pattern is in place before any real mapping hangs on it. - **Exit/status reporting mechanics** — reporting from spawned tasks rides the cloned `Session::handle()`: `exit-status` sent **before** channel close, from the spawned task. The *derivation contract's rules* (which code → which SSH exit shape) are wave-3 session-mapping material (`mapping.md` rules 1–6); this task lands the mechanics — the handle clone, the exit-status send, the ordering-before-close — proven end to end. - **The proof** — a stub serving path that spawns, completes asynchronously (after the handler has returned), and reports `exit-status(N)` + close through the cloned handle; the loopback harness client observes the exit-status arriving after the handler already returned. A handler-returns-fast assertion (the handler completes while the spawned task is still running) pins the constraint as a test, not a convention. This is a load-bearing correctness constraint of the whole spine, not an implementation choice — the wave-3 mappings inherit the pattern mechanically from the stubs this task leaves behind. ## Acceptance Criteria - [ ] Every serving path (the dispatch stubs) runs in a `tokio::spawn`; no handler hook awaits a serving path inline - [ ] Exit/status reporting from spawned tasks rides the cloned `Session::handle()`; `exit-status` is sent before channel close - [ ] A harness test proves the mechanics end to end: the client observes `exit-status(N)` from a spawned stub that completed after the handler returned - [ ] A handler-returns-fast assertion pins the structural constraint as a test - [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check` pass ## References - docs/architecture/decisions/006-mapping-bridge-and-dispatch.md (decision 4 — the structural constraint) - docs/architecture/mapping.md §The structural constraint: spawn-and-return; §exit_status row (sent before close, from the spawned task) - docs/plans/implementation.md §Wave 2 (the spawn-and-return bullet) - docs/research/poc-3-mapping-bakeoff.md (the deadlock-by-construction evidence) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion