tui: clear stale hook row after turn completion (#27619)

Fixes #27210.

## Why
When the app server reports a visible `HookStarted` event for a
`PostToolUse` hook but the turn reaches `TurnCompleted` before a
matching hook completion event arrives, the TUI can leave the transient
`Running PostToolUse hook` row visible after the agent is done.
Interrupted and failed turn cleanup already drops transient live hook
rows; the normal completion path did not.

## What Changed
- Added `ChatWidget::clear_active_hook_cell()` for dropping transient
live hook status without writing it to history.
- Call that cleanup from normal task completion, while reusing it for
the existing start/finalize cleanup paths.
- Added `completed_turn_clears_visible_running_hook` snapshot coverage
for the reported `PostToolUse` case.

## Tests
- `just test -p codex-tui completed_turn_clears_visible_running_hook`
- `just test -p codex-tui` (fails on current `main` in unrelated
guardian tests:
`update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
and
`update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`)
This commit is contained in:
Mitsuhiro Kotake authored and GitHub committed 2026-06-12 09:15:04 +09:00
1 parent 92d9036910
commit 44403dd3dd
4 files changed
+44 -6

No files matched your search

@@ -2173,6 +2173,32 @@ async fn interrupted_turn_clears_visible_running_hook() {
);
}
#[tokio::test]
async fn completed_turn_clears_visible_running_hook() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
handle_hook_started(
&mut chat,
hook_started_run(
"post-tool-use:0:/tmp/hooks.json",
codex_app_server_protocol::HookEventName::PostToolUse,
/*status_message*/ None,
),
);
reveal_running_hooks(&mut chat);
let before_completion = active_hook_blob(&chat);
handle_turn_completed(&mut chat, "turn-1", /*duration_ms*/ None);
assert_chatwidget_snapshot!(
"completed_turn_clears_visible_running_hook",
format!(
"before completion:\n{before_completion}after completion:\n{}",
active_hook_blob(&chat)
)
);
}
#[tokio::test]
async fn status_line_fast_mode_renders_on_and_off() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;