fix(terminal): certify replay wedges only after a quiet window and re-kick recovery on reveal#8691
Conversation
…-kick recovery on reveal A slow-but-alive xterm parse (hidden-restore backlogs) could be certified "wedged" by the replay guard's flat probe deadline, opening the guard while replay bytes were still parsing (auto-replies leak into the agent's stdin) and handing healthy panes to recovery. The wedge deadline now extends while parse progress is observed and certifies only after a fully quiet window. Two self-heal holes in the same family: a restore requested on a certified-dead pipeline silently returned false — if certification's own recovery request was budget-declined, a revealed dead pane kept its stale pre-death frame forever; it now re-kicks pane recovery once per xterm instance. And remountTerminalTabForRecovery returning false was the one unbreadcrumbed recovery outcome; it now leaves a trace.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughTerminal write health now records parse progress and distinguishes successful parsing, cancellation, and write failure. Output scheduling propagates write failures while preserving acknowledgement handling and preventing discarded output from recording progress. Replay stall handling uses quiet-window checks before releasing a replay as wedged. Pane recovery adds a 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…-reveal-self-heal
Summary
A hidden Claude Code split pane on 1.4.138-rc.7 returned with a fossilized mid-Ink frame: transcript intact, but the input box, task list, and status line were erased. A split resize repaired it by delivering SIGWINCH and forcing the TUI to repaint.
Production breadcrumbs showed 1,483
terminal_replay_guard_wedged_releaseevents and zero recoveries, including tight bursts on the affected pane. The pane later parsed successfully, so those wedge verdicts were false positives against a slow-but-alive xterm parser.This PR makes replay wedge certification progress-aware, separates rejected writes from parse progress, stops output work once an xterm is certified dead, and makes pane recovery self-heal across reveal and cooldown races without restarting the live PTY.
Root cause
engageReplayGuardreleasedwedgedafter a flat2 × stallCheckMs, even when sibling write completions proved the parser was still draining a hidden-restore backlog. That opened the replay guard while replay bytes could still emit xterm auto-replies, leaked CPR/DA/ESC fragments into the agent's stdin, permanently certified a healthy xterm instance dead, and handed it to pane recovery.Review then found seven adjacent correctness holes before merge:
Date.now()progress tracking missed a completion in the same millisecond;discardTerminalOutput()reused the stall-watch settle function, so cleanup could impersonate parse progress;terminal.write()rejection invoked completion-shaped callbacks, recording progress that never occurred and leaving detached scheduler tails retryable;The final implementation uses monotonic generations for both parse progress and per-tab recovery ownership, keeps successful parse settlement distinct from cancellation/failure, and treats certified-dead xterms as terminal output sinks until remount.
Reliability contract
terminal-output.replay-guard-safety): replay-generated terminal replies remain suppressed until the replay is known parsed or the parser has produced no real completion for a full certification window; cleanup and rejected writes are never evidence of parser liveness; certified-dead xterms receive no further queued/new output; recovery remains bounded and only the current xterm generation may retry/remount.terminal-output.scrollback-restore(experimental/partial) andterminal-performance.output-backpressure-budget(experimental/partial). The registered restore command is the focusedpty-connection.test.tssuite; scheduler/health/replay suites provide the narrower output-pipeline oracle.terminal_replay_guard_wedged_release,terminal_replay_guard_lost_completion, andterminal_pane_recovery_remountbreadcrumbs remain;terminal_pane_recovery_remount_unavailablecovers the previously silent missing-tab outcome.Changes
Generation-based quiet-window certification (
replay-guard.ts,terminal-write-pipeline-health.ts)Rejected writes are not parse progress (
pane-terminal-output-scheduler.ts,pane-terminal-foreground-render-settle.ts)onParsedor advances the progress generation.Certified-dead output suppression and cleanup (
pty-connection.ts,pane-terminal-output-scheduler.ts)nullinstead of publishing an old xterm buffer.Generation-owned cooldown recovery (
terminal-pane-recovery.ts,pty-connection.ts)Reveal re-kick and diagnostics (
pty-connection.ts,terminal-pane-recovery.ts)restore-blockedonce per xterm instance.remountTerminalTabForRecoveryrecordsterminal_pane_recovery_remount_unavailablewithout consuming recovery budget.Provider and platform coverage
Verification
Final pushed head
467d01acc:584focused tests across PTY connection, pane recovery, replay guard, scheduler, pipeline health, serializer registry, lifecycle, and write-callback guard: passed.null;terminal-hidden-tui-visual-restore.spec.ts+terminal-wedged-write-pipeline-recovery.spec.ts: 6/6 passed.pnpm run typecheck: passed.pnpm run build:electron-vite: passed.pnpm run lint, switch exhaustiveness, reliability-gate manifest, max-lines ratchet, and localization checks: passed.git diff --check: passed.Performance budget
Residual gaps
Not in this PR
schedulePaneRevealRepaintremains WebGL-only).Refs #8630, #8673, and the #8104 symptom family.