Skip to content

Reduce TUI frame stalls under tool activity - #358

Merged
edwin-zvs merged 9 commits into
mainfrom
investigate-tui-frame-lag
Jun 6, 2026
Merged

Reduce TUI frame stalls under tool activity#358
edwin-zvs merged 9 commits into
mainfrom
investigate-tui-frame-lag

Conversation

@edwin-zvs

@edwin-zvs edwin-zvs commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • stop running zarvis tool blocks from invalidating the synthesized PTY parser every elapsed second
  • add an elapsed-time budget to notification burst draining so heavy session activity yields back to paint/tick/input sooner
  • bound zarvis tool expand/collapse work by rebuilding only the retained suffix and capping inline expanded output
  • reuse already-rendered pending text metrics when a new zarvis tool entry flushes live text into structured history
  • handle partial pending flushes by reusing the rendered prefix and processing only the newly appended suffix
  • keep the TUI's local tool-output preview bounded at ingestion
  • remove terminal-TUI tool grouping so expand/collapse and live result updates invalidate only one tool block
  • update the tool-call grouping spec to document the TUI terminal-rendering exception

Investigation

The repro points at zarvis tool calls and expand/collapse. User traces showed the remaining visible freezes were inside render:terminal_replay, with two clicked expand/collapse frames spending about 328-330 ms in replay and about 343-345 ms in total draw time.

The bisected regression commit was 23f120d (Group adjacent tool calls in clients). The remaining expand/collapse issue was the grouping unit itself: bootstrap and transcript replay could build large ToolGroup items. Clicking one child output mutated the group item, so the renderer had to re-synthesize and replay all grouped siblings rather than one affected ToolBlock.

The terminal TUI now keeps tool calls as independent top-level blocks. That trades grouped chrome for bounded frame work: clicking or updating one tool block invalidates one block, not a potentially large group. The existing spec now says grouping is still allowed for clients that can keep updates bounded, but terminal-style renderers should prefer independently invalidated blocks.

Other fixes in this PR address earlier stalls found while narrowing the issue:

  • Running tool signatures no longer include elapsed seconds, avoiding once-per-second parser invalidations.
  • feed_task_start recognizes pending PTY text already rendered by the previous frame. If pending text was flushed into a PtyChunk, the renderer reuses the rendered prefix metrics.
  • The pending-flush reuse also handles the partial case where the flushed chunk contains an already-rendered prefix plus an unrendered suffix.
  • Expanded output is capped inline, and local TUI tool-result history stores a bounded preview rather than cloning full tool output into the renderer.
  • Notification draining has a time budget, so session bursts do not monopolize the render loop.

Tests

  • cargo test -p agentd-cli notification_drain_stops_on_count_or_time_budget
  • cargo test -p agentd-cli run_loop_notification_drain_uses_time_budget
  • cargo test -p agentd-cli run_loop_select_biases_input_ahead_of_notifications
  • cargo test -p agentd-cli running_tool_signature_ignores_elapsed_seconds_until_controls_appear
  • cargo test -p agentd-cli running_tool_block_shows_keyboard_hints_not_buttons
  • cargo test -p agentd-cli replay_full_scales_with_history_size
  • cargo test -p agentd-cli replay_full_rows_grow_does_not_reprocess_history
  • cargo test -p agentd-cli zarvis_steady_state_render_is_cheap_with_many_items
  • cargo test -p agentd-cli expanded_tool_output_is_capped_inline
  • cargo test -p agentd-cli tool_result_history_preview_is_bounded
  • cargo test -p agentd-cli zarvis_tool_expand_collapse_rebuilds_only_retained_suffix -- --nocapture
  • cargo test -p agentd-cli zarvis_tool_start_reuses_already_rendered_pending_text -- --nocapture
  • cargo test -p agentd-cli zarvis_tool_start_reuses_partially_rendered_pending_text -- --nocapture
  • cargo test -p agentd-cli ungrouped_tool_update_stays_bounded -- --nocapture
  • cargo test -p agentd-cli live_tool_start_after_render_stays_append_only
  • cargo test -p agentd-cli live_late_tool_use_hydration_after_render_stays_append_only
  • cargo test -p agentd-cli adjacent_same_tool_task_starts_stay_separate_blocks
  • cargo test -p agentd-cli late_tool_use_hydration_stays_separate_from_previous_block
  • cargo test -p agentd-cli many_small_events_stay_fast_as_history_grows -- --nocapture
  • cargo test -p agentd-cli pty_render -- --nocapture
  • git diff --check
  • cargo build

Note: cargo fmt --check --package agentd-cli still reports existing formatting drift in unrelated CLI files, so I did not apply repo-wide formatting churn.

@edwin-zvs
edwin-zvs merged commit d97bdd1 into main Jun 6, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the investigate-tui-frame-lag branch June 6, 2026 08:00
@edwin-zvs edwin-zvs mentioned this pull request Jun 6, 2026
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.

1 participant