Skip to content

Shellper replay buffer stores the stream, not the screen: unbounded growth for full-screen TUIs (17MB+ replays) #1205

Description

@amrmelsayed

Problem

The shellper's replay buffer is bounded by line count (10,000 lines) plus an unbounded whole-kept partial. A full-screen TUI (Claude sessions) redraws in place via cursor addressing and emits almost no newlines, so an entire multi-day session accumulates as one line that never ends: the line cap never triggers and the buffer grows monotonically for the life of the session.

Measured impact (from the #1198 incident, 2026-07-19):

Byte-trimming the buffer from the front was deliberately rejected previously (#1047): a trim can cut mid-escape-sequence or drop the alt-screen-enter, corrupting replay rendering. So the buffer is whole-by-design, and its growth is unbounded-by-design — but the wire (16MB frame cap) and the UI (xterm parse cost per attach) are not.

Containment already in place (PR #1204)

These make the current design safe, not right:

  • Tower's frame parser discards oversized frames ('frame-skipped') instead of treating them as fatal stream errors.
  • New shellpers truncate the replay they send to an 8MB tail (REPLAY_PAYLOAD_MAX).
  • Adoption seeds at most a 1MB tail into the ring buffer (RING_SEED_MAX_BYTES); viewers rely on the post-connect resize nudge to repaint TUIs.

Every cap is a lossy tail-cut that can transiently render rough until the nudge repaints. The buffer itself still grows without bound in shellper memory.

Proposed direction

Make replay O(screen), not O(session lifetime): store something screen-shaped rather than stream-shaped.

  • Preferred: run PTY output through a headless terminal-state emulator inside the shellper and serve reconnecting clients the current screen state (plus bounded recent scrollback). Replay size becomes a few hundred KB at any session age, with no escape-sequence corruption risk, and the resize-nudge crutch becomes unnecessary.
  • Cheaper alternative: reset the buffer at detectable full-frame boundaries (clear-screen / cursor-home redraw sequences), keeping only bytes since the last full repaint.

Wire-compat constraint: running shellpers are old binaries that survive Tower upgrades, so Tower must keep tolerating old-style (potentially oversized) replays regardless.

Acceptance criteria

Refs: #1198, PR #1204.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/terminalArea: Terminal-specific (PTY, vscode terminal pane)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions