Skip to content

fix(terminal): supersede stale synchronized TUI frames - #2442

Draft
zopenb wants to merge 1 commit into
binaricat:mainfrom
zopenb:fix/coalesce-pending-sync-frames
Draft

fix(terminal): supersede stale synchronized TUI frames#2442
zopenb wants to merge 1 commit into
binaricat:mainfrom
zopenb:fix/coalesce-pending-sync-frames

Conversation

@zopenb

@zopenb zopenb commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Prevent the renderer from spending seconds replaying obsolete full-screen TUI snapshots when xterm parsing falls behind the remote stream.

OpenCode emits complete DEC 2026 synchronized snapshots. On a 266x68 terminal, one observed frame was 53,529 bytes and updated roughly 18,000 cells. Netcatty previously queued every snapshot serially, so a newer input response could remain behind frames that no longer had visible value.

Type of Change

  • Bug fix
  • New feature
  • Refactor / code cleanup
  • Documentation update
  • Build / CI change
  • Other (please describe):

Related Issue (optional)

Related to #2272

Changes Made

  • Conservatively identify complete replaceable DEC 2026 full-screen snapshots:
    • the synchronized block must be closed;
    • it must return the cursor to 1;1 (or implicit home);
    • only terminal query CSI sequences, such as OpenCode's CSI 14 t, may precede the block;
    • incremental blocks, printable prefixes, and incomplete frames remain non-replaceable.
  • Add replacePendingKey to the terminal write queue.
  • Remove only older matching writes that have not started; the active xterm write and ordinary terminal output remain ordered and untouched.
  • Preserve renderer flow-control accounting by reporting the replaced writes' original ingress bytes through the existing dropped-byte ACK handler.
  • Handle replaceable writes even after pending output has been merged into a flood item.
  • Add regression coverage for classification, ordinary pending queues, merged flood queues, and ingress-byte accounting.

Screenshots / Demo

Manual reproduction environment:

  • Netcatty v1.1.71 on Windows
  • SSH to Linux, running OpenCode 1.18.4
  • terminal size: 266 columns x 68 rows
  • observed full snapshot: 53,529 bytes, correctly closed with CSI ? 2026 l

Before the patch, the UI repeatedly appeared frozen while the remote PTY and SSH socket queues were empty. A forced PTY resize made OpenCode immediately emit complete frames, confirming that the remote TUI was responsive. A longer soak subsequently reproduced the freeze. The new capture shows a separate path: multiple OpenCode sessions emit small, closed DEC 2026 incremental animation blocks every 40-80 ms without cursor-home full-frame markers. This patch intentionally does not replace those incremental blocks, so it is not a complete fix for the reported freeze. The PR remains a draft while that path is investigated.

Testing

  • Production build passes (npm run build)
  • Changed files pass ESLint
  • Focused tests pass:
    • DEC 2026 full-frame classification
    • replacement and ingress accounting
    • replacement inside merged flood items
  • Full repository test suite (npm test)
  • Generated capability tool specs are updated when applicable (not applicable)
  • No new console errors or warnings observed in the portable build

npx tsc --noEmit currently reports numerous pre-existing errors on upstream/main, primarily in SFTP, AI bridge, and test-fixture types. The changed files pass their scoped ESLint and runtime tests.

Checklist

  • My code follows the existing project style
  • I have added regression tests for the changed behavior
  • I have not introduced a breaking API change

@zopenb

zopenb commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Longer-soak update: the freeze reproduced again. The new live capture differs from the original 53.5 KB full snapshots: two OpenCode 1.18.4 sessions were each emitting small, closed DEC 2026 incremental animation blocks at roughly 10-20 FPS (spinner intervals in current OpenCode source are 40/80 ms). The remote processes averaged about 109% and 132% CPU, PTY queues remained empty, and the Netcatty renderer saturated a core. These blocks do not contain cursor-home full-frame markers, so this PR correctly leaves them untouched and therefore does not solve that recurrence. I have updated the PR description and am keeping it in Draft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants