From 249cb6dbc267373580dd702f8fd2b16ef4dd41a5 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Wed, 9 Sep 2026 09:21:03 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20D6=20spike=20=E2=80=94=20engine-side=20?= =?UTF-8?q?headless=20VT=20per=20session,=20recommend=20"not=20now"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Answer the D6 design question (WI-130) in a new docs/ROADMAP.md: should the engine run a headless terminal emulator per session so a cold attach ships screen state instead of byte history? - Crate comparison: vt100 (best fit — minimal grid, smallest surface), alacritty_terminal and termwiz (heavier, renderer/feature surface a snapshot server does not need). - Grounded per-session cost at 8 sessions: ~1.7–3.4 MB grid each (~14–27 MB total) ON TOP of the raw ring, plus continuous VT parse CPU on the PTY hot path — a cost the raw-ring design pays only per attach. No live number taken (no stack reachable); H1's load session is the measurement path if revisited. - Wire shape (snapshot-start kind:"screen" + server escape stream), the server-VT-vs-xterm fidelity risk and how the H2 harness proves it, and the modest mobile win (F5 already gives a bounded one-write restore). Recommendation: not now. F1–F5 (+F4) address both operator symptoms by bounding and client-side serialization without doubling engine memory or adding hot-path CPU. Revisit only if, once shipped and measured, mobile cold attach is still bottlenecked on the tail, the client cache proves untenable, or a new server-authoritative-screen requirement appears. No new work items filed. WI-130. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UtEFLJAhLiq4NfZCN2vuBb --- docs/ROADMAP.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 docs/ROADMAP.md diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 00000000..022d40cc --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,111 @@ +# Roadmap and design notes + +Forward-looking design decisions that are not yet work in flight. Each entry is +a recommendation with enough reasoning and numbers to act on — or to decide +"not now" without re-deriving the analysis. + +## Engine-side headless VT per session (D6, WI-130) + +*Spike, 2026-09-09. Part of the terminal-attach-budget initiative (WI-121).* + +### Question + +The engine keeps only **raw PTY bytes** in a per-session ring +(`scrollback.rs`), and every client rebuilds a terminal by re-parsing those +bytes into xterm.js on attach. Should the engine instead run a **headless +terminal emulator per session**, so a cold attach ships *screen state* +(tmux-style) — a serialized screen plus N lines of scrollback — instead of byte +history? + +### What the budget work already fixed + +Before committing to a VT, weigh it against what shipped in this initiative: + +- **F1 (WI-125)** bounds *every* replay to the tail budget — the 4 MiB aged-out + flood is gone; a cold or stale attach now ships at most the budget + (default 1 MiB), ground-state aligned. +- **F3 (WI-127)** stops retained tabs from time-slicing the active pane's + parser on reload. +- **F5 (WI-129)** persists a serialized xterm screen client-side, so a *reload* + restores in one write with no raw re-parse, and (once F4 lands) a *switch* + keeps the socket open and never re-streams. + +So the two operator symptoms (switch re-stream, slow fresh open) are addressed +by bounding and by client-side serialization, **without** an engine VT. The VT +is only worth its cost if a residual problem remains after these ship and are +measured on prod — see "When to revisit". + +### Crate options + +| Crate | What it is | Fit for a screen-snapshot server VT | +|---|---|---| +| `vt100` | A minimal pure-Rust parser that maintains a screen grid + scrollback, no rendering. | **Best fit.** Smallest surface and memory; exposes the cell grid directly, which is all a snapshot needs. Serializing its screen to an xterm-compatible escape stream is a bounded amount of new code. | +| `alacritty_terminal` | The terminal model behind Alacritty. | Heavier per-cell model and an API shaped around a GPU renderer's needs; more memory and more moving parts than a snapshot server wants, and version churn tied to Alacritty. | +| `termwiz` | WezTerm's terminal library. | Full-featured (its own line/cell/attribute model, image protocols); the largest surface of the three. Overkill for "hold a screen and emit a snapshot". | + +### Cost estimate (per session, at 8 busy sessions) + +No live measurement was taken (an agent session cannot reach a prod stack; use +H1's load session — `load_session_command` in the engine integration tests — to +measure before adopting). Grounded estimate for `vt100`: + +- **Memory.** A cell is a codepoint + attributes ≈ 8–16 B. A 200-col grid with + 50 visible rows + 1000 scrollback lines ≈ 210 000 cells ≈ **1.7–3.4 MB per + session**, i.e. **~14–27 MB across 8 sessions** — *on top of* the existing + 4 MiB raw ring per session unless the ring is then shrunk. The ring cannot be + dropped entirely: warm `resume_from` deltas and the history archive still read + raw bytes. +- **CPU.** The VT must parse every byte the PTY produces, on the hot reader + path. The client already parses at ~5–6 MB/s into xterm; a Rust grid VT is + faster (tens of MB/s) but it is now paid **once per session, always**, not + once per attach. For a chatty agent session this is continuous cost the + raw-ring design does not have. + +### Wire shape (if adopted) + +Add a snapshot kind rather than changing the existing one: + +```json +{"type":"snapshot-start","kind":"screen","session_id":"…","scrollback_pos":N,"reset":true} +``` + +followed by a server-produced, xterm-compatible escape stream that redraws the +screen and the last N scrollback lines, then live bytes as today. `resume_from` +still selects the byte-delta path (unchanged); `kind:"screen"` is the cold / +aged-out path only. The history archive is untouched (it keeps raw bytes). + +### Fidelity + +A server VT and the client xterm are two independent emulators; they can +disagree (edge cases in wide chars, unusual SGR, DEC private modes). The +snapshot is only correct if the server's redraw reproduces on xterm what the +program intended. The H2 corpus + fidelity harness is the right place to prove +this: parse each corpus in the candidate crate, emit the screen escape stream, +write it into headless xterm, and compare `serialize()` against xterm fed the +raw bytes. Adopt only if that comparison is clean across the corpus. + +### Mobile + +The prize on mobile would be dropping the IndexedDB cache entirely: if a cold +attach always ships a small screen snapshot, the phone need not persist raw +scrollback. But F5 already gives mobile a one-write restore from a *bounded* +serialized cache, so the marginal win is "no client cache at all" versus "a +small, capped client cache" — modest. + +### Recommendation: **not now** + +Ship and measure F1–F5 (and F4) on prod first. The engine VT doubles per-session +memory and adds continuous CPU on the PTY hot path to solve a problem the +bounding + client serialization work already targets. Revisit **only** if, with +those shipped and measured: + +1. a first cold attach on mobile is still too slow *and* the ≤1 MiB tail is the + bottleneck (not network), or +2. keeping any client-side scrollback cache proves untenable (storage, privacy), + or +3. a new requirement wants server-authoritative screen state (e.g. server-side + search over live screens, or thumbnails). + +If revisited, the first step is a `vt100`-based measurement against H1's load +session at 8 sessions, then the H2 fidelity comparison above — no new work items +are filed now.