Skip to content

fix(terminal-core): preserve cache fields dropped on every remount - #31

Merged
rockyway merged 1 commit into
developfrom
fix/terminal-cache-field-drop
Aug 15, 2026
Merged

fix(terminal-core): preserve cache fields dropped on every remount#31
rockyway merged 1 commit into
developfrom
fix/terminal-cache-field-drop

Conversation

@rockyway

Copy link
Copy Markdown
Contributor

What

TerminalEngine.mount() ends by rebuilding the terminal cache entry as a fresh object literal — a deliberate delete-then-set so the key moves to the end of the Map for LRU ordering. It copied ~25 TerminalCacheEntry fields but omitted four the type declares.

Every remount (tab switch, pane collapse, cross-window detach, reload) silently reset them:

Field Consequence of losing it
agentColorLocked (cache.ts:23) Gates the colour-OSC guard, so a per-agent colour lock broke on any remount and the running program's palette OSCs could overwrite the assigned scheme
lastDataAt / lastInputAt (cache.ts:51, :58) The quiet-period gates. undefined short-circuits every && guard as already settled, risking a term.reset() mid-keystroke right after a remount
lastSnapshot (cache.ts:50) Forces one guaranteed extra full repaint on the next mirror-mode resync

Fix

Spread the existing entry first, before the explicit field list. This fixes the class of bug: any field added to TerminalCacheEntry in future survives a remount by default instead of having to be remembered here. Explicit keys still come after the spread and win wherever the rebuild intentionally overwrites or resets a field.

One line of production code.

Verification

  • Added a cache.test.ts case that sets all four fields, remounts on the same cacheKey, and asserts they survive.
  • Red/green confirmed: with the spread removed the test fails Expected: true, Received: undefined; with it, it passes.
  • Full terminal-core suite: 483 passed, 30 suites.

Notes

Found during Canvas Mode Phase 0 design review. Pre-existing and unrelated to Canvas Mode — worth landing on its own.

TerminalEngine.mount()'s end-of-mount cache rebuild (the delete-then-set
that reorders the entry for LRU eviction) copied ~25 TerminalCacheEntry
fields into a fresh object literal but omitted four the type declares:
agentColorLocked, lastSnapshot, lastDataAt, lastInputAt. Every remount
(tab switch, pane collapse, etc.) silently reset them to undefined:

- agentColorLocked gated the color-OSC guard, so a per-agent color lock
  broke on any remount and the running program's palette OSCs could
  start overwriting the assigned scheme.
- lastDataAt/lastInputAt are the quiet-period gates for the heal/resync
  settle checks; undefined short-circuits every "&&" guard as already
  settled, risking a term.reset() mid-keystroke right after a remount.
- lastSnapshot losing its value forces one guaranteed extra full repaint
  on the next mirror-mode resync.

Fix: spread the existing cache entry first, before the explicit field
list, so any field TerminalCacheEntry declares survives a remount by
default instead of needing to be named here. The explicit keys still
come after the spread and win where the rebuild intentionally
overwrites/resets a field (terminal, fitAddon, disposables, kbState,
win32State, etc.) — spreading first cannot clobber them.

Added a cache.test.ts case that sets all four fields, remounts on the
same cacheKey, and asserts they survive; confirmed it fails against the
prior literal and passes with the spread.
@rockyway
rockyway merged commit 1734f0f into develop Aug 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants