---
name: v0121-l2-attach-wedge-proven
description: "v0.12.1 L2/L3/L4 LANDED @e883f45 — ATTACH-WEDGE prove-don't-change (stale root debunked, real-harness int gate green) + a disk-full machine event"
metadata: 
  node_type: memory
  type: project
  originSessionId: d19346dd-4d44-4649-8e79-b0e9d71eb078
---

v0.12.1 **L2/L3/L4 LANDED @e883f45** (branch v0.12.1-lifecycle). Follows L0 @cf5eab4 + L1 @5ae68f8. See [[v0121-l1-viewer-close-detach-findings]], [[v0121-l0-attach-deadlock-root]].

**PROVE-DON'T-CHANGE (like M11-W4):** REQ-HAZARD-ATTACH-WEDGE activated `[int]`, ZERO impl/unit diff. The post-L0 code already prevents the wedge. The B-side root ("loopback write_all blocks forever → parks the 2-worker net runtime") is STALE:
1. serve_attach forwards FIRE-AND-FORGET (`net_stream_send` op_id=None); broker-side `send_stream` is ALREADY bounded by `bounded_block_on` (BROKER-QUIC-DEADLINE, 10s) — not forever.
2. the loopback duplex is drained broker-INTERNALLY by the operator row's own read pump (nethost.rs:544 `RecvHalf::Loopback`); for an ordinary attach stream (`retentive_cap==0`) it NEVER parks → `peer_w` never backs up on a dead rc (a dead rc = a dropped IPC subscriber against a bounded EVICTING ring).
3. `bounded_block_on` = `runtime.handle().block_on` → parks the BROKER DISPATCH thread, NOT a net worker → worker-pool exhaustion doesn't hold.
- L3: broker exit-waiter `waiter.wait()` (broker.rs:889) returns on abrupt taskkill too → session removed → `reconcile_hosted_liveness` (livehost.rs:511) offlines the controllable online perch within one `LIVE_RECONCILE_INTERVAL_MS`=5s tick. L4: daemon stop already bounded.

**int gate** = `crates/spt/tests/attach_wedge_e2e.rs` (REAL detached daemon + dummy-harness fixture): serve victim (rc sees tick), abruptly kill rc (dropped pump)+kill PTY child → NEW endpoint still online+served (L2), dead endpoint OFFLINED within tick (L3: `LIVENESS_RECONCILE_OFFLINE:wedge1`), daemon stop bounded (L4). GREEN 6.82s. traceable EXIT=0, clippy --workspace -D warnings clean. **doyle pinged for independent gate re-run.** CLIPPY GOTCHA: a spawned Child never `wait()`ed trips `clippy::zombie_processes` (-D warnings) → reap inside the helper (`kill()`+`wait()` = the abrupt drop anyway).

**DISK-FULL MACHINE EVENT (resolved):** C: hit 100% FULL (36K free of 1.9T) mid-session → cargo build died `rustc-LLVM ERROR: IO failure on output stream: no space on device`. My workspace `target/` had grown to **307.5GB** (253694 files) → `cargo clean` reclaimed → 288G free, box stable. ~1.6TB still used ELSEWHERE on the 1.9T drive (NOT mine; did NOT hunt/delete across the shared box). Notified operator (powershell WinForms balloon — WinRT toast type unavailable in pwsh7) + doyle. CI runner shares this box (HFENDULEAM) — watch for CI disk-fails. cargo clean of MY workspace target is scoped+safe (live agents run from installed binaries, CI from GITHUB_WORKSPACE — not this debug target).

**Wave 3 Picker roots (investigated read-only):** P2 REQ-PICKER-ONLINE-ACTION = REAL small bug — model.rs `confirm_options` offers redundant `Start` on an online free endpoint (`confirm_terminal` maps online Start→Attach), the mislabeled "Start now"; fix = gate `Start` OFFLINE-only. P1 REQ-PICKER-HISTORY-FRESH = correct "fresh=no committed context yet" semantics, NOT a loader bug (PICKER-2 already fixed) → needs doyle product call.

**NEXT:** await doyle L2 gate + L1 breakaway ruling. Independent unblocked work = Wave 3 P2 fix or Wave 4 E1 (endpoint-list merge-local, fully specified). v0.12.1 release pends doyle gate on all waves.
