Status indicator redraws at 32 ms while only waiting on a background terminal

Open Beginner friendly
#37,706 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
rust
Domain
cli, performance

Research direction

Start in codex-rs/tui/src/status_indicator_widget.rs at StatusIndicatorWidget::render, then inspect ChatWidget::on_terminal_interaction and the shared waiting-state header. Preserve the 32 ms interval for active turns while using a 200 ms interval for the background-terminal waiting state, and ensure the indicator no longer redraws at full rate while idle.

Written by the indexing model from the issue text.

Description

bug CLI performance TUI

Summary

The TUI status indicator schedules a new animation frame every 32 ms whenever animations are enabled, including while the agent is only waiting on a background terminal and has nothing to animate.

Reported downstream by @rebroad with measurements and a tested patch: DioNanos/codex-termux#16. The analysis and the numbers below are his; I am filing here because the code is upstream's and the contributing policy asks for an issue rather than a pull request.

Measurement

On Android/Termux, roughly 30% CPU in the codex-main thread while waiting for a separate rustc process. The compiler was not a child of the TUI process — the CPU was the redraws themselves, at about 31 frames per second with nothing changing on screen.

Root cause

StatusIndicatorWidget::render in codex-rs/tui/src/status_indicator_widget.rs:

if self.animations_enabled {
    self.frame_requester
        .schedule_frame_in(Duration::from_millis(32));
}

The interval does not depend on what the indicator is showing. The Waiting for background terminal state, set by ChatWidget::on_terminal_interaction while polling background output, animates at the same rate as an active turn.

Outline of a fix

The original suggestion was a flat 200 ms. That also slows the indicator while the model is working, so a narrower version keys off the state:

let interval = if self.header == WAITING_ON_BACKGROUND_TERMINAL_HEADER {
    Duration::from_millis(200)
} else {
    Duration::from_millis(32)
};
self.frame_requester.schedule_frame_in(interval);

About 5 fps for a waiting indicator and an elapsed-time display, unchanged everywhere else. Making the header label a shared constant, used both where the state is set and where the rate is chosen, keeps the two from drifting.

Battery and thermal cost is most visible on phones, but the idle redraws are not platform-specific.

Dominant language
Rust
Stars
125k
Forks
19.5k
Avg merge
1m
Merged PRs (30d)
1k

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/codex

All issues in openai/codex

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.