Repository navigation
fix(client): scroll ticks wait for the app's answer, and a slow app gets at most 12 a go - #52
Merged
Merged
Conversation
…ets at most 12 a go Ticks sent to an app go out once its output has answered the last ones (leaving no synchronized update open), the ticks due meanwhile together, so an app that redraws slowly (Claude Code fullscreen inside herdr) no longer falls behind and jumps. While the app's answers average over 40 ms, a go sends at most 12 ticks and drops the rest past one more go, so a fast TrackPoint push moves evenly. ADR 069.
jesse23
force-pushed
the
fix/tick-backpressure
branch
from
October 16, 2026 11:03
0349557 to
3d389c1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Stacked on #51 (merge that first).
Scrolling Claude Code's fullscreen mode inside herdr lagged and then jumped a long way; Claude Code on its own, pi, and pi inside herdr were smooth, and 1.15 lagged the same way. Traced in the browser and on the server: webtty was idle and flow control never blocked; herdr redraws its whole screen (~35 KB, one synchronized update) for every scroll tick, starting 30–45 ms later, and with ticks paced about one a frame (ADR 045) each was redrawn separately, so a slow app fell behind and caught up in a jump.
Ticks wait for the app's answer.
TickPacersends no more ticks until the app has answered the last ones with output that leaves no synchronized update open (or 100 ms pass); the ticks due meanwhile go together. Apps that answer within a frame (pi, vim, Claude Code alone) get their ticks as before.A slow app gets at most 12 ticks a go. While the app's answers average over 40 ms, a go sends at most 12 ticks and drops the rest past one more go. A real TrackPoint capture (hard pushes of 300–1,150 px every 15–60 ms) moved Claude Code inside herdr 24 lines an update and up to 190, unevenly; capped, 12 lines every update. Fast apps are never capped (identical replay).
Measured on a real herdr session at its size, a fast scroll: tick to output 31–42 ms at the 90th percentile and 45–48 ms worst, against 50–125 / 67–212 ms with paced ticks and 48–79 / 49–116 ms for 1.15. Scroll benchmark against the build before: unchanged for pi, vim, nvim, yazi, less, the shell, the stream test and pi inside herdr; Claude Code inside herdr 28.5 → 35.9 paints/s with a wheel and 375 → 83 ms p90 gap with a TrackPoint; Claude Code alone with a wheel paints less often for the same distance (46 → 36/s). Details in ADR 069.
Tests: ticks due while waiting go together, a slow app is capped and the excess dropped, a fast app is not, an unanswered app gets ticks after 100 ms, the end of a synchronized update is the answer (each fails without its rule). Unit tests (3091), lint, coverage targets and the architecture check pass.
ADRs