# Terminal Performance Initiative Working plan for the `orca-performance` branch. Goal: make Orca's terminal as performant as the architecture allows, with every claim backed by a number. Started 2026-07-02. ## Why (user-reported, from the team meeting) 1. Typing in the terminal is sometimes laggy — occasionally seconds of delay. 2. Users say the terminal is slower than iTerm (unclear if typing or scrolling). 3. Scrolling in Claude Code / OpenCode is slow. 4. Idle memory is high (1–2 GB). 5. Battery usage is high. Goals: legit performance complaints ≤ 1/week; sampled P90 typing/scrolling latency down significantly; lower memory with 0–1 agents. ## Ground truth (verified against source, 2026-07-02) Research corpus: xterm.js 6 / VS Code / Ghostty internals study (verified file:line claims) — see the archived digest and the "xterm.js vs Ghostty" deep-dive. The Orca-specific findings below were re-verified against this repo's code: - **Electron main sits on every terminal byte's path** (daemon → main → renderer). VS Code ships the same xterm.js but bypasses main entirely: its ptyHost is a UtilityProcess with a direct MessagePort to each renderer. - **The PTY producer is never paused.** `acknowledgeDataEvent` is a no-op in both `LocalPtyProvider` and `DaemonPtyAdapter`. Only main→renderer delivery is watermarked (512 KB, `src/main/ipc/pty.ts:1374`); main's own buffer can grow toward a 512 MB cap under flood. VS Code pauses the actual pty at 100k unacked chars (kernel backpressure blocks the shell). - Renderer terminals share one thread with the entire React app; xterm.js parses in 12 ms slices at a documented 5–35 MB/s ceiling. - Renderer scrollback default is 5,000 rows (`src/shared/terminal-scrollback-policy.ts`), 5× VS Code's default; 12 B/cell plus per-line JS objects; O(all lines) reflow on column resize. - Latency physics: Ghostty ~4 ms median keypress latency, VS Code ~31 ms (same-library reference), native class 5–10 ms. Realistic target: beat VS Code, close on iTerm2, eliminate the stall/jank class entirely (P99 dominates perception). ## Current state Branch `orca-performance` (long-lived testing line, from main @ `8e8a08ac7`): 1. `tools/benchmarks/terminal-pipeline-bench.mjs` — cross-terminal rig (see Benchmark protocol below). 2. Merge of PR #7153 = #7150 (freeze/memory: backlog caps, wedge guards, probe-certified replay release) + #7139 (cooperative drain: paced backlog draining keeps typing responsive under floods). Post-merge on this base: `pnpm typecheck` clean, 626 targeted tests green (scheduler, guards, pty/pty-connection/pty-transport suites). #7153 itself is a disposable testing PR; #7139 and #7150 land separately on main. ## Workstreams ### 1. Baseline benchmarks (now; human-in-terminal required) Run the rig in each terminal on the same machine — Orca pane, iTerm2, Ghostty, Terminal.app, VS Code (T3Code if available): ``` node tools/benchmarks/terminal-pipeline-bench.mjs --label - node tools/benchmarks/terminal-pipeline-bench.mjs report ``` These numbers answer "are we actually slower than iTerm, and where," and are the before/after for everything below. ### 2. Validate #7153 on orca-performance (this week, extended testing) Watch for: typing responsiveness under agent floods, bounded memory, skip-notice + snapshot repaint on overflow, no permanent input loss. When validated, land #7139 and #7150 as separate PRs on main. ### 3. Revive term-speed-2 (the headline structural work) History: nwparker's ~38-branch chain (+20k lines) implementing the terminal model/view contract — hidden view parking, hidden delivery gate, side-effect authority in main, model query authority, skip-grammar deletion — all kill-switched, documented in `origin/nwparker/term-speed-2-architecture-docs:docs/reference/terminal-model-view-contract.md`. It shipped only in v1.4.78-rc.1, a deliberate personal-testing build; it was never rejected and never reached main. Directly targets complaints 3–5 (hidden panes stop receiving bytes and unmount their xterm + WebGL atlases). Merge scout (2026-07-02, chain tip into orca-performance): 144 files, 34 conflicted, 115 hunks. Hotspots: `pty-connection.ts` (31), `pty.ts` (16), `daemon-pty-adapter.ts` (6), `orca-runtime.ts` (5). `pane-terminal-output-scheduler.ts` does NOT conflict — #7139/#7150 and the chain touch different layers; runtime interaction (drain pacing × hidden gate) still needs deliberate testing. Execution: dedicated focused session; resolve on `revive/term-speed-2` off orca-performance; keep both sides' kill switches; validate with typecheck + the contract tests listed in the model-view-contract doc + #7153's suites; merge back to orca-performance for extended testing. Estimated ~1 day of careful resolution + validation. ### 4. Remaining stall-bug fixes (parallel, independently shippable) The "seconds of delay" class = discrete thread-blocking events, not steady-state latency: - PR #7105 (open): skip synchronous cold-restore replay for live daemon sessions in doSpawn. - `SerializeAddon.serialize()` audit: ~1.2 s renderer block at 50k scrollback rows (#5096 follow-up, never done). Call sites include the mobile snapshot path (`pty-connection.ts:2861`) and sleep/hibernate serialization. - #2836 frozen-terminal leads: replay-guard latch, codex-stale gate, uncapped buffers (repro harness exists). - Checkpoint-RPC main-thread scrub (measured ~2–10 ms bursts per hot 5 s tick; small, part of the same program). ### 5. Producer-side PTY flow control Ack-driven pause/resume of the actual PTY through the daemon protocol (node-pty supports it), watermarks per the xterm.js flow-control guide (≤500 KB). Converts flood-induced buffered lag into shell blocking — the correct physics. Sequence after #7139 lands (interacts with its drain pacing). Design (2026-07-03, implement after the term-speed-2 revival merges — same files): - Signal source: main already tracks per-pty pending + in-flight (`pendingData`, `rendererInFlightCharsByPty` in `ipc/pty.ts`). When a pty's pending exceeds HIGH (256 KB), main asks the producer to pause; below LOW (32 KB), resume. - Producer side: two new protocol notifications (`pausePty`/`resumePty`, protocol vNext, version-gated like `supportsIncrementalCheckpoints`); daemon `Session` calls node-pty `pause()`/`resume()` — stops reading the pty fd, kernel buffer fills, the shell blocks on write: true kernel backpressure, identical physics to VS Code's 100k/5k design. `LocalPtyProvider` calls pause/resume directly. - Safety invariants: (1) failsafe auto-resume after 5 s regardless of watermark, so a lost resume can never wedge a shell; (2) resume on detach/exit/kill/daemon-reconnect; (3) pause must not suppress the interactive-echo bypass — with the pipeline fixed (11.5 MB/s dev), the HIGH watermark is only reachable during genuine floods where echo is already queued; (4) PTY reads never stop for model/tail ingestion (term-speed-2 invariant #1) — pause gates the fd read, so daemon-side emulator state pauses with it, which is correct (state = what was read). - Tests: watermark transition unit tests, lost-resume failsafe, kill/exit cleanup, plus an e2e pressure scenario asserting bounded main memory and a blocked producer (`yes` exits promptly on SIGINT while paused). ### 6. Extend the measurement rig - True keypress→pixel latency: Typometer manual protocol (the DSR probe stops at the parser reply, before paint). - Idle memory + battery: per-process RSS breakdown + `powermetrics` sampling at 0/1/5 agents (goal-3 metric). - FPS under flood; event-loop-delay probes (`monitorEventLoopDelay`) in main/daemon/renderer behind a debug flag for pipeline attribution. ### 7. utilityProcess terminal router (structural endgame; gated on data) An Electron UtilityProcess owns the daemon socket and hands each renderer a MessagePort — VS Code's topology while keeping Orca's detached daemon (warm reattach). Takes main off the terminal data path entirely; daemon-side history persistence falls out naturally. Prototype only after baselines show how much tail latency lives in the main hop. ### 8. Production P90 telemetry Sampled keypress→echo latency + long-task/stall counts from real users; defines the success criterion and becomes the permanent regression gate. Design after the local rig stabilizes so the metrics match. ## Benchmark protocol `tools/benchmarks/terminal-pipeline-bench.mjs` measures, from inside any terminal: - **DSR idle latency** — ESC[6n round trips (p50/p90/p99); replies come only after the parser reaches the query, so it proxies the input pipeline without keystroke injection. - **Fenced throughput** — 4 deterministic fixtures (`ascii-log`, `cjk-emoji`, `agent-tui` — Claude-Code-shaped transcript + DEC-2026 status repaints — and labeled-pathological `styles-stress`), each run ended by a DSR fence so xterm.js-class ingest queues can't flatter the result. - **DSR under load** — latency sampled during a paced 1 MB/s agent-TUI stream: "typing while the agent works," quantified. Rules: same machine, AC power, comparable window size, no tmux/screen, hands off the keyboard during runs. Never compare numbers across machines. ## Sequencing ``` now: [1] baselines [2] #7153 testing (parallel) next: [3] term-speed-2 revival (dedicated session) parallel: [4] stall fixes, [6] rig extensions after 2/3: [5] flow control gated: [7] utility router [8] telemetry ``` BMW-group crash work remains the team's priority gate above all of this (#7150's wedge guards overlap it); this plan runs measurement and revival prep in parallel without displacing it. ## Findings log ### 2026-07-02 — baseline + decomposition (results committed in tools/benchmarks/results/) Same machine, unattended serial runs (Orca 1.4.91 prod, Terminal.app, Ghostty 1.3.1; iTerm2 not installed, VS Code pending): | metric | Orca prod | Terminal.app | Ghostty | |---|---|---|---| | DSR idle p50/p99 (ms) | 0.69 / 22.7 | 0.35 / 0.68 | 0.19 / 0.72 | | DSR under 1 MB/s agent load p50/p99 (ms) | **134 / 292** | 0.45 / 7.9 | 0.21 / 6.1 | | agent-tui fenced throughput | **2.0 MB/s** | 37 | 78 | | ascii-log fenced throughput | 13 MB/s | 39 | 93 | Decomposition of the 51× agent-tui gap — both pipeline ends are fast: - Bare `@xterm/headless` (114×85, scrollback 5000): agent-tui **103 MB/s** (`terminal-headless-parse-bench.mjs`). The xterm parser is not the problem. - Daemon `Session` ingest (emulator + pending-output recording + fanout): agent-tui **103 MB/s** (`session-ingest-throughput.bench.test.ts`, `ORCA_TERMINAL_PERF_BENCH=1`). The daemon is not the problem. Conclusions: (1) idle latency is fine — the extra process hop costs ~0.5 ms, so the utilityProcess router is deprioritized by data; (2) the crisis is queueing between daemon egress and renderer parse completion — main per-chunk processing, the 512 KB delivery/ACK pacing (ACKs fire after renderer write callbacks, so renderer slowness throttles delivery multiplicatively), and renderer per-chunk layers above xterm; (3) the agent-TUI shape (DEC-2026 frames + erase/repaint) is 6.5× worse than plain text inside Orca while being equal-cost everywhere else — profile it in the renderer first (task #9). ### 2026-07-02 — dev-build check of #7139/#7150 (confounded; directional only) Dev build of orca-performance (282-col window, 3MB fixtures, dev-mode overhead): DSR idle p50 0.64 ms (unchanged), **DSR under load p50 161 ms** — the cooperative-drain branch does not move the under-load class. In hindsight this is structural: DSR replies are ordered within the output stream, so the metric measures output-queue depth; #7139 paces draining to protect input-send responsiveness but cannot reorder the queue. Implications: (1) the 134 ms-class number is fixed only by shrinking the queue (producer flow control) or raising drain rate (the 51× throughput hunt); (2) #7153's own wins (freeze class, bounded memory, input-loss guards) must be validated with freeze scenarios and real typing, not DSR. Also learned: dev-mode runs are ~2× slower across the board and fences need `--dsr-timeout-ms` headroom. ### 2026-07-02 — 51× loss attributed: scheduler fixed-nap drip (task #9) The renderer output scheduler (`pane-terminal-output-scheduler.ts`) drained at most 2×16KB per tick, then slept 4ms (high-priority) / 16ms (background) regardless of parse speed. Isolation bench (fake timers, instant-parse terminal — `pane-terminal-output-scheduler-throughput.bench.test.ts`, `ORCA_TERMINAL_PERF_BENCH=1`): **background cadence = 1.9 MB/s — matching prod's measured 2.0 MB/s agent-tui ceiling**; foreground = 27 MB/s (only when arrivals re-poke 0ms drains; Chromium's ~4ms timer clamp makes the sustained real-world HP ceiling ~8 MB/s). Classification: pty-connection's `isLatencySensitiveForegroundOutput` routes sizable no-recent-input chunks to the queue, so floods always ride the drip. Fix (committed 9e8bb2243): high-priority drains are now **parse-clocked** — a pacer re-arms a 0ms drain when xterm's write callback confirms the batch parsed — and carry 8 writes/tick (128KB ≈ 1.3ms parse). Isolation ceiling: 27 → **117.6 MB/s** (parse-limited). Background cadence deliberately unchanged (protects the focused pane; hidden panes are term-speed-2's job). `DRAIN_TIME_BUDGET_MS` still bounds tick work (cooperative-drain intent of #7139 preserved; its budget-yield test still passes). 621 tests green. Open follow-ups from this attribution: (a) end-to-end dev verification (in progress); (b) whether main's `background:true` delivery marking demotes visible-pane floods to the background drip — check `window.__terminalOutputSchedulerDebug` counters in a dev run; (c) ascii-log gap (13 vs 83 MB/s headless) — likely per-chunk `beforeWrite` side-effect scanning; profile after (a). ### 2026-07-03 — THE WHALE: main's retained-tail redraw path is O(tail) per chunk Parse-clock fix didn't move end-to-end (agent-tui still 0.7 MB/s dev). Layered probes (renderer scheduler counters → main whole-method timer → per-section timers → targeted micro-benches) attributed it fully: - Renderer receives only ~350–770 KB/s — it is **starved**, not slow. - `OrcaRuntime.onPtyData` consumes **~93% of main's event loop** during the flood (~950 ms/s at ~450 chunks/s ≈ 2.1 ms/chunk). - All wrapped sub-calls (OSC scanners, agent detect, watchers, headless track, leaves loop, mobile touch) together: **~3.5%**. The remainder is the pty-record tail block. - Micro-bench (`appendNormalizedToTailBuffer` with a real agent-TUI frame containing `ESC[10A ESC[0J`): **0.888 ms/chunk at a 2,000-line tail** — 32× the plain-append path. Cause: `appendNormalizedToMultilineTailBuffer` materializes ~2,001 row objects per chunk (orca-runtime.ts:22324) and `finalizeRetainedTerminalRows` allocates them all again plus runs a trailing-whitespace regex per row (:22458) — ~4k allocations + 2k regexes per tiny chunk, twice the tail length in O(n) passes. Every Claude-Code frame (cursor-up + erase-below) takes this path; plain logs don't — which is exactly the measured agent-tui vs ascii asymmetry. Chain: TUI flood → O(tail) work per chunk in main → main event loop saturates → daemon socket backpressures → renderer starved at ~0.4 MB/s → deep queue → 134 ms DSR-under-load. Fix (in progress): run the existing algorithm on a lazy suffix window (the cursor's maximum upward reach, computed from the chunk) with the untouched prefix shared by reference; differential fuzz test proves output equality against the original implementation. Worst case (pathological full-height cursor-up) falls back to today's cost. ### 2026-07-03 — windowed-tail fix: partial end-to-end win; next suspect queued Dev-build bench after the windowed redraw-tail fix (label dev-tailfix, same protocol as dev-parseclock): agent-tui **0.7 → 1.0 MB/s (+43%)**, DSR-under- load **p50 161 → 108 ms, p99 624 → 154 ms (4×)**. Real movement for the first time, but the pipeline is still far from the renderer's 27–117 MB/s capacity — another main-side consumer remains hot. Next cycle (exact recipe): re-apply the whole-method main probe (`onPtyDataMs` sampler in `pty.ts` bindProviderListeners) on the fixed build. If onPtyData still dominates, the remaining O(tail)/per-chunk suspects in priority order: (1) `buildTerminalWaitText` ×2 per chunk (full tail join, 0.116 ms/chunk in prod-node isolation — likely 2-4× that in dev); (2) `normalizeTerminalChunk` (regex over every chunk, never measured); (3) the per-leaf duplicate tail path when `tailStateMatches` fails. If onPtyData no longer dominates, probe the main→renderer delivery batching next. The probe/bench cycle is mechanical: relaunch dev (`ELECTRON_ENABLE_LOGGING=1 pnpm dev`), `orca-dev terminal create --command " --label X --size-mb 3 --dsr-timeout-ms 120000"`, grep the log. ### 2026-07-03 — post-fix attribution: `blockedCheck` is the remaining whale Post-windowed-tail probe run (dev build, agent-tui): `onPtyData` still ~90% of main's event loop (~930 ms/s). Bucket split per second: **blockedCheck ≈ 700–790 ms (~85%)**, waitText ≈ 70, append ≈ 25 (windowed fix confirmed), normalize ≈ 7, preview ≈ 0. Mechanism (orca-runtime.ts:23128 `nextTailHasNewerBlockedReason` + its callers): per chunk, TWO full wait texts are built (`buildTerminalWaitText` joins the whole ≤256KB tail), then the check calls `.toLowerCase()` on both (another ~512KB of string allocation per chunk) and runs multi-pattern blocked/ready scans (`findTerminalWaitBlockedSignal`, `findKnownReadyPromptIndex` — lastIndexOf/regex passes over the full text) — all to timestamp `waitBlockedAt` for `terminal wait`. Fix design (next session): blocked/ready prompts are end-anchored — an actionable prompt is at the END of output. (1) Run the check on a bounded suffix of the wait text (last ~64 lines / 16KB) instead of the full tail; (2) cheap pre-filter: skip entirely unless the appended chunk (plus a small carry for split keywords) can contain a blocked keyword; (3) build the two wait texts only when the check runs. Verification mirrors the windowed-tail pattern: keep the full-text check as reference + differential fuzz over randomized tails/prompts (split-across-chunks cases included — the `appendCandidateSignal` ordering semantics at :23146 must be preserved), plus the terminal-wait contract tests. Expected effect: removes ~85% of remaining onPtyData cost; combined with the two landed fixes should finally unlock the pipeline toward the renderer's measured 27–117 MB/s. ### 2026-07-03 — pipeline unlocked: three stacked fixes, 16× throughput, 9× latency Dev-build bench with all three fixes (parse-clocked drains 9e8bb2243, windowed tail 4e08a28cd, throttled blocked-check 66f20258e), label dev-blockedfix, same protocol/config as prior dev rows: | metric | pre-fix dev | +tail fix | +blocked fix | |---|---|---|---| | agent-tui MB/s | 0.7 | 1.0 | **11.5** | | DSR load p50/p99 (ms) | 161 / 624 | 108 / 154 | **18.8 / 24.9** | | DSR idle p50/p99 (ms) | 0.95 / 21 | 1.09 / 18 | **0.52 / 8.6** | | ascii-log MB/s | 6.4 | 4.7 | **9.6** | The agent-TUI-specific penalty is gone (agent-tui ≈ cjk ≈ ascii now). The throttled blocked-check delivered the predicted ~85% cut. Dev mode carries ~2× overhead vs prod, so the prod build should land near ~10ms DSR-under- load — from the 134ms baseline (~13×) — pending a packaged-build rerun. Remaining floor is structural cadence (8ms daemon batch + 4ms HP drain ticks + xterm 12ms slices), which flow control (#6) does not target; re-evaluate the "within 10× of Terminal.app" goal line after a prod measurement. Next: term-speed-2 revival (#4), then flow control (#6). ### 2026-07-03 — term-speed-2 revival: merged, green, NOT yet mergeable (perf gate) `revive/term-speed-2` pushed (merge a5052c35f, tip 64b6f7abe): 144 files, typecheck clean, ~2,776 targeted tests green, all three of our fixes verified present, chain features present and kill-switched (subagent's six review risks recorded in its report). Bench verdict on the revived build (dev): DSR-load p50 ~19ms holds, but **throughput regressed ~35% unconditionally** (agent-tui 11.5 → 7.2–7.4 MB/s; all-switches-OFF round proved the kill switches are NOT the cost) and idle p50 doubled. Attribution so far: main exonerated (whole-method probe: onPtyData ~60ms/s ≈ 6%); renderer reconcile + HP-first selection O(1)-checked; **daemon CONVICTED by unit bench — `Session` ingest 103 → 39.5/47.7 MB/s (2.2–2.6×) on the revive branch** (`session-ingest-throughput.bench.test.ts`, ORCA_TERMINAL_PERF_BENCH=1). Cause: the chain's headless-emulator restructure (scanner classes / query-reply forwarding / view-attribute responder) added per-byte cost to the daemon hot path. Chunks reaching main are now ~5.8KB vs ~650B (daemon emits slower, batches bigger). NEXT (fast inner loop — pure unit bench, no app restarts): on revive/term-speed-2, diff `headless-emulator.ts`/`session.ts` vs 7839fb9db, find the per-chunk scanner cost, restore our bounded-parser fast paths (the daemon emulator must never pay per-byte JS scanning for bytes that contain no ESC — same pre-filter pattern as the blocked-check keyword bypass), verify with the ingest bench back at ~100 MB/s, then full dev bench expecting blockedfix parity (~11.5 MB/s), THEN merge to orca-performance. A residual renderer-side share is possible once the daemon is fixed — re-attribute after. Merge gate: revive branch merges only at ≥ blockedfix numbers. **RETRACTION (2026-07-03, later):** the daemon conviction above was a confounded measurement — the 39–48 MB/s ingest runs executed while a dev app was still running. On a quiet machine the revive branch ingests at **82–109 MB/s** (≈ pre-merge) and its HeadlessEmulator alone does 99.5 MB/s vs raw xterm 77.7. The daemon is innocent. Consequently the end-to-end revival delta (11.5 → 7.2/7.4 dev) is also UNTRUSTED — none of those runs were load-controlled, and unit benches show up to 2.6× machine-load variance. Scanner pre-filters landed anyway on revive (71c89da9b; strictly positive, 641 daemon tests green). **New measurement protocol (mandatory from here):** quiet machine (no dev apps or benches concurrent), paired A/B runs back-to-back alternating branches, n≥2 per side, report spread not just p50. The merge-gate comparison (blockedfix vs revive) must be redone under this protocol before any verdict. Next: run the controlled A/B; if the delta disappears, merge revive into orca-performance and proceed to flow control (#6); if it persists, resume attribution renderer-side (probe pty-connection dataCallback additions per chunk). ### 2026-07-03 — A/B gate passed; term-speed-2 MERGED to orca-performance Load-controlled alternating A/B (fresh app per run, n=2/side, agent-tui + DSR-load): perf 6.7/5.2 MB/s, dsr p50 19.9/21.3, p99 107.8/218.1; revive 6.1/3.6 MB/s, dsr p50 21.4/20.3, **p99 63.4/26.1**. Verdict: latency p50 tied, p99 better on revive, throughput within overlapping noise (revive2's 3.6 followed two runtime-busy create failures). The earlier "35% regression" is confirmed noise. Note: both branches ~5-7 MB/s today vs 11.5 yesterday — dev benches carry ~2x day-to-day machine variance; absolute dev numbers are only comparable within one A/B session. Merged revive/term-speed-2 → orca-performance; typecheck clean, 288 post-merge spot tests green. orca-performance now = main-ish base + #7153 + three perf fixes + full term-speed-2 chain (kill-switched, default ON) + scanner pre-filters. Extended user testing now covers everything. Remaining from the revival agent's risk list: gate×drain e2e specs (terminal-hidden-*, parked-memory, sleep-wake) still not run — queue them. Next: producer flow control (#6) per design §5; prod packaged-build bench for the real headline numbers. ### 2026-07-03 — flow control merged; goal-state accounting Producer flow control merged to orca-performance (348aeb325): protocol v19 `pausePty`/`resumePty`, 256KB/32KB watermarks on main's pendingData, node-pty kernel backpressure, 5s daemon-side lost-resume failsafe + main-side pause re-assert, resume on every teardown path, version-gated (v≤18/SSH no-op), kill switch `PRODUCER_FLOW_CONTROL_ENABLED` (ipc/pty.ts:143), 29 new tests. Typecheck + 292 post-merge spot tests green. **Definition-of-done accounting:** - 51× loss: ATTRIBUTED AND FIXED (three fixes; agent-tui 0.7→11.5 MB/s and DSR-load p50 161→18.8 dev, results committed). - term-speed-2: REVIVED AND MERGED (A/B gate passed). - Flow control: IMPLEMENTED AND MERGED. - "Within 10× of Terminal.app (4.5ms)": RE-SCOPED to pending a packaged RC measurement. Evidence: dev = 18.8ms with ~2× dev overhead → prod projection ~9-10ms ≈ 20× Terminal.app (vs 300× at baseline). The remaining gap is structural cadence (daemon 8ms batch, renderer drain ticks, xterm 12ms parse slices) — tunable follow-ups, distinct from the waste class this initiative eliminated. Prod verification path: electron-vite preview CANNOT host the bench (CLI-created panes are not adopted by the preview window's renderer → no ACKs → pending-cap drop; two attempts, documented) — measure on the next packaged RC cut from orca-performance using the committed rig + protocol instead. **Deferred, ordered:** (1) sync orca-performance with main — conflicts incl. stream-opcode collision (chain `Ack=12` vs main's #7205-era `Metadata=12`; renumber chain side, audit mobile/web stream consumers); (2) chain's e2e specs (hidden parking / parked memory / sleep-wake) — gate×drain risk; (3) cadence tuning toward the 10× line; (4) rig extensions + P90 telemetry (tasks #3/#8). ### 2026-07-03 — PROD VERDICT: v1.4.121-rc.0 benchmarked (the headline numbers) Same rig, same protocol, same machine as the 1.4.91 baseline: | metric | 1.4.91 baseline | v1.4.121-rc.0 | change | |---|---|---|---| | DSR idle p50 | 0.69 ms | **0.44 ms** | = Terminal.app (0.45) | | DSR under load p50 | 134 ms | **18.6 ms** | 7.2x | | DSR under load p99 | 292 ms | **29.7 ms** | 9.8x | | agent-tui | 2.0 MB/s | **11.2 MB/s** | 5.6x | | styles-stress | 7.8 MB/s | **10.4 MB/s** | 1.3x | | ascii-log | 13 MB/s | 11.0 MB/s | ~0.85x | | cjk-emoji | 15 MB/s | 12.2 MB/s | ~0.81x | Reading: the anomalous TUI penalty is GONE — all four fixtures now sit at a uniform ~11-12 MB/s, which is the scheduler pacing ceiling, not parse CPU (prod ≈ dev for both latency and throughput; the pipeline is cadence-bound, so faster prod code changes nothing). That uniform cap also explains plain-text dipping slightly below baseline: ascii/cjk used to run unpaced ahead of the old scheduler; now everything flows through the same parse-clocked path. Goal line check: 18.6 ms = 41x Terminal.app under load (goal was 10x = 4.5 ms) — NOT met; down from 300x. Idle IS at parity. The remaining 4x is the named cadence stack (daemon 8 ms batch, scheduler drain ticks + 8x16KB per-tick budget, xterm 12 ms slices) — next lever, tunable, tracked as follow-up. p99 tail (the freeze class) is 29.7 ms — users cannot perceive it. Caveat: measured on the user's live app (this session active in it); idle p99 118 ms reflects that activity, not the terminal path. ### 2026-07-03 — Same-engine reference: VS Code head-to-head (same machine, same rig) | metric | Orca v1.4.121-rc.0 | VS Code | verdict | |---|---|---|---| | DSR idle p50 | **0.44 ms** | 7.00 ms | Orca 16x faster | | DSR load p50 | 18.6 ms | **7.18 ms** | VS Code 2.6x faster | | DSR load p99 | **29.7 ms** | 43.4 ms | Orca 1.5x better tail | | ascii-log | **11.0 MB/s** | 9.0 | Orca +22% | | cjk-emoji | 12.2 | 11.3 | tie | | agent-tui | 11.2 | 11.7 | tie | | styles-stress | **10.4 MB/s** | 2.0 | Orca 5.2x | Orca now beats or ties the best-known xterm.js terminal on 5 of 6 metrics — including 16x at idle (what users feel all day) and 5x on SGR-heavy output — and holds a better p99 tail under load. Throughput sits at the shared engine ceiling (~9-12 MB/s), confirming the class limit. The one loss (load p50) has a clean mechanism: VS Code's producer flow control caps unacked output at ~100KB, so its standing queue is ~100KB / 11.7 MB/s ≈ 8.5 ms — matching its 7.18. Our standing queue (18.6 ms ≈ ~200KB at 11 MB/s) is set by the main→renderer ACK window (512KB/pty high water) + drain re-arm cadence (Chromium clamps nested setTimeout to ~4ms). Two levers, both cheap to test: (1) MessageChannel drain scheduling (sub-ms re-arm; also raises the throughput ceiling); (2) tighter effective in-flight window on the renderer delivery path. Target: VS Code's ~7ms class or below without giving back throughput. ### 2026-07-03 — Batch windows were the gap: dev DSR-load p50 19 -> 8.0ms Lever results (dev, 3MB protocol, same session): - MessageChannel drains (2434dfaae): 19.01ms — NO change. Proved the ~19ms was NOT queue depth: at 1MB/s vs ~11MB/s capacity (9% util) there is no standing queue. Kept (correct, removes a real clamp). - Batch windows 8->2ms on BOTH hops (e67a91d7a: daemon STREAM_DATA_BATCH_INTERVAL_MS + main PTY_BATCH_INTERVAL_MS): **p50 8.00 / p90 10.13 / p99 12.26ms** (from 19.01/22.7/28.1). Throughput unchanged (agent-tui 9.8 vs 9.1, ambient noise). 239 batcher+pty tests green after timing updates. Dev-mode 8.0ms already matches VS Code prod (7.18); prod build should land BELOW VS Code. p99: ours 12.3 vs VS Code 43.4. The remaining fixed-latency terms are renderer/xterm-internal (12ms parse slices). Note: main's interactive bypass (input-gated) means real keystroke echo skips batching entirely — the DSR metric understates real typing responsiveness; VS Code measured on the same freight path, comparison fair. Next: cut RC, confirm in prod, re-baseline vs Terminal.app (expect ~8-15x from 300x at baseline; goal line 10x = 4.5ms now plausibly in reach). ### 2026-07-03 — Chain e2e debt PAID: all 6 hidden-pane specs green terminal-hidden-view-parking (parks + restores rich TUI on reveal; bell/ title side effects live while parked), terminal-sleep-wake-restore (output restored + input accepted after wake), terminal-parked-memory (renderer memory released on park; views retained when kill-switched off): 6/6 passed, electron-headless, 1.1m. The gate x drain interplay — the revival's top flagged risk — now has e2e coverage on the exact branch the RC ships from. Remaining garble-hardening: differential hide/reveal fuzz harness (next build), reveal-time seq diagnostics. ## Success criteria (baseline-relative; finalize after task 1) - DSR-under-load p90 in Orca within striking distance of iTerm2 on the same box; zero DSR timeouts (today's freeze class). - Fenced agent-tui throughput ≥ VS Code on the same box. - Idle RSS with 0–1 agents materially down (target set after the memory harness lands; hidden-pane parking is the main lever). - Zero >100 ms event-loop stalls in main/renderer during a 10 MB flood. - Production P90 typing latency down and monitored continuously.