orca/docs/terminal-main-owned-state.md

89 lines
4.1 KiB
Markdown

# Terminal Main-Owned State
## Problem
Hidden and background terminal panes cannot rely on renderer memory as the only
place that terminal output exists. Chromium may throttle a hidden Electron
document while the PTY continues producing bytes. If the renderer retains every
hidden byte until xterm can parse it, a noisy terminal can pin large strings in
renderer memory and stall or crash the app.
The renderer must keep a hard memory bound, but the user-visible terminal state
should still be recoverable when the pane becomes visible again.
## Reference Pattern
Use a host-owned terminal model as the recovery source and treat the renderer as
a view:
- The host process receives PTY bytes first and appends them to a bounded
headless terminal model.
- The renderer writes visible output directly for low latency.
- Hidden renderer queues are bounded. When they overflow, the renderer marks its
xterm as stale and drops further hidden bytes instead of retaining them.
- On visibility resume, the renderer asks the host for a serialized snapshot,
clears its xterm, replays that snapshot, then resumes live writes.
- Live output racing with restore carries a monotonic sequence number, so bytes
already included in the snapshot are not written twice.
This gives the foreground path the same latency profile as today, bounds hidden
renderer memory, and preserves terminal state from the host-owned model instead
of depending on an unbounded renderer backlog.
## Requirements
- Renderer hidden-output memory is capped per terminal.
- A hidden flood must not grow the renderer by retaining strings or chunk arrays
past the cap.
- Restoring a stale renderer must use main/runtime state for local, daemon, and
SSH PTYs.
- Remote runtime PTYs that do not have local main-owned state must keep the
existing warning fallback rather than pretending recovery is available.
- Restore must avoid xterm query auto-replies reaching the shell.
- Clear, resize, exit, and pane disposal must clean up recovery state.
- The SSH path must participate in the same sequencing and snapshot behavior as
local PTYs.
## Chosen Design
The existing runtime headless terminal is the main-owned model. Every PTY byte
already reaches `OrcaRuntimeService.onPtyData` before renderer delivery for
local, daemon, and SSH PTYs. That path keeps a headless xterm emulator updated
and can serialize it.
The renderer scheduler keeps its 2 MB background cap. When the cap is exceeded:
1. The scheduler replaces the queued backlog with a small warning fallback.
2. The terminal connection marks that pane as needing main-state recovery.
3. Further hidden bytes for that stale pane are not enqueued in the renderer.
4. When the pane/document becomes visible, the connection requests a main-owned
snapshot, clears xterm, replays the snapshot under the replay guard, and
sends the normal post-reattach reset.
5. Live foreground chunks that arrive while restore is in flight are retained in
a small bounded queue. After snapshot replay, sequence numbers decide which
chunks were already included and which still need to be written.
If the main snapshot is unavailable, the small warning fallback remains the
visible behavior. That is the expected fallback for surfaces without local
main-owned terminal state.
## Non-Goals
- This does not add a durable full byte log. The host-owned model is bounded
terminal state, not an infinite transcript.
- This does not throttle the PTY producer. Producer backpressure can be added
later with ACKs if we need to reduce host-side work during extreme floods.
- This does not change foreground terminal write latency.
## Verification
- Unit-test scheduler overflow callback behavior and chunk-count bounding.
- Unit-test renderer recovery so hidden overflow drops renderer backlog, fetches
the main snapshot on visibility/foreground resume, and does not duplicate
sequenced live output.
- Unit-test main IPC snapshot sequencing.
- Run terminal scheduler and PTY connection tests.
- Run typecheck.
- Exercise the Electron hidden-flood repro and confirm renderer memory stays
bounded while the recovered terminal shows the host-owned terminal state.