HERTZ RCA — stale glyphs / missing whitespace across picker + rc PTY surfaces VERDICT There is no shared whitespace-trimming function. Raw live PTY bytes, including spaces and ANSI erases, are preserved end-to-end. The field symptoms are three distinct render-lifecycle failures with one common invariant violation: the physical terminal is mutated or terminated outside its renderer's ordered state model. 1) BROKER EXIT OVERTAKES FINAL OUTPUT — primary PTY loss - Exact live path preserves bytes: spt-term/src/reader.rs:118-161 -> spt-daemon/src/broker.rs OutputLog::append:903-968 -> msg.rs base64 -> attach.rs:600-609 -> spt/src/rc.rs:1966-1993 decode + write_all. - The broker exit waiter at broker.rs:3757-3792 independently direct-writes KIND_EXIT via all_sinks(). Normal PTY output is queued through controller_writer at 1675-1778. The per-connection gate serializes writes but does not impose producer order. - Exit can therefore overtake the last queued Output. serve_attach returns immediately on Exit (attach.rs:610-617), emits AttachRecord::Exit+FIN; rc returns immediately on that record (rc.rs:1995). A final space/EL, SGR reset, cursor-show, DECSTBM reset, or ?1049l can be stranded. - This race is already admitted in crates/spt-daemon/tests/broker.rs:376-403: the test says Exit can arrive BEFORE Output and compensates by continuing to drain. Production rc does not. Source fix: make the PTY drain/output writer the sole FIFO sequencer for terminal Output + Exit. Wait for drain EOF/completion, then enqueue Exit behind all prior output per sink. A mutex alone is insufficient. Additive defense: Exit { after_seq } final watermark; attach/rc must not terminate before render cursor reaches it. Regression: short-lived child emits `XXXX ESC[2K ESC[?25h ESC[?1049l` then exits; assert that exact Output precedes Exit through production broker->attach->rc ordering. 2) RC NEVER UNCONDITIONALLY RESTORES DISPLAY STATE — explains dirty terminal after intentional exit, disconnect, timeout, displacement, error - run_attach_inner rc.rs:1254-1303 restores only StatusRow when enabled (currently disabled), then drops RawGuard and prints prose. - RawGuard::drop rc.rs:841-856 restores input raw mode, mouse, and Windows console mode; it does NOT reset child-controlled VT display state. - Every final path can leave alt screen/cursor/SGR/scroll region dirty: detach, child exit, displacement, first-event stall, fatal error, and 30s reconnect give-up. The reconnect banner itself clears+homes (1719-1744), then give-up prints at its current centered cursor without a final reset. Fix: separate a display RAII guard from OS input/raw guard. On every exit path, before disabling VT output mode, best-effort emit at least SGR reset, reset full scroll region, show cursor, leave alternate screen, clear+home (or restore a captured owned surface where possible), then print the parting line. Cleanup must be idempotent and run on errors/unwind too. Preserve the order: cleanup bytes while Windows VT processing is enabled, then restore prior output mode. Regression: dirty sink with `ESC[?1049h ESC[?25l ESC[31m`; exercise every PumpEnd/error class; assert cleanup postlude precedes final prose. 3) PICKER INVALIDATES RATATUI'S DIFF BASELINE WITH OUT-OF-BAND STDERR - Picker owns ratatui alternate screen and previous Buffer (picker/mod.rs:95-118). - Purge runs inline while the TUI is active (picker/mod.rs:165-178). cmd_endpoint_purge writes diagnostics/PURGED to stderr (cli.rs:12707-12826), moving/mutating the same physical alternate screen without updating ratatui's previous Buffer. - Next Terminal::draw correctly emits only model differences; cells ratatui believes are already blank are skipped, leaving stderr glyph fragments. This exactly matches the x-purge confirmation/report symptom. It is unrelated to ScreenGrid. Fix, preferred: make purge core return structured outcome without terminal output; picker converts it to model.flash and remains sole renderer. Alternatives: suspend/restore terminal around the command and recreate/clear Terminal baseline before re-entry, or force a backend clear + baseline reset before next draw. Do not merely print more spaces. Regression: recording/stateful backend, draw ConfirmPurge, inject an external display mutation, transition back to list, assert next frame reconstructs the complete target screen. Current pure TestBackend view snapshots cannot catch baseline desync. 4) COLD REPAINT STATE GAP — secondary PTY desync - ScreenGrid render_repaint (spt-term/src/screen.rs:577-623) omits trailing default blanks/blank rows only AFTER `ESC[2J`; that omission is semantically correct, not the missing-whitespace bug. - But the grid tracks DECSTBM margins (screen.rs:790-802) and repaint does not emit them. The client and server grids can therefore interpret subsequent raw scrolling against different regions, leaving/moving stale rows. Fix: replay every tracked render-affecting mode, at minimum DECSTBM, before final cursor placement. Add emulator/stateful contract: dirty screen + synthesized repaint + next raw frame must equal server grid. HERDR LEARNINGS (ogulcancelik/herdr v0.7.4, commit 02a6e874; AGPL — use ideas, do not copy code without license review) - src/protocol/render_ansi.rs BlitEncoder holds a rectangular per-client last_frame. First/resize frame emits ED2+home; later frames compare every display cell and write changed symbols INCLUDING literal spaces. No trim. Wide-glyph replacement invalidates max(old,new) display width. - src/server/render_stream.rs + headless.rs separates prepare from commit and commits baseline only after queue try_send succeeds; reset_baseline forces a full repaint. - TerminalGuard Drop resets wrap/focus/paste/mouse/cursor and ratatui terminal state; panic hook also restores. - Useful principles for SPT: one baseline per terminal surface; blanks are real cells; new attach/reconnect/resize/write failure invalidates baseline; full clear+repaint after invalidation; diff all cells or EL variable-length row suffixes; commit only after successful delivery; idempotent comprehensive teardown. - Do NOT import Herdr's full multiplexer stack. Also do not copy its thin-client bug: it ignores stdout write/flush errors and commits anyway. RANKED IMPLEMENTATION ORDER P0 broker Output-before-Exit ordering + regression. P0 unconditional rc VT teardown guard + matrix regression. P0 picker structured/no-output purge path + baseline-desync regression. P1 ScreenGrid tracked-mode replay/stateful emulator contract. P2 optional Exit.after_seq defense and Herdr-style per-client semantic baseline if SPT later transforms live frames instead of forwarding raw bytes. The screenshot shape is consistent with a renderer believing blank cells are unchanged while the physical screen contains externally/previously written glyphs. The code disproves generalized whitespace filtering; fix lifecycle/order/baseline ownership at each source.