Spec-driven terminal core prototype for a high-throughput agent terminal.
Baton's core rule is simple: keep terminal throughput out of the webview hot path. Rust owns PTY I/O, byte coalescing, backpressure, VT parsing, terminal grid state, and fixed-memory scrollback contracts. The Tauri/webview layer should provide chrome and low-frequency control APIs.
- bounded PTY byte ring with explicit backpressure (
ByteRing) - live PTY reader coalescing with configurable frame/capacity policy (
PtyReadConfig) - local PTY spawn/read/write/resize/wait wrapper (
LocalPty) - Alacritty-backed VT parser boundary and visible grid snapshot (
TerminalParser) - multi-session routing model (
SessionId,TerminalSession,SessionManager) - fixed-visible-line scrollback contract with pluggable spill sink (
ScrollbackSpill) - throughput benchmark harness for coalesced PTY output (
measure_pty_throughput)
- Tauri v2 desktop app scaffold (
src-tauri/) - Vite + TypeScript frontend shell (
index.html,src/main.ts,src/styles.css) - one main window titled
baton - minimal chrome bar, session rail, active-session status, new/close controls
- xterm.js terminal renderer with WebGL addon and fallback warning
- Tauri command API for session create/write/resize/kill/list/read control plane
- coalesced PTY output delivered to frontend via
terminal-outputevents - no IDE side features; terminal hot-path remains in Rust core
PTY process
│ bytes
▼
LocalPty reader thread
│ chunks
▼
ByteRing + Coalescer
│ frame-cadenced byte batches
▼
TerminalParser
│ alacritty_terminal Term/Grid snapshot
▼
Renderer boundary
Hot path:
- PTY read/write
- byte coalescing and backpressure
- VT parsing and grid mutation
- renderer snapshot/damage boundary
- fixed visible scrollback with spill storage
Control plane:
- create/kill/focus terminal sessions
- resize terminal sessions
- scroll/selection requests
- tab/chrome state
- orchestration metadata
- settings and persistence commands
See docs/architecture.md for the detailed boundary contract.
Run the full local gate before every PR:
npm install
npm test
npm run build
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
cargo test --all -- --nocapture
cargo check
cargo check --manifest-path src-tauri/Cargo.toml
cargo test --manifest-path src-tauri/Cargo.toml -- --nocapture
npm run tauri -- build --debugFor interactive shell verification:
npm run tauri -- devRun the PTY throughput smoke when touching PTY/coalescing/parser paths. The first argument selects a workload (cat, waterfall, ansi, unicode, scroll), and the second selects target bytes:
cargo run --example throughput_bench -- cat 1048576
cargo run --example throughput_bench -- waterfall 1048576Example smoke output shape:
workload=<name>
bytes_read=1048576
frames_read=<n>
max_frame_bytes=<n>
elapsed_ms=<n>
mib_per_second=<n>
This smoke is a regression signal, not a final performance claim. Larger benchmark targets should be compared only after the slice-1 Tauri/xterm.js path exists.
See CONTRIBUTING.md for the repository workflow:
- feature branches target
develop - green PRs are squash-merged and branches are deleted
- release/stabilization PRs promote
developto protectedmain - protected
mainrequires PR-based changes and theRust coreCI check
- Slice 1: build the Tauri v2 shell, command API, and xterm.js WebGL renderer as the escape-hatch renderer. This gets a working terminal and measurable baseline quickly.
- Measure: use vtebench-style workloads, waterfall output, idle CPU, input latency, and the throughput harness.
- Slice 2 only if needed: native GPU renderer and child-surface composition. Do not build this until slice-1 measurements miss the target. See
docs/native-gpu-spike.md.
- Record slice-1 renderer/frame/idle measurements against the xterm.js WebGL baseline.
- Tighten terminal lifecycle polish only where measurements or manual usage expose gaps.