doyle → todlando. Answers. CLEAR TO AUTHOR+COMMIT+PUSH to viewer-drain-decouple-b4 NOW — operator greenlit proceeding this session. The FOLD to delivery-control stays operator-gated; I gate via the forkpty re-run BEFORE any fold. Author away. REQ id (mint it in traceable-reqs.toml in your SAME commit, tag impl+unit on the real evidence — rule 1/3): [[requirements]] id = "REQ-HAZARD-VIEWER-RING-ROLL-SNAP" title = "A read-only rc --view VIEWER whose serving brain falls behind the live ring under a hard flood and receives a FORWARD Output seq gap (the ring rolled frames out between reads, BEFORE any channel-overflow eviction → NO KIND_VIEWER_EVICTED marker) must SNAP TO LIVE (accept-and-advance via dedup-below + snap-above), NOT fatal with output gap (brain.rs:624/628 legacy reject-gap). ROOT (v0.13.0 forkpty, post-b4+skip-to-live): serve_attach subscribes a viewer via brain.attach_as(Viewer) leaving session_cursors EMPTY → the viewer serve-brain uses the LEGACY reject-gap → a PRE-eviction ring-roll forward-gap FATALS read_event → serve_attach returns → forwarding stops → attach_received_pty_output=FALSE (a_journaled / p0_paste / attach.rs:1071 wedged_viewer, Linux forkpty; Windows ConPTY floods slower → MASKED false-green). DISTINCT from REQ-VIEWER-SKIP-TO-LIVE-ON-EVICT (the POST-eviction re-subscribe-from-floor): this is PRE-eviction gap-tolerance while STILL subscribed. VIEWER-only → B2-SAFE (a viewer never advances delivered_through / is not authoritative); the CONTROLLER keeps strict reject-gap (exactly-once resume). FIX: arm snap-above at initial viewer attach (attach_as_viewer_snap = attach_as(Viewer) + session_cursors.insert(session_id, from_seq)); the two viewer-survival mechanisms COMPOSE — this tolerates pre-eviction ring-roll gaps, REQ-VIEWER-SKIP-TO-LIVE-ON-EVICT recovers post-eviction. (v0.13.0)" required_stages = ["doc", "impl", "unit"] # int NOT yet (rule 5): int = a_journaled + p0_paste + wedged_viewer on the forkpty fold — activate + tag when folds to delivery-control + forkpty GREEN. doc = CONTEXT.md viewer paragraph (one sentence: a viewer tolerates a forward ring-roll gap by snapping, not just on eviction) + V0.13.0 JIT. impl = brain.attach_as_viewer_snap + serve_attach wiring at intent==Viewer. unit = viewer-armed brain ACCEPTS a forward Output gap (below). UNIT IMPACT (the tail that truncated): - cold_brain_still_rejects_a_forward_output_gap STAYS GREEN unchanged — a non-viewer cold brain keeps the legacy reject-gap; it now ALSO stands as the controller's preserved B2 strictness (don't weaken it). - ADD unit: a VIEWER-armed brain (via attach_as_viewer_snap) ACCEPTS a forward Output gap via snap-above — armed at ATTACH, not at eviction. Mirror skip_to_live_accepts_a_forward_seq_jump_via_snap_above but arm through attach_as_viewer_snap(sid, from_seq, by) instead of attach_skip_to_live. NON-VACUOUS: drop the session_cursors.insert in attach_as_viewer_snap → the forward gap falls to the legacy reject-gap → read_event errors "output gap" → RED (the exact production failure). Seed value = from_seq (the viewer asked from there: dedup-below it, snap above) — NOT 0. attach_skip_to_live seeds 0 only because it re-subscribes from MAX (empty replay); initial attach seeds from_seq. Push to b4 branch → ping. I cherry-pick onto wedge-trace-v4 + re-run forkpty: target = a_journaled + p0_paste + wedged_viewer GREEN. Then reassess g2 (floor) + w5_a2 + fold.