From c9b66649a5340607ecff1ef8fc0e6709f4833594 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Wed, 13 May 2026 11:38:58 -0700 Subject: [PATCH] Preserve hidden hook transcript order --- codex-rs/tui/src/chatwidget/hook_lifecycle.rs | 1 + ...flushes_prior_assistant_text_snapshot.snap | 11 ++++ .../src/chatwidget/tests/status_and_layout.rs | 50 +++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot.snap diff --git a/codex-rs/tui/src/chatwidget/hook_lifecycle.rs b/codex-rs/tui/src/chatwidget/hook_lifecycle.rs index bba15124e1..9e1ef02256 100644 --- a/codex-rs/tui/src/chatwidget/hook_lifecycle.rs +++ b/codex-rs/tui/src/chatwidget/hook_lifecycle.rs @@ -43,6 +43,7 @@ impl ChatWidget { if completed_existing_run { self.bump_active_cell_revision(); } else { + self.flush_answer_stream_with_separator(); match self.active_hook_cell.as_mut() { Some(cell) => { cell.add_completed_run(completed); diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot.snap new file mode 100644 index 0000000000..75597216e5 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot.snap @@ -0,0 +1,11 @@ +--- +source: tui/src/chatwidget/tests/status_and_layout.rs +expression: "format!(\"active hooks:\\n{}history:\\n{history}\", active_hook_blob(&chat))" +--- +active hooks: + +history: +• I found the relevant policy. + +• PostToolUse hook (completed) + warning: review this managed hook warning diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index 47226816f1..521fd644eb 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -2856,6 +2856,56 @@ async fn visibility_hidden_hooks_skip_routine_rows_but_render_failures_snapshot( ); } +#[tokio::test] +async fn visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + + handle_agent_message_delta(&mut chat, "I found the relevant policy.\n"); + + let mut hidden_started = hook_started_run( + "post-tool-use:3:/tmp/hooks.json", + codex_app_server_protocol::HookEventName::PostToolUse, + Some("checking output policy"), + ); + hidden_started.visibility_hint = codex_app_server_protocol::HookVisibilityHint::Hidden; + handle_hook_started(&mut chat, hidden_started); + + let mut hidden_warning = hook_completed_run( + "post-tool-use:3:/tmp/hooks.json", + codex_app_server_protocol::HookEventName::PostToolUse, + codex_app_server_protocol::HookRunStatus::Completed, + vec![codex_app_server_protocol::HookOutputEntry { + kind: codex_app_server_protocol::HookOutputEntryKind::Warning, + text: "review this managed hook warning".to_string(), + }], + ); + hidden_warning.visibility_hint = codex_app_server_protocol::HookVisibilityHint::Hidden; + handle_hook_completed(&mut chat, hidden_warning); + + let history = drain_insert_history(&mut rx) + .iter() + .map(|lines| lines_to_single_string(lines)) + .collect::(); + assert_chatwidget_snapshot!( + "visibility_hidden_hook_completion_flushes_prior_assistant_text_snapshot", + format!( + "active hooks:\n{}history:\n{history}", + active_hook_blob(&chat) + ) + ); + + let assistant_index = history + .find("I found the relevant policy.") + .expect("assistant text should be flushed into history"); + let hook_index = history + .find("PostToolUse hook (completed)") + .expect("visible hidden-hook completion should be in history"); + assert!( + assistant_index < hook_index, + "assistant text should precede later hidden-hook output: {history:?}" + ); +} + #[tokio::test] async fn completed_hook_output_precedes_following_assistant_message() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;