---
name: bug6-screen-grid-plan
description: "#6 REQ-BROKER-SCREEN-GRID BUILT @dcbbf3d — server-side vte ScreenGrid + clean repaint on cold attach (v0.19.0 P6); awaiting doyle gate"
metadata: 
  node_type: memory
  type: project
  originSessionId: 250041a9-eea2-457a-8792-a5f724c30908
---

**#6 REQ-BROKER-SCREEN-GRID — GATE GREEN ✓ @dcbbf3d** (doyle gated: independent fresh-target traceable 0 / clippy 0 / nextest 540/540). Branch `bug6-screen-grid` (worktree `../spt-core-bug6`, off field-bug-milestone @026a118) STAYS UNMERGED — folds into field-bug-milestone at the v0.19.0 release-PR cut (fold-in protocol). Design refinement (cold-attach scoping) BLESSED by doyle. Design = `V0.19.0-P6-SCREEN-GRID-DESIGN.md` + `docs/adr/0031-...md`. Fixes #6 (scrollback corrupt on rc to running alt-screen TUI) + #7 (residual cells) + #8 (resize artifacts). See [[counter38-field-bug-milestone]].

## What shipped
- **spt-term `screen.rs`** (new, clean-room): `ScreenGrid` wraps `vte::Parser` + a `GridState impl vte::Perform`. Pinned **`vte = "0.13"`** (resolved 0.13.1 — the ONE non-clean-room dep, ADR-0031). Handles print+deferred-autowrap, C0 (BS/HT/LF/CR), CSI (CUU/D/F/B/CUP/CHA/VPA/ED/EL/ECH/ICH/DCH/IL/DL/SU/SD/SGR/DECSET+DECRST 25·47·1047·1049/DECSTBM), ESC (RIS/DECSC/DECRC/IND/RI/NEL). `render_repaint()` → clean paint: `?1049h/l` alt-select + `2J` + row-by-row cells w/ minimal SGR runs (trims trailing default-blank) + current pen + cursor visibility + CUP. `Color{Default,Idx,Rgb}` (basic/bright/256/truecolor). `resize` reallocs preserving top-left. lib.rs `pub use screen::ScreenGrid`.
- **broker.rs**: `OutputLog.grid: ScreenGrid`, fed in `append` AFTER fan-out (7.12 lock-clean). `repaint_initial(from_seq)` returns the repaint ONLY for **COLD attach (from_seq==0)** at pseudo-seq `next_seq-1`; else None. `become_controller`/`add_viewer`: repaint on cold, ELSE raw-ring replay (resume unchanged). `set_size_and_notify` resizes grid.
- **brain.rs**: new `baseline_next_output` bool. `attach`/`attach_as` set it `= (from_seq==0)`; legacy read path adopts the first output frame's seq as baseline (the repaint's forward jump) then reverts to strict reject-gap. So cold-attach repaint accepted; **resume-from-floor (from_seq>0) = raw replay + strict reject-gap + irrecoverable-behind UNCHANGED (B2)**.

## KEY DESIGN REFINEMENT (surfaced to doyle, folded in design doc §Build-time refinement)
Repaint's collapsed pseudo-seq is a FORWARD JUMP; controller brain dedup = strict reject-gap (B2) → FATALS it → cold attach to pre-populated ring wedged. **Resolution = repaint scoped to COLD attach only; resume keeps raw re-fetch.** Did NOT take the design's "re-repaint on mid-session resume" (would obsolete irrecoverable-behind) — deferred. Conservative: #6 changes ONLY the cold-attach initial batch (its documented scope).

## Gate (my side, PASSED)
- clippy `--workspace --all-targets -- -D warnings` = 0.
- traceable-reqs check exit 0: REQ-BROKER-SCREEN-GRID +doc+impl+unit+int (activated in toml).
- nextest `-p spt-term -p spt-daemon --no-fail-fast` = **540/540 pass** (5 leaky=benign).
- GOTCHA fixed: `render_until` test helper asserted strict seq-from-0 → baseline on first frame; `attach_survives` re-framed onto `ScreenGrid` screen-equality (was raw byte-identity); `superseded_controller_writer` re-cast onto the single repaint frame w/ distinct seqs.

## NEXT
1. Reping doyle @dcbbf3d for the gate (fresh-target clippy+traceable+nextest). NO [twohost].
2. On his green: land bug6 + bug4 (W7) onto field-bug-milestone → then #2-TTL + #3-local-view → ONE v0.19.0 (fold-in). See [[counter38-field-bug-milestone]].
3. Follow-up REQ (out of scope): sticky ID-marker redo (#8/#14 marker-half) as a grid-backed overlay now that the grid exists.

GOTCHAs carried: [[gate-clean-target-not-incremental]] (doyle gates fresh target), [[shared-seam-change-run-all-seam-tests]] (ran all attach/dispatch), transient LNK1104 exe-lock from a killed nextest run (retry after the process dies; NOT code).
