Skip to content

perf(terminal): bound idle render costs for terminals and agent spinners - #675

Open
onlynet wants to merge 1 commit into
Kuddev:mainfrom
onlynet:pr/idle-cpu-optimizations
Open

onlynet wants to merge 1 commit into
Kuddev:mainfrom
onlynet:pr/idle-cpu-optimizations

Conversation

@onlynet

@onlynet onlynet commented Oct 17, 2026

Copy link
Copy Markdown

Result / 用户结果

Idle terminals and waiting agent sessions no longer redraw the whole
window at display refresh rate. Previously a parked agent turn kept the
spinner (and therefore the window) at vsync rate, the cursor blink chain
ticked forever on unattended panes, every PTY read batch issued a frame,
and the 1 Hz watchdog ran a full-machine ps enumeration on the UI
thread (up to ~1 s of input latency per probe with large process tables,
measured in the Fleet fork on 2026-10-12). All of these costs now have a
local budget; a motionless terminal produces zero render frames.

Design / 设计边界

  • Responsibility and affected modules: terminal/view.rs (cursor blink
    idle pause, output frame coalescing), workspace/agents.rs
    (SPINNER_TICK 80 ms chain, real-time phase), process_tree.rs
    (cached_activity_evidence, background thread + 250 ms shared
    snapshot), ai_agents.rs (identify/detect single-slot memo keyed by
    manifest generation), agent_detection/claude.toml (prompt_idle region
    widened to six bottom non-empty lines for the Claude CLI 2.x chrome).
  • Each budget sits at its own source; there is no global frame
    throttling and no semantic change: blink restarts on any input, output
    storms neither keep the cursor lit nor add restart churn, spinner
    animation speed is unchanged (phase advances by real elapsed time).
  • Threading: watchdog probes move off the UI thread; results are applied
    on the UI thread and stale batches are dropped by command identity.
  • Compatibility: the memo never shortens the sampling cadence — the
    Fleet regression where caching short-circuited
    refresh_agent_screen_state and pinned agents at Running is recorded
    in the note and explicitly avoided (cache key includes the manifest
    generation so rule edits invalidate old conclusions).

Evidence / 验证依据

  • cargo test -p nebula — full suite green on Windows; includes new
    unit tests for the pure decision functions
    (blink_silence_pauses_only_after_the_full_threshold,
    output-frame timing cases, spinner phase wrap).
  • python scripts/check_architecture.py --base upstream/main — exit 0
    (587 counted source lines incl. the note).
  • Hot-path changes: measurements cited from the Fleet fork sampling
    (2026-10-12): vsync-rate spinner redraws were the single largest
    idle-CPU item; watchdog stalls reproducible as periodic input latency.
    After: 12.5 Hz spinner chain, zero frames for motionless terminals,
    ≤125 fps output ceiling, UI thread never blocks on ps.
  • Not run: macOS/Linux sides of the suite (Windows verification
    machine); no platform-specific code is touched beyond the shared
    watchdog path.
  • Decision record:
    architecture/notes/nebula_app/gpui_shell/terminal/2026-10-12-idle-render-frame-budgets.md
    (a future render-snapshot cache stays a separate design gate and is
    deliberately not part of this PR).

Required Review / 必须确认

  • I followed CONTRIBUTING.md, docs/architecture.md, and docs/project-constraints.md.
  • I split responsibilities, not arbitrary line ranges; no duplicate behavior authority was added.
  • python3 scripts/check_architecture.py --base <PR-base-commit> passes; budgets were not inflated to fit the change.
  • Tests cover success and failure; platform/feature coverage limitations are stated.
  • New messages use typed i18n IDs and matching placeholders; untranslated content has an explicit fallback.
  • Governance changes include a counterexample, corrected contract, tests, and a maintainer-reviewed decision. (No governance changes in this PR.)

🤖 Generated with Claude Code

@onlynet
onlynet requested a review from Kuddev as a code owner October 17, 2026 14:51
@onlynet
onlynet force-pushed the pr/idle-cpu-optimizations branch 2 times, most recently from 8cf9982 to 90b9ced Compare October 18, 2026 02:56
Idle terminals and waiting agent sessions no longer redraw the whole
window at display refresh rate:

- Spinner badges run one 80 ms timer chain with real-time phase advance
  (vsync-rate redraws were the largest idle-CPU item, measured in the
  Fleet fork); a packed latch+epoch (SpinnerChain) guarantees at most
  one chain and tears down in-flight chains when the badge hides.
- Cursor blink stops toggling after 8 s of total silence and stays
  solid: a motionless terminal produces zero render frames; input
  restarts it immediately, PTY output restarts it only when paused.
- PTY output frames coalesce to at most one frame per 8 ms (≤125 fps)
  with a bounded trailing frame; hiding a pane clears the merge baseline.
- The 1 Hz watchdog's ps enumeration moves off the UI thread behind
  cached_activity_evidence (250 ms shared snapshot); stale batches are
  dropped by command identity.
- Agent identify/detect gain a single-slot memo keyed by manifest
  generation; sampling cadence is untouched (the Fleet regression where
  caching short-circuited refresh_agent_screen_state and pinned agents
  at Running is documented in the note).

Decision functions are pure and unit-tested.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@onlynet
onlynet force-pushed the pr/idle-cpu-optimizations branch from 90b9ced to 5432487 Compare October 18, 2026 03:20
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