Skip to content

Client re-presents stale pane frames: every visible pane jumps to an older viewport position and snaps back, ~12 s cycle (follow-up to #2521/#283; reproduced on master) #2793

Description

@pedrohcmeneses

Is this a reproducible bug?

  • I confirm this is a reproducible bug, not a feature request, idea, question, contribution proposal, or direction check.
  • I reproduced this bug on the version and environment reported below using the exact steps provided.

Current behavior

Agent panes intermittently jump to an older viewport position and snap back, roughly every ~12 s, for 0.25–1.5 s each time. During the jump the pane shows the position of the start of the last user turn, together with a Jump to bottom (click) ↓ pill; then it returns to the bottom on its own.

The pill turns out to be the agent's own UI, not Herdr chrome: the string Jump to bottom exists in the Claude Code binary and is absent from both the herdr binary and every installed plugin. So the flashed frame is stale pane content — a frame the inner TUI drew at some earlier moment — being re-presented by the client and then corrected. That narrows the defect to the client re-presenting an out-of-date frame, consistent with the coordinate-diff analysis in #2521.

Key detail: it happens in every visible pane at the same time, including panes whose agent is idle and producing no output.

Measurements (macOS screencapture burst at 0.2 s, cross-checked against herdr pane read <pane> --source visible sampled in parallel):

  • 13 flash events in 5 minutes, intervals clustering at ~11.7 / 12.3 / 12.1 s (longer gaps are exact multiples, i.e. events the detector missed).
  • The server-side buffer is unchanged during the flash: herdr pane read returns byte-identical output across the event. Only the rendered frame changes. This points at the client/render layer, not pane content.
  • The server-side scroll state is also unchanged: polling herdr pane list every 0.3 s for 90 s while the flashing was ongoing, scroll.offset_from_bottom stayed 0 for every pane the whole time. Buffer static + scroll static + pixels changing = the artifact lives entirely in the client's frame presentation.
  • A rigid vertical-shift detector (best dy alignment between consecutive frames) is blind to it — the whole pane region changes, which matches the coordinate-based diff analysis in Interactive agent output (menu/prompt) causes viewport to scroll up then snap back — coordinate-based diff, not scroll-region aware (follow-up to #283) #2521.

This looks like the same defect described in #2521 (closed by bot for template reasons, not fixed) and originally in #283. That report's code-level analysis — src/protocol/render_ansi.rs::write_changed_cells() doing a coordinate-based cell diff with no scroll-region/insert-line awareness — is consistent with everything measured here.

Expected behavior

A pane that is following live output stays pinned to the bottom. The rendered frame should never briefly show a previous viewport position (nor the Jump to bottom pill) unless the user actually scrolled.

Reproduction

  1. Open a workspace tab with 2–4 panes running an interactive agent (Claude Code here), each with a long transcript (several hours of history; short/fresh sessions reproduce far less often).
  2. Focus that tab and leave the machine idle — no typing, no scrolling, no tab/space switching.
  3. Watch for ~5 minutes, or capture frames: for i in $(seq 1 500); do screencapture -x -t jpg -R<x,y,w,h> f-$i.jpg; sleep 0.2; done
  4. Detect events by looking for a large frame change that reverts to the previous frame within ~1.5 s (a plain dy shift detector will not catch it).

Ruled out here by direct measurement, in case it saves triage time:

  • [experimental] pane_history on and off — no difference.
  • Restarting the client, and restarting the server — no difference.
  • All 6 installed plugins disabled at once (herdr plugin disable), then a clean 5-minute observation window on the same tab (2026-08-13) — still flashes. Plugins are ruled out.
  • Running master (b710176, 2026-08-13, includes fix(render): compact large terminal redraws #2675 "compact large terminal redraws") as both server and client — still flashes. fix(render): compact large terminal redraws #2675 does not fix it.
  • Focus events as trigger: injecting \e[O\e[I (focus-out/in) into a pane's PTY 8 times at 2 s intervals while watching it — no flash.
  • Same-size SIGWINCH as trigger: kill -WINCH on the agent process 8 times at 2 s intervals — no flash.
  • redraw_on_focus_gained = false — no difference.
  • External API calls (herdr agent list, herdr pane read, herdr pane list) fired at marked timestamps produced no flash, so third-party API polling is not the trigger.
  • Idle panes do not leave follow mode on their own (offset_from_bottom stayed 0 over 3 minutes), and neither pane zoom nor pane split (a real 45→22 row resize) knocked a pane out of follow.

Not yet determined: whether it reproduces with plain shell panes and no agent attached. My attempt at that test was contaminated (the panes had been scrolled manually), so I am reporting it as open rather than claiming it.

One possibly related observation: herdr plugin log shows pane.focused events firing repeatedly while the user is not switching panes.

Screenshots are omitted because the panes contain patient data (the user is a physician). I can reproduce and attach redacted or synthetic-content captures on request.

Impact

Continuous, on every visible agent pane, during normal work. Text becomes hard to read because the pane keeps jumping to an older position and back while you are reading it. The only workaround found is zooming a single pane (prefix+z), which reduces exposure but does not fix the underlying behavior.

Environment

  • Herdr version: 0.8.0 stable (Homebrew) AND master b710176 (2026-08-13) — identical behavior on both
  • Operating system: macOS 27.0 (Darwin 27.0.0), Apple Silicon
  • Terminal: Ghostty
  • Shell: zsh
  • Agent: Claude Code 2.1.232 (runs in alternate screen; panes therefore report no scrollback)
  • Relevant config: tab_bar_position = "top", pane_scrollbars = false, show_agent_labels_on_pane_borders = true, agent_panel_sort = "priority", [ui.toast] delivery = "terminal", [experimental] pane_history = true (default restored after testing)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions