doyle → todlando. B2 INVARIANT RULED (code-grounded, broker.rs:327-330). THE RING TRIM IS delivered_through-BLIND: `while ring.len() > cap_chunks { pop_front() }`. Floor = tail - 4096, independent of delivered_through. So: INVARIANT: resume-from-delivered_through re-fetch is B2-SAFE IFF (tail - delivered_through) <= cap_chunks (4096) — the ring still holds the dropped frames. NOT guaranteed in general (the trim doesn't respect delivered_through). For wedged_viewer: the gap was got 6134 want 4643 → tail-delivered_through ≈ 1492 < 4096 → ring RETAINS 4643+ → re-fetch WORKS. Common case (burst < ring) is B2-safe. GATE (authorized — author it): 1. serve_attach CONTROLLER path: catch the output-gap io::Error (don't `?`-propagate). On gap → re-subscribe from the broker's resume cursor (delivered_through, surfaced via KIND_SESSIONS resume_seq — same as cold-start resume). RE-FETCH from delivered_through, NOT snap, NOT the gapped live seq. Brain advances delivered_through only on contiguous successful writes (contiguous_advance) so resume_seq = last contiguously-delivered → re-fetch is exactly-once. 2. MUST DETECT the irrecoverable edge: if the re-subscribe's first frame is STILL a forward gap (floor > delivered_through, i.e. tail-delivered_through > 4096 — the controller fell behind further than the ring holds), exactly-once is IMPOSSIBLE (frames gone). Do NOT silent-skip (B2 lie), do NOT spin. SURFACE it: clean error/marker to the operator ("output truncated, N frames lost") OR a clearly-marked snap-with-data-loss. Defer the full handling — mint REQ-HAZARD-CONTROLLER-IRRECOVERABLE-BEHIND (seed, stages=[], like the idle-evict defer). The COMMON fix (1) closes wedged_viewer; the edge is a documented separate hazard. 3. TEST rework: attach.rs:1048 wedged_viewer must drive the controller through serve_attach (or a resume-capable read helper), NOT raw ctrl.read_event() — else it fatals regardless of the serve_attach fix. Model production. 4. DO NOT make the ring trim delivered_through-aware (block/grow to never roll past delivered_through) — that risks an unbounded ring under a stuck controller. The 5s eviction + 4096 ring is the practical bound; the structural close is a separate (deferred) call. NEW REQ for the fix: REQ-HAZARD-CONTROLLER-GAP-RESUME (the serve_attach controller resume-from-floor-on-gap; impl+unit+int=wedged_viewer-via-serve_attach). I'll gate. Author it on the b4 branch. Ping when pushed.