Skip to content

fix: reduce interactive pane echo latency - #5149

Merged
JJLiebig merged 3 commits into
masterfrom
issue/4884-input-latency
Oct 11, 2026
Merged

JJLiebig merged 3 commits into
masterfrom
issue/4884-input-latency

Conversation

@JJLiebig

@JJLiebig JJLiebig commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Ordinary PowerShell redraws arrive as separate cursor/style, glyph, and prompt updates. The server's 16 ms cadence can delay each later part, and Windows timer overshoot amplifies that wait. Native Windows probes reproduced roughly 30 ms character and Ctrl+C prompt response on the unchanged base.

Give visible PTY output from a pane or popup receiving successfully forwarded keyboard/text input a 32 ms opportunity to render early. A server-wide budget permits at most 16 extra attempts per 16 ms; more keys cannot replenish that budget. This covers the observed split PSReadLine redraw, including escape-only updates before its glyph/prompt. Ordinary output retains the existing cadence. Hidden-only output, synchronized-output boundaries, retained rendering, and slow-client recovery keep their existing paths. Mouse events, releases, host scrolling, and paste do not arm early rendering.

The final revision also preserves priority for accepted keys when a later input event fails, and skips empty ANSI writes when both the cursor and its display policy are unchanged. Content revisions and the global budget are retained.

Current head: c9a0014e53c32d31b4356d9d57dac75e00e0016f. Refs #4884.

Validation

  • Initial workstation measurements: candidate 3eca9a688977734176c4410b65c1e01272820d98, base 2563803dca97c040beaf3dc3acdcb5a3221b4238.
  • Windows just check passed: formatting, Clippy, 3,402 Rust tests passed / 12 skipped, Python maintenance checks, UI architecture checks, integration assets, docs contracts, and debug build. Pre-commit just lint passed.
  • The owning server regression forwards input to its runtime channel and exercises retained patches across separate escape/clear/glyph/new-prompt updates, budget exhaustion without key replenishment, refill, expiry, and unrelated/hidden sources. Existing synchronized-output and backpressure regressions passed in just check.
  • Both release binaries used just build, identical locked dependencies, Rust 1.96.1, Zig 0.16.0, and the verified app-local ConPTY bundle. Diagnostic tracing was disabled for final measurements. A reboot interrupted validation; rebuilding its corrupt generated PDB recovered the linker, and the complete final run passed.

Initial workstation render benchmark

Ran canonical just bench-render-scale twice per variant, sequentially in the same isolated worktree, at 120x40 geometry with populated histories and 1/15/50 panes. Every command passed all six selected profiles. Values below are combined server surface + client composition median microseconds, with both passes shown.

Scenario Panes Master (us), runs 1 / 2 Original PR (us), runs 1 / 2
Background workspaces, one pane each 1 655 / 873 555 / 625
Background workspaces, one pane each 15 882 / 631 569 / 594
Background workspaces, one pane each 50 654 / 586 589 / 586
Active panes, one workspace 1 862 / 875 573 / 560
Active panes, one workspace 15 918 / 874 648 / 644
Active panes, one workspace 50 1045 / 857 741 / 725

These profiles measure render cost/scaling, not input latency. Before samples were captured before the workstation reboot, after samples afterward; sampled total-machine CPU was 23-35% before and 9-21% after. Lower after times are not evidence of a causal render-speed improvement. Active 15-pane/1-pane median ratios after were 1.13/1.15; background ratios were 1.03/0.95. No added per-pane renderer work is introduced.

Initial workstation response comparison

An isolated outer ConPTY at 120x30 injects Z, Ctrl+C, and Enter into PowerShell 7 (-NoLogo -NoProfile). A blocking reader timestamps the first bytes containing the target glyph or completed PS4884> prompt. Four fresh sessions per variant with eight samples each = 32 samples per action, using before/after/after/before order twice. Valid configuration, no diagnostic tracing, and identical terminal capabilities were used on both sides. P95 uses nearest rank.

