Symptom
session:screen is 93% of recorded IPC bytes (recording g00000190: 16,728 frames × 8.8 KB = 158 MB / 123 min) and arrives 6–10×/s per thinking session, yet the live UI reads only picker (TileLeaf.tsx:970) and the Enter-baseline latestScreenRef (useComposerKeybinds.ts:233,321, claudePaste.ts:155-162). runtime.screen/recentScreen/*Markdown are consumed only by ReaderView's fallback, DebugPanel/DevDebug and saveDebugBundle. Every frame is still structured-cloned, dispatched, committed via setRuntimes (new map identity → whole-tree re-render, see the selectors issue) and stringified by the recorder. Routing is per owning window, never per visible/attached/focused session (sendToSessionWindow, windowRegistry.ts): terminal-mode panes, hidden tiled tabs, Reader-hidden sessions and background grid tabs all receive full frames.
Expected
- Renderer declares per-session interest:
none (terminal-mode / hidden / background), picker (visible feed panes), full (a lease while ReaderView live mode, DebugPanel, paste-confirm or the Enter-baseline capture need the text). Forwarder sends full frames only for full, a tiny session:picker message only when the picker state changes, nothing otherwise.
- Enter-baseline and paste-confirm take a lease before they need the screen or call a new
session:get-screen invoke; they must never observe a stale screen.
- The headless snapshot in main is NOT throttled or gated (condition parsers, prompt gate, permission/trust/resume detection run on every raw frame for every session). Only the IPC forward is gated.
- Minimal first step (≤1 day): renderer tells main "terminal-mode or hidden" → forwarder skips
session:screen for those sessions (excluding Reader-visible ones); picker split onto its own channel on change.
Impact
Estimated −90–99% screen messages, −90% recorder bytes, −60–100 setRuntimes/s in a 10-session workspace, −~1 MB/s main-thread serialisation.
Refs #746, #761 (spinner gate + wire alias, complementary). Refs #103, #390. Source: architecture audit 2026-09-03 (scratchpad/arch-perf-audit.md).
Symptom
session:screenis 93% of recorded IPC bytes (recording g00000190: 16,728 frames × 8.8 KB = 158 MB / 123 min) and arrives 6–10×/s per thinking session, yet the live UI reads onlypicker(TileLeaf.tsx:970) and the Enter-baselinelatestScreenRef(useComposerKeybinds.ts:233,321,claudePaste.ts:155-162).runtime.screen/recentScreen/*Markdownare consumed only by ReaderView's fallback, DebugPanel/DevDebug andsaveDebugBundle. Every frame is still structured-cloned, dispatched, committed viasetRuntimes(new map identity → whole-tree re-render, see the selectors issue) and stringified by the recorder. Routing is per owning window, never per visible/attached/focused session (sendToSessionWindow,windowRegistry.ts): terminal-mode panes, hidden tiled tabs, Reader-hidden sessions and background grid tabs all receive full frames.Expected
none(terminal-mode / hidden / background),picker(visible feed panes),full(a lease while ReaderView live mode, DebugPanel, paste-confirm or the Enter-baseline capture need the text). Forwarder sends full frames only forfull, a tinysession:pickermessage only when the picker state changes, nothing otherwise.session:get-screeninvoke; they must never observe a stale screen.session:screenfor those sessions (excluding Reader-visible ones); picker split onto its own channel on change.Impact
Estimated −90–99% screen messages, −90% recorder bytes, −60–100
setRuntimes/s in a 10-session workspace, −~1 MB/s main-thread serialisation.Refs #746, #761 (spinner gate + wire alias, complementary). Refs #103, #390. Source: architecture audit 2026-09-03 (scratchpad/arch-perf-audit.md).