TUI leaves a running PostToolUse hook row visible after normal turn completion

Resolved 💬 0 comments Opened Jun 9, 2026 by Rokurolize Closed Jun 12, 2026

What version of Codex CLI is running?

Source checkout reproduction: openai/codex main at a770e5b8470d3320eb53a56a286ea4a0a70a1f59 (2026-06-10). No installed CLI version is required to reproduce the regression test below.

What subscription do you have?

Not applicable to the source-level regression test.

Which model were you using?

Not model-specific. The source-level reproduction below exercises TUI state handling directly.

What platform is your computer?

Not expected to be platform-specific. The source-level regression test was run from a Linux checkout.

What terminal emulator and version are you using (if applicable)?

Not applicable to the source-level regression test.

Codex doctor report

Not available. The reproduction below is a focused `codex-tui` unit test rather than an installed CLI diagnostic.

What issue are you seeing?

After a turn completes normally, the TUI can still show a live hook status row such as:

• Running PostToolUse hook

At that point the turn is already complete, so the row appears to be stale UI state. The report is about the live hook row remaining visible after normal completion, not about whether hook dispatch itself works.

This looks related to the existing turn-finalization cleanup from #20674, but it appears to be a separate completed-turn path. #20674 covered interrupted/finalized turns and added interrupted_turn_clears_visible_running_hook; the reproduction below adds the sibling completed-turn case.

What steps can reproduce the bug?

From a clean checkout of openai/codex at a770e5b8470d3320eb53a56a286ea4a0a70a1f59, add this regression test next to interrupted_turn_clears_visible_running_hook in codex-rs/tui/src/chatwidget/tests/status_and_layout.rs:

#[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);
    assert!(
        active_hook_blob(&chat).contains("Running PostToolUse hook"),
        "precondition: hook row should be visible before turn completion"
    );

    handle_turn_completed(&mut chat, "turn-1", /*duration_ms*/ None);

    assert_eq!(
        active_hook_blob(&chat),
        "<empty>\n",
        "completed turns should not leave a PostToolUse hook row visible"
    );
}

Then run:

cargo test --manifest-path codex-rs/Cargo.toml \
  -p codex-tui completed_turn_clears_visible_running_hook -- --nocapture

An equivalent patch can be attached for

public-posttooluse-stale-hook-repro.patch

convenience, but the test above is the full reproduction.

What is the expected behavior?

When TurnCompleted is handled, the visible PostToolUse hook row should be cleared. The completed turn should not leave Running PostToolUse hook visible as if work were still in progress.

Additional information

Observed test result on a770e5b8470d3320eb53a56a286ea4a0a70a1f59:

test chatwidget::tests::status_and_layout::completed_turn_clears_visible_running_hook ... FAILED

assertion failed: `(left == right)`: completed turns should not leave a PostToolUse hook row visible

Diff < left / right > :
<• Running PostToolUse hook
><empty>

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 2803 filtered out; finished in 0.02s

I searched existing issues and PRs for terms including PostToolUse, Running PostToolUse hook, active_hook, TurnCompleted hook, stale hook row, and visible running hook. I found merged PR #20674 for interrupted/finalized turns, but did not find an open issue specifically for the normally completed-turn stale row.

View original on GitHub ↗