Response Master median / P95 (ms) Original PR median / P95 (ms)
Character echo 30.91 / 34.05 15.11 / 21.74
First Ctrl+C ^C feedback 30.08 / 39.46 15.36 / 20.85
Completed Ctrl+C prompt 30.08 / 39.46 20.79 / 35.61
Enter to completed prompt 26.41 / 32.92 15.09 / 22.51

After 155 Enter events at 31 Hz, sentinel latency was 15.64-33.27 ms before and 13.79-19.42 ms after across four sessions. This measures output bytes, not displayed pixels. The completed Ctrl+C prompt still has tail latency; the bounded allowance is not a deadline guarantee for arbitrary packet counts or shell execution time.

Initial workstation sustained-output cost

One visible pane repeatedly writes a carriage-return counter. Each phase runs for 60 seconds: continuous output alone, then the same output with 1,800 x keys (30 Hz). CPU sums Herdr server + client process time as percent of one logical core, excluding PowerShell and ConPTY. Frame counts are completed synchronized-output markers at the outer pipe, not all internal render attempts.

Scenario Master CPU Original PR CPU Frames: master / original PR Bytes: master / original PR
Continuous output 20.08% 19.74% 2,088 / 2,239 57,992 / 62,147
Continuous output + 30 Hz keys 19.35% 28.57% 2,165 / 13,983 60,435 / 263,912

The trade-off is +9.22 percentage points of one core while continuously typing into busy output, with more delivered updates; output without typing remains near baseline. The global budget bounds extra attempts even with repeated keys or multiple input panes. This load probe used one pane/client; live multi-client, 15-pane busy-output scheduling, and displayed-pixel latency were not measured. Multi-pane render scaling and existing hidden-output/backpressure/synchronized-output tests passed, but do not substitute for those live workloads.

Full benchmark/check logs, sample/chunk JSON, probe drivers, and binary hashes are retained locally under %TEMP%\herdr-4884-validation. Before binary SHA-256: 8c95f746a37ad7de74042d4f012f826ade889efe62731a57958b92f5e2011fe2; after: dd193151e71f621f4234b3894bb1e573bf7bc36bd143485d420485673a02dea2.

Laptop follow-up at c9a0014e53c32d31b4356d9d57dac75e00e0016f

The final revision keeps priority for earlier accepted keyboard/text events when a later event in the same batch fails (for example, when the runtime input queue fills). Both pane and popup callers preserve that success flag and still log the error. The existing server regression fills three of four queue slots, submits A then B, and verifies A is accepted, B is rejected, and early rendering remains armed.

The ANSI encoder now remembers the committed cursor suppression policy. An empty-row patch with unchanged cursor state and unchanged policy emits no bytes, including when the native cursor is visible. The existing regression covers unchanged visible cursors and both native/client-drawn cursor transitions. Content revisions still propagate for selection, copy/search, and links; the global render budget is unchanged.

All follow-up validation ran on JJLAPTOP; no workstation tests, builds, or benchmarks were run. The exact four-file diff was recovered from the workstation, then checked and measured on the laptop. origin/master remained 2563803dca97c040beaf3dc3acdcb5a3221b4238 before publication. Laptop just check passed: 3,402 Rust tests passed / 12 skipped, formatting, Clippy, maintenance and architecture checks, integration assets, docs contracts, and debug build. All six profiles passed in each of four just bench-render-scale runs (twice per variant). Both release variants were freshly built on the laptop using Rust 1.96.1, Zig 0.16.0, locked dependencies, and the app-local ConPTY bundle. The published candidate 3eca9a6 is the baseline for the tables below; the earlier workstation tables above describe that candidate against master, not the final follow-up.

Laptop latency: published PR candidate versus final revision

Same outer-ConPTY protocol and 120x30 geometry as above. Original/optimized/optimized/original order twice, four fresh sessions per variant, eight samples per session, 32 per action. Values are pooled median / nearest-rank P95 in milliseconds.

Response Original PR median / P95 (ms) Final median / P95 (ms)
Character echo 15.64 / 16.03 15.57 / 16.38
First Ctrl+C feedback 15.36 / 17.15 15.36 / 16.48
Completed Ctrl+C prompt 15.45 / 31.00 15.36 / 16.66
Enter to completed prompt 15.32 / 20.18 15.66 / 17.87

Character and completed Ctrl+C medians remained near 15 ms. Enter median increased 0.34 ms (2.2%). Held-Enter sentinel ranges were 14.74-16.52 ms versus 15.18-20.21 ms, including one slower final-revision sample. This is a small sample, not a guarantee about tail latency or displayed pixels.

Laptop sustained output: two 60-second runs per variant

Run order was original/optimized/optimized/original. Each fresh session measured output alone, then output with 1,800 keys at 30 Hz. CPU includes Herdr server + client and is percent of one logical core. Ranges show the two runs; medians are the midpoint of those two observations.

Scenario Original PR CPU median (range) Final CPU median (range) Relative change Percentage-point change
Output only 18.64% (17.68-19.61) 18.88% (18.80-18.96) +1.3% +0.24
Output + 30 Hz keys 32.71% (29.99-35.43) 29.05% (28.46-29.63) -11.2% -3.67
Scenario Published PR frames, runs 1 / 2 Final frames, runs 1 / 2 Published PR bytes, runs 1 / 2 Final bytes, runs 1 / 2
Output only 2,017 / 2,024 2,020 / 2,021 55,839 / 56,033 56,489 / 56,713
Output + 30 Hz keys 13,530 / 15,105 13,472 / 13,844 257,045 / 278,463 255,827 / 261,948

The busy-typing CPU median was lower in this comparison, while output-only CPU remained near baseline. Different frame counts and run-to-run variance mean this does not establish a general 11.2% CPU reduction, nor isolate all of it to skipped writes. The deterministic regression proves redundant writes are removed; broader scheduler changes are outside this fix.

Laptop render scaling

Combined server surface + client composition median microseconds, fixed 120x40 geometry and populated histories. Each cell shows both passes.

Scenario Panes Original PR (us), runs 1 / 2 Final (us), runs 1 / 2
Background workspaces 1 749 / 802 857 / 752
Background workspaces 15 863 / 902 857 / 771
Background workspaces 50 843 / 928 791 / 791
Active panes 1 726 / 751 755 / 758
Active panes 15 871 / 844 848 / 850
Active panes 50 1,010 / 963 1,017 / 975

These render profiles are supporting scaling evidence, not a measurement of the ANSI write optimization. Active-pane medians changed by +2.4%, -1.0%, and +1.0% at 1/15/50 panes; background medians changed by +3.7%, -7.8%, and -10.7%. No causal render-speed claim is made.

Laptop evidence, raw samples, scripts, complete checks/benchmark logs, and the preserved working diff are under %TEMP%\herdr-4884-validation\laptop-*. Laptop original release SHA-256: 51950f432d295ada26be6c3b2add14d576a188fc6a4eba1b23a7515be9626404; final release: 01d97e83cd556fa0711ea4fb3998e7762aac6bcbcc1b73ffece81034c898f561.

Live multi-client and multi-pane busy-output scheduling, and displayed-pixel latency, remain unmeasured. The main Herdr session was untouched; disposable probe sessions were stopped and deleted.

@kangal-bot

Copy link
Copy Markdown
Collaborator

@coderabbitai review
@greptileai

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c4491c28-5975-444f-b725-dc8b58337c68





📥 Commits

Reviewing files that changed from the base of the PR and between 3eca9a6 and c9a0014.






📒 Files selected for processing (4)
  • src/protocol/render_ansi.rs
  • src/server/headless.rs
  • src/server/headless/tests/mod.rs
  • src/server/pane_input.rs





Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.







📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

Pane and popup input handlers now report whether they forwarded keyboard input. The headless server tracks per-pane interactive input deadlines and limits interactive renders with a render budget. The render loop can render eligible interactive work when normal cadence and presentation work are not due. ANSI blit encoding also stores and compares cursor-suppression policy when encoding patches. Tests cover interactive render behavior and cursor-policy changes.

Suggested reviewers: ogulcancelik












Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to c9a00

No actionable merge-blocking issue was identified. The change is mergeable after normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 39.13% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check Passed The description directly explains the interactive rendering changes, input conditions, validation, performance trade-offs, and known measurement limits.
Linked Issues check Passed The description references issue #4884, which provides a linked issue for the stated latency fix.
Out of Scope Changes check Passed The changes support the stated latency objective. Accepted-input priority and unchanged cursor-write suppression are also documented as implementation goals.
Title check Passed The title clearly and concisely identifies the main change: reducing interactive pane echo latency.

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@JJLiebig JJLiebig added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Oct 11, 2026
@JJLiebig

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@greptile-apps

greptile-apps Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues remain.

Summary

The PR lets visible pane and popup output render early after accepted keyboard or text input.

  • Visible shell output can render sooner after you type.
  • Unchanged cursor patches no longer send empty ANSI writes.

No new actionable issues were found. The unnumbered previous thread is fixed: both callers check keyboard_forwarded before logging a later error.

The previous-review commits have different merge bases with the target, so this review used the full PR diff. No runtime tests or benchmarks were run during this review.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Accepted keyboard or text input] --> B[Arm pane for 32 ms]
  B --> C[Visible PTY output arrives]
  C --> D{Ordinary render due?}
  D -->|Yes| F[Existing render paths]
  D -->|No| E{Window active and budget available?}
  E -->|Yes| G[Count extra attempt]
  G --> F
  E -->|No| H[Wait for ordinary cadence]
  H --> F
Loading

Reviews (3) · Last reviewed commit: "Merge branch 'master' into issue/4884-in..." · Reviewed by Greptile

Comment thread src/server/pane_input.rs Resolved
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@JJLiebig

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@coderabbitai review

Please review the latest head c9a0014e53c32d31b4356d9d57dac75e00e0016f.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@JJLiebig

Copy link
Copy Markdown
Collaborator Author

@greptileai full review
@coderabbitai

@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@JJLiebig, do you also want CodeRabbit to run a full review of this PR?

You are interacting with an AI system.

@JJLiebig
JJLiebig merged commit 75f46fe into master Oct 11, 2026
7 checks passed
@JJLiebig
JJLiebig deleted the issue/4884-input-latency branch October 11, 2026 12:49
aryrabelo added a commit to aryrabelo/bora-herdr-ada that referenced this pull request Oct 12, 2026
Upstream commits brought in:
- fix: detect codex startup update chooser (herdrdev#5174)
- fix: confirm closing a workspace's last pane in the tui (herdrdev#5173)
- fix: keep sgr pixel mouse units consistent (herdrdev#4971)
- fix: reject workspace numbers past the last workspace (herdrdev#5171)
- fix: reject oversized canonical input on macos (herdrdev#4988)
- fix: save sessions before agents die during macos restart (herdrdev#5092)
- fix: preserve hermes plugin list structure (herdrdev#4984)
- fix: restore keyboard protocol flags after a terminal reconnect (herdrdev#5088)
- fix: reduce interactive pane echo latency (herdrdev#5149)
- fix: flush session saves before replacing persisted files (herdrdev#5152)

Conflict resolutions:
- client shell close-pane confirmation: take upstream's request_pane_close,
  keep the fork's SetPaneStatus menu action and closes_group confirmation
  field; a group-root close still confirms client-side and closes the whole
  group (the fork has no server confirmation_required path, 2d839ce), so
  upstream's close_pane group test is adapted like close_tab.rs.
- pane input: keep the fork's backpressure-preserving send paths and add
  upstream's keyboard_forwarded tracking; keep send_bytes_after and
  write_user_input next to upstream's validate_input_submission.
- pane mouse: take upstream's merged encoder/position helper.
- satisfy the fork's redundant_clone deny lint in the new macos input tests.

Sets UPSTREAM_HERDR_COMMIT to 6d697e1 and bumps bora to 0.52.3.
maciejul pushed a commit to maciejul/herdr-supercharged that referenced this pull request Oct 28, 2026
* fix: reduce interactive pane echo latency

refs herdrdev#4884

* fix: preserve accepted input priority and skip unchanged cursor writes

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

Labels

ai-review Trigger automated AI reviews for pull requests admitted by the PR gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants