replace-custom-pty

Replace custom PTY/scrollback rendering with battle-tested terminal emulator crate

Metadata

Statusabandoned ‖ paused
Created2026-04-29T13:53:43.000770807+00:00
Tagspriority-high,refactor,tui,pty,architecture

Description

Description

We've shipped 3+ targeted fixes for PTY scrollback corruption in the last 24 hours and the symptom keeps coming back in different code paths or different triggers. Pattern observed:

  • fix-tui-pty (commit e0f5d029b) — resize-trigger duplication
  • fix-pty-scrollback (commit e18de347c) — initial-render duplication
  • And now: still corrupting on what user reports as 'every resize, but resize happens all the time'

User quote: 'why is this so hard?'

The honest answer: terminal emulation is genuinely hard. Custom code is fighting xterm's state machine (cursor save/restore, scroll regions, alt-screen, SGR/SGR-mouse, line-wrap continuation, OSC sequences, mode bits). Every fix surfaces the next bug. Time to stop.

Goal

Replace the home-rolled PTY parsing + scrollback buffer with a maintained terminal emulator crate. After this lands, future terminal-rendering bugs become 'submit a patch upstream' instead of 'another fix-pty-X task.'

Crate options to evaluate

  • alacritty_terminal — used by Alacritty + several other Rust TUIs; full xterm-compatible state machine; widely battle-tested; most thorough but largest dep surface
  • vt100 — focused, smaller; parses VT100/xterm escape sequences into a grid; simpler integration; good enough for our chat-tab use case
  • wezterm-term — used by WezTerm; rich feature set; bigger dep surface
  • termwiz — also from WezTerm project; lower-level building blocks

Recommend: alacritty_terminal if we need full fidelity (e.g. to render codex's complex output), vt100 if we want minimum dep surface and our use case is mostly chat-tab text + occasional alt-screen.

Scope

  1. Identify the boundary in src/tui/ where bytes-from-PTY become rendered-cells. Replace the parser + grid + scrollback buffer with the chosen crate's equivalents.
  2. Keep the chat-tab outer chrome (tab bar, status line, etc.) — we're only swapping the terminal-emulation core.
  3. Migrate the existing scrollback persistence (chat history JSONL) — the new emulator's cell buffer should be hydrated from that JSONL on tab activation.
  4. The rendering path: emulator's cell grid → ratatui buffer → screen. Should be a clean adapter.
  5. Resize handling becomes the emulator's responsibility (which is exactly what these crates are tested for).

Validation

  • Failing tests written first (TDD): a series of recorded byte sequences (resize, codex output, claude output, ANSI color stress test) that the new renderer must handle without duplication or corruption
  • Live smoke: open the TUI, fill scrollback, resize many times in many directions, switch tabs, alt-screen apps (vim, less) — no corruption visible. Recording in task log.
  • Live smoke: codex chat tab renders identically to native codex CLI (same colors, same layout, no scrollback dup)
  • Live smoke: claude chat tab renders correctly (no regression from current state)
  • All previously-filed PTY tasks (fix-tui-pty, fix-pty-scrollback, the symptoms in fix-codex-agent) — verify their repros are gone after this refactor
  • cargo build + cargo test pass with no regressions
  • Permanent smoke scenarios for the resize / scrollback / alt-screen paths added to manifest with this task id in owners
  • cargo install --path . was run before claiming done

Process note

This task supersedes targeted PTY-bug fixes. After this lands, close any remaining PTY-rendering bugs as 'fixed by architectural reset' and verify their original repros are gone. If they're not, file follow-ups against the chosen crate (upstream bug or wrong adapter usage), NOT against our home-rolled code.

Depends on

Required by

Log