Skip to content

fix(tui): restore working shimmer after preamble output#10701

Merged
joshka-oai merged 1 commit intomainfrom
joshka/fix-missing-preamble-shimmer
Feb 5, 2026
Merged

fix(tui): restore working shimmer after preamble output#10701
joshka-oai merged 1 commit intomainfrom
joshka/fix-missing-preamble-shimmer

Conversation

@joshka-oai
Copy link
Collaborator

Problem

When a turn streamed a preamble line before any tool activity, ChatWidget hid the status row while committing streamed lines and did not restore it until a later event (commonly ExecCommandBegin). During that idle gap, the UI looked finished even though the turn was still active.

Mental model

The bottom status row and transcript stream are separate progress affordances:

  • transcript stream shows committed output
  • status row (spinner/shimmer + header) shows liveness of an active turn

While stream output is actively committing, hiding the status row is acceptable to avoid redundant visual noise. Once stream controllers go idle, an active turn must restore the status row immediately so liveness remains visible across preamble-to-tool gaps.

Non-goals

  • No changes to streaming chunking policy or pacing.
  • No changes to final completion behavior (status still hides when task actually ends).
  • No refactor of status lifecycle ownership between ChatWidget and BottomPane.

Tradeoffs

  • We keep the existing behavior of hiding the status row during active stream commits.
  • We add explicit restoration on the idle boundary when the task is still running.
  • This introduces one extra status update on idle transitions, which is small overhead but makes liveness semantics consistent.

Architecture

run_commit_tick_with_scope in chatwidget.rs now documents and enforces a two-phase contract:

  1. For each committed streamed cell, hide status and append transcript output.
  2. If controllers are present and all idle, restore status iff task is still running, preserving the current header.

This keeps status ownership in ChatWidget while relying on BottomPane helpers:

  • hide_status_indicator() during active stream commits
  • ensure_status_indicator() + set_status_header(current_status_header) at stream-idle boundary

Documentation pass additions:

  • Clarified the function-level contract and lifecycle intent in run_commit_tick_with_scope.
  • Added an explicit regression snapshot test comment describing the failing sequence.

Observability

Signal that the fix is present:

  • In the preamble-idle state, rendered output still includes • Working (… esc to interrupt).
  • New snapshot: codex_tui__chatwidget__tests__preamble_keeps_working_status.snap.

Debug path for future regressions:

  • Start at run_commit_tick_with_scope for hide/restore transitions.
  • Verify bottom_pane.is_task_running() at idle transition.
  • Confirm current_status_header continuity when status is recreated.
  • Use the new snapshot and targeted test sequence to reproduce deterministic preamble-idle behavior.

Tests

  • Updated regression assertion:
    • streaming_final_answer_keeps_task_running_state now expects status widget to remain present while turn is running.
  • Renamed/updated behavioral regression:
    • preamble_keeps_status_indicator_visible_until_exec_begin.
  • Added snapshot regression coverage:
    • preamble_keeps_working_status_snapshot.
    • Snapshot file: tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__preamble_keeps_working_status.snap.

Commands run:

  • just fmt
  • cargo test -p codex-tui preamble_keeps_status_indicator_visible_until_exec_begin
  • cargo test -p codex-tui preamble_keeps_working_status_snapshot

Risks / Inconsistencies

  • Status visibility policy is still split across multiple event paths (commit tick, turn complete, exec begin), so future regressions can reintroduce ordering gaps.
  • Restoration depends on is_task_running() correctness; if task lifecycle flags drift, status behavior will drift too.
  • Snapshot proves rendered state, not animation cadence; cadence still relies on frame scheduling behavior elsewhere.

When a preamble line was streamed into history, commit ticks hid the status
indicator and left a dead period before the next tool event restored it.
That made the UI look finished even though the turn was still running.

Restore the status row when stream controllers go idle during an active
turn, preserving the current header so spinner/shimmer animation continues
across preamble-to-tool gaps.

Documentation pass:
- clarify the run_commit_tick_with_scope contract and why status is
  hidden while streaming but restored on idle
- add an explicit regression snapshot that captures the preamble-idle
  state with the Working status line present

Tests:
- cargo test -p codex-tui preamble_keeps_status_indicator_visible_until_exec_begin
- cargo test -p codex-tui preamble_keeps_working_status_snapshot
@joshka-oai
Copy link
Collaborator Author

Compared with #10700: there is semantic overlap, but they cover different timing windows, so both are useful.

Together they address the "looks finished too early" UX across both phases.

I also suggest adding a visual snapshot test for the exec-begin path (in addition to the state/visibility assertion), so we verify the user-visible working affordance and guard against future rendering regressions.

@joshka-oai joshka-oai enabled auto-merge (squash) February 5, 2026 03:09
@joshka-oai joshka-oai merged commit d876f3b into main Feb 5, 2026
32 checks passed
@joshka-oai joshka-oai deleted the joshka/fix-missing-preamble-shimmer branch February 5, 2026 03:28
@github-actions github-actions bot locked and limited conversation to collaborators Feb 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants