feat(web): F5 persist serialized xterm state for one-write reload restore (WI-129) - #672
Closed
thedancingdeveloper wants to merge 1 commit into
Closed
Conversation
…tore
On reload a pane re-parsed its cached raw scrollback tail into xterm — the
per-pane parse cost F1 already bounds, but still a full re-parse. F5 persists
the pane's xterm screen + a capped scrollback via @xterm/addon-serialize and
restores it in a single term.write, no raw re-parse, then reattaches with the
F1-bounded resume_from delta.
Cache format (terminalCache.ts): an entry now holds EITHER a serialized screen
(a live pane, the fast path) OR raw bytes. Raw is still written by the headless
pre-warm path (terminalPrewarm has no xterm to serialize) and as a live-pane
fallback when serialization is unavailable, and it still reads a pre-F5 entry —
so the format change needs no DB version bump or migration (a deviation from
the plan's "bump + drop v1", forced by the pre-warm path the plan did not
account for; keeping raw as a first-class shape is strictly more compatible).
The ArrayBuffer check is now realm-safe.
Restore (Terminal.tsx): restoreCache() writes a serialized entry in one go, or
re-parses a ground-state raw tail through the shared queue, and it composes with
F3 — a parked tab defers the whole entry and restores on first activation. The
serialized scrollback is capped at 2000 lines (SERIALIZE_MAX_SCROLLBACK), well
under the 5000-line buffer, to bound the cache entry and the restore write.
Tests:
- serializeRestore.test.ts: over the real corpus (H2), serialize→restore→
reserialize is a fixed point (screen + scrollback preserved) through the same
core VT parser (@xterm/headless); the cap holds. This also lands H2's deferred
serialize()-compare fidelity.
- terminalCacheRoundtrip.test.ts (fake-indexeddb): serialized and raw entries
round-trip, raw is ground-state trimmed, a malformed entry reads as cold.
- terminalPrewarmAttach updated for the { data } payload shape.
Verification: pnpm typecheck clean; pnpm vitest 938 passed (10 new); pnpm build
bundles addon-serialize. WI-129.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtEFLJAhLiq4NfZCN2vuBb
This was referenced Sep 9, 2026
Contributor
Author
|
Cleanup: superseded by the single promotion PR #676. All of this work is already integrated into |
thedancingdeveloper
deleted the
feat/terminal-attach-f5-serialize-state
branch
September 9, 2026 20:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sixth PR of the terminal-attach-budget initiative (WI-121). Stacked on #671 (H2); all land on
feat/terminal-attach-budget.What this does (F5 / WI-129)
On reload a pane re-parsed its cached raw scrollback into xterm — the per-pane parse cost F1 bounds but still a full re-parse. F5 persists the pane's xterm screen + capped scrollback via
@xterm/addon-serializeand restores it in a singleterm.write(no raw re-parse), then reattaches with the F1-boundedresume_fromdelta.Cache format — backward-compatible union (deviation from the plan, explained)
The plan called for a
v2 { serialized }format with a DB bump that drops v1. ButterminalPrewarm.tsalso writes the cache and has no xterm to serialize (it's a headless pre-warm) — it must keep writing raw bytes. So an entry now holds either a serialized screen (live pane, fast path) or raw bytes (pre-warm, live-pane fallback, and any pre-F5 entry). Raw stays a first-class restore path, which is strictly more compatible than the plan's wipe — no DB version bump or migration needed. TheArrayBufferdetection is now realm-safe.Restore composes with F3
restoreCache()writes a serialized entry in one go, or re-parses a ground-state raw tail through the shared queue. A parked tab (F3) defers the whole entry and restores on first activation. Serialized scrollback is capped at 2000 lines (SERIALIZE_MAX_SCROLLBACK), well under the 5000-line buffer, to bound the cache entry and the restore write.Tests
serializeRestore.test.ts— over the real corpus (H2),serialize → restore → reserializeis a fixed point (screen + scrollback preserved) through the same core VT parser (@xterm/headless); the cap holds. This also lands H2's deferredserialize()-compare fidelity.terminalCacheRoundtrip.test.ts(fake-indexeddb) — serialized and raw entries round-trip, raw is ground-state trimmed, a malformed entry reads as cold.terminalPrewarmAttachupdated for the{ data }payload shape.Verification
pnpm typecheckclean;pnpm vitest— 938 passed (10 new);pnpm buildbundlesaddon-serialize.The H3 mocked before/after reload-budget measurement lands with H3's spec (WI-124).
🤖 Generated with Claude Code
https://claude.ai/code/session_01UtEFLJAhLiq4NfZCN2vuBb