From 43a5e0e434319e2dd958acbfa7132fe6eb44587f Mon Sep 17 00:00:00 2001 From: won Date: Wed, 20 May 2026 14:44:46 -0700 Subject: [PATCH 1/6] draft --- codex-rs/exec/src/lib.rs | 33 +++++++++----- codex-rs/exec/src/lib_tests.rs | 47 ++++++++++++++++++++ codex-rs/exec/tests/suite/approval_policy.rs | 41 +++++++++++++++++ codex-rs/exec/tests/suite/mod.rs | 1 + 4 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 codex-rs/exec/tests/suite/approval_policy.rs diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 3db7a51576d..d1df1030a82 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -80,6 +80,7 @@ use codex_otel::set_parent_from_context; use codex_otel::traceparent_context_from_env; use codex_protocol::SessionId; use codex_protocol::ThreadId; +use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::config_types::SandboxMode; use codex_protocol::models::ActivePermissionProfile; use codex_protocol::models::PermissionProfile; @@ -405,12 +406,12 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result None // No model specified, will use the default. }; - // Load configuration and determine approval policy let overrides = ConfigOverrides { model, review_model: None, config_profile, - // Default to never ask for approvals in headless mode. Feature flags can override. + // Default to never ask for approvals in headless mode. Rebuild below if + // the fully resolved reviewer is AutoReview. approval_policy: Some(AskForApproval::Never), approvals_reviewer: None, sandbox_mode, @@ -435,14 +436,26 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result additional_writable_roots: add_dir, }; - let config = ConfigBuilder::default() - .cli_overrides(cli_kv_overrides) - .harness_overrides(overrides) - .loader_overrides(loader_overrides) - .strict_config(strict_config) - .cloud_requirements(cloud_requirements) - .build() - .await?; + let build_config = |overrides| { + ConfigBuilder::default() + .codex_home(codex_home.to_path_buf()) + .cli_overrides(cli_kv_overrides.clone()) + .harness_overrides(overrides) + .loader_overrides(loader_overrides.clone()) + .strict_config(strict_config) + .cloud_requirements(cloud_requirements.clone()) + .build() + }; + let config = build_config(overrides.clone()).await?; + let config = if config.approvals_reviewer == ApprovalsReviewer::AutoReview { + build_config(ConfigOverrides { + approval_policy: None, + ..overrides + }) + .await? + } else { + config + }; #[allow(clippy::print_stderr)] match check_execpolicy_for_warnings(&config.config_layer_stack).await { diff --git a/codex-rs/exec/src/lib_tests.rs b/codex-rs/exec/src/lib_tests.rs index 79d07a0b761..c8fccc2e66d 100644 --- a/codex-rs/exec/src/lib_tests.rs +++ b/codex-rs/exec/src/lib_tests.rs @@ -477,6 +477,53 @@ async fn thread_start_params_include_user_thread_source() { ); } +#[tokio::test] +async fn exec_config_preserves_reviewable_approval_policy_for_required_auto_review() { + let codex_home = tempdir().expect("create temp codex home"); + let cwd = tempdir().expect("create temp cwd"); + let cloud_requirements = codex_config::CloudRequirementsLoader::new(async { + Ok(Some(codex_config::ConfigRequirementsToml { + allowed_approvals_reviewers: Some(vec![ApprovalsReviewer::AutoReview]), + ..Default::default() + })) + }); + let build_config = |approval_policy| { + ConfigBuilder::default() + .codex_home(codex_home.path().to_path_buf()) + .harness_overrides(ConfigOverrides { + approval_policy, + cwd: Some(cwd.path().to_path_buf()), + ..Default::default() + }) + .loader_overrides(LoaderOverrides::without_managed_config_for_tests()) + .cloud_requirements(cloud_requirements.clone()) + .build() + }; + let constrained = build_config(Some(AskForApproval::Never)) + .await + .expect("build config with exec approval override"); + let exec_config = if constrained.approvals_reviewer == ApprovalsReviewer::AutoReview { + build_config(None) + .await + .expect("rebuild config without exec approval override") + } else { + constrained + }; + let expected = build_config(None) + .await + .expect("build config without exec approval override"); + + assert_eq!( + exec_config.approvals_reviewer, + ApprovalsReviewer::AutoReview + ); + assert_eq!(expected.approvals_reviewer, ApprovalsReviewer::AutoReview); + assert_eq!( + exec_config.permissions.approval_policy.value(), + expected.permissions.approval_policy.value() + ); +} + #[test] fn active_profile_selection_uses_profile_id_only() { let selection = permission_profile_id_from_active_profile(ActivePermissionProfile::new( diff --git a/codex-rs/exec/tests/suite/approval_policy.rs b/codex-rs/exec/tests/suite/approval_policy.rs new file mode 100644 index 00000000000..66b3fcb845f --- /dev/null +++ b/codex-rs/exec/tests/suite/approval_policy.rs @@ -0,0 +1,41 @@ +#![cfg(not(target_os = "windows"))] +#![allow(clippy::expect_used, clippy::unwrap_used)] + +use core_test_support::responses; +use core_test_support::test_codex_exec::test_codex_exec; + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn exec_preserves_on_request_for_auto_review_config() -> anyhow::Result<()> { + let test = test_codex_exec(); + std::fs::write( + test.home_path().join("config.toml"), + r#" +approval_policy = "on-request" +approvals_reviewer = "auto_review" +"#, + )?; + + let server = responses::start_mock_server().await; + let body = responses::sse(vec![ + responses::ev_response_created("response_1"), + responses::ev_assistant_message("response_1", "done"), + responses::ev_completed("response_1"), + ]); + responses::mount_sse_once(&server, body).await; + + let output = test + .cmd_with_server(&server) + .arg("--skip-git-repo-check") + .arg("check approval mode") + .output()?; + + assert!(output.status.success(), "exec run failed: {output:?}"); + + let stderr = String::from_utf8(output.stderr)?; + assert!( + stderr.contains("approval: on-request"), + "stderr missing preserved auto-review approval mode: {stderr}" + ); + + Ok(()) +} diff --git a/codex-rs/exec/tests/suite/mod.rs b/codex-rs/exec/tests/suite/mod.rs index c6fa0f9fde6..6f868563273 100644 --- a/codex-rs/exec/tests/suite/mod.rs +++ b/codex-rs/exec/tests/suite/mod.rs @@ -1,6 +1,7 @@ // Aggregates all former standalone integration tests as modules. mod add_dir; mod apply_patch; +mod approval_policy; mod auth_env; mod ephemeral; mod mcp_required_exit; From ce1b465f59e05bee26781cf1b913be891aca3d79 Mon Sep 17 00:00:00 2001 From: won Date: Wed, 20 May 2026 15:20:24 -0700 Subject: [PATCH 2/6] addressing dangerously --- codex-rs/exec/src/lib.rs | 4 ++- codex-rs/exec/tests/suite/approval_policy.rs | 37 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index d1df1030a82..8b5be895336 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -447,7 +447,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result .build() }; let config = build_config(overrides.clone()).await?; - let config = if config.approvals_reviewer == ApprovalsReviewer::AutoReview { + let config = if config.approvals_reviewer == ApprovalsReviewer::AutoReview + && !dangerously_bypass_approvals_and_sandbox + { build_config(ConfigOverrides { approval_policy: None, ..overrides diff --git a/codex-rs/exec/tests/suite/approval_policy.rs b/codex-rs/exec/tests/suite/approval_policy.rs index 66b3fcb845f..3e48240446e 100644 --- a/codex-rs/exec/tests/suite/approval_policy.rs +++ b/codex-rs/exec/tests/suite/approval_policy.rs @@ -39,3 +39,40 @@ approvals_reviewer = "auto_review" Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn exec_bypass_preserves_never_for_auto_review_config() -> anyhow::Result<()> { + let test = test_codex_exec(); + std::fs::write( + test.home_path().join("config.toml"), + r#" +approval_policy = "on-request" +approvals_reviewer = "auto_review" +"#, + )?; + + let server = responses::start_mock_server().await; + let body = responses::sse(vec![ + responses::ev_response_created("response_1"), + responses::ev_assistant_message("response_1", "done"), + responses::ev_completed("response_1"), + ]); + responses::mount_sse_once(&server, body).await; + + let output = test + .cmd_with_server(&server) + .arg("--skip-git-repo-check") + .arg("--dangerously-bypass-approvals-and-sandbox") + .arg("check approval mode") + .output()?; + + assert!(output.status.success(), "exec run failed: {output:?}"); + + let stderr = String::from_utf8(output.stderr)?; + assert!( + stderr.contains("approval: never"), + "stderr missing bypass approval mode: {stderr}" + ); + + Ok(()) +} From 4d8242a1795911373635b847d87e4f4179800898 Mon Sep 17 00:00:00 2001 From: won Date: Thu, 21 May 2026 18:47:18 -0700 Subject: [PATCH 3/6] Remove mirrored codex exec auto-review unit test --- codex-rs/exec/src/lib_tests.rs | 47 ---------------------------------- 1 file changed, 47 deletions(-) diff --git a/codex-rs/exec/src/lib_tests.rs b/codex-rs/exec/src/lib_tests.rs index c8fccc2e66d..79d07a0b761 100644 --- a/codex-rs/exec/src/lib_tests.rs +++ b/codex-rs/exec/src/lib_tests.rs @@ -477,53 +477,6 @@ async fn thread_start_params_include_user_thread_source() { ); } -#[tokio::test] -async fn exec_config_preserves_reviewable_approval_policy_for_required_auto_review() { - let codex_home = tempdir().expect("create temp codex home"); - let cwd = tempdir().expect("create temp cwd"); - let cloud_requirements = codex_config::CloudRequirementsLoader::new(async { - Ok(Some(codex_config::ConfigRequirementsToml { - allowed_approvals_reviewers: Some(vec![ApprovalsReviewer::AutoReview]), - ..Default::default() - })) - }); - let build_config = |approval_policy| { - ConfigBuilder::default() - .codex_home(codex_home.path().to_path_buf()) - .harness_overrides(ConfigOverrides { - approval_policy, - cwd: Some(cwd.path().to_path_buf()), - ..Default::default() - }) - .loader_overrides(LoaderOverrides::without_managed_config_for_tests()) - .cloud_requirements(cloud_requirements.clone()) - .build() - }; - let constrained = build_config(Some(AskForApproval::Never)) - .await - .expect("build config with exec approval override"); - let exec_config = if constrained.approvals_reviewer == ApprovalsReviewer::AutoReview { - build_config(None) - .await - .expect("rebuild config without exec approval override") - } else { - constrained - }; - let expected = build_config(None) - .await - .expect("build config without exec approval override"); - - assert_eq!( - exec_config.approvals_reviewer, - ApprovalsReviewer::AutoReview - ); - assert_eq!(expected.approvals_reviewer, ApprovalsReviewer::AutoReview); - assert_eq!( - exec_config.permissions.approval_policy.value(), - expected.permissions.approval_policy.value() - ); -} - #[test] fn active_profile_selection_uses_profile_id_only() { let selection = permission_profile_id_from_active_profile(ActivePermissionProfile::new( From 20b644b2ef00b9bd550f56dd5b71beea87f1b628 Mon Sep 17 00:00:00 2001 From: won Date: Fri, 29 May 2026 09:21:13 -0700 Subject: [PATCH 4/6] Preserve full-auto approval policy in exec auto-review --- codex-rs/exec/src/lib.rs | 1 + codex-rs/exec/tests/suite/approval_policy.rs | 37 ++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index b4bf12de345..beec5df7238 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -448,6 +448,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result let config = build_config(overrides.clone()).await?; let config = if config.approvals_reviewer == ApprovalsReviewer::AutoReview && !dangerously_bypass_approvals_and_sandbox + && !removed_full_auto { build_config(ConfigOverrides { approval_policy: None, diff --git a/codex-rs/exec/tests/suite/approval_policy.rs b/codex-rs/exec/tests/suite/approval_policy.rs index 3e48240446e..625a26d25f7 100644 --- a/codex-rs/exec/tests/suite/approval_policy.rs +++ b/codex-rs/exec/tests/suite/approval_policy.rs @@ -76,3 +76,40 @@ approvals_reviewer = "auto_review" Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn exec_full_auto_preserves_never_for_auto_review_config() -> anyhow::Result<()> { + let test = test_codex_exec(); + std::fs::write( + test.home_path().join("config.toml"), + r#" +approval_policy = "on-request" +approvals_reviewer = "auto_review" +"#, + )?; + + let server = responses::start_mock_server().await; + let body = responses::sse(vec![ + responses::ev_response_created("response_1"), + responses::ev_assistant_message("response_1", "done"), + responses::ev_completed("response_1"), + ]); + responses::mount_sse_once(&server, body).await; + + let output = test + .cmd_with_server(&server) + .arg("--skip-git-repo-check") + .arg("--full-auto") + .arg("check approval mode") + .output()?; + + assert!(output.status.success(), "exec run failed: {output:?}"); + + let stderr = String::from_utf8(output.stderr)?; + assert!( + stderr.contains("approval: never"), + "stderr missing full-auto approval mode: {stderr}" + ); + + Ok(()) +} From 75239d2fa47baf603076dc25536fe89a8d0b7d2d Mon Sep 17 00:00:00 2001 From: won Date: Sun, 31 May 2026 20:23:01 -0700 Subject: [PATCH 5/6] Retry exec auto-review config without synthetic approval --- codex-rs/exec/src/lib.rs | 55 ++++++++++++++++++++++-------- codex-rs/exec/src/lib_tests.rs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 13 deletions(-) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index bf2f13faed8..53c6379f1b2 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -135,6 +135,7 @@ pub use exec_events::TurnStartedEvent; pub use exec_events::Usage; pub use exec_events::WebSearchItem; use serde_json::Value; +use std::future::Future; use std::io::IsTerminal; use std::io::Read; use std::path::Path; @@ -439,19 +440,12 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result .cloud_requirements(cloud_requirements.clone()) .build() }; - let config = build_config(overrides.clone()).await?; - let config = if config.approvals_reviewer == ApprovalsReviewer::AutoReview - && !dangerously_bypass_approvals_and_sandbox - && !removed_full_auto - { - build_config(ConfigOverrides { - approval_policy: None, - ..overrides - }) - .await? - } else { - config - }; + let config = build_exec_config( + overrides, + dangerously_bypass_approvals_and_sandbox || removed_full_auto, + build_config, + ) + .await?; #[allow(clippy::print_stderr)] match check_execpolicy_for_warnings(&config.config_layer_stack).await { @@ -577,6 +571,41 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result .await } +async fn build_exec_config( + overrides: ConfigOverrides, + preserve_headless_approval_policy: bool, + build_config: BuildConfig, +) -> std::io::Result +where + BuildConfig: Fn(ConfigOverrides) -> BuildFuture, + BuildFuture: Future>, +{ + let build_without_headless_approval_policy = || { + build_config(ConfigOverrides { + approval_policy: None, + ..overrides.clone() + }) + }; + match build_config(overrides.clone()).await { + Ok(config) + if config.approvals_reviewer == ApprovalsReviewer::AutoReview + && !preserve_headless_approval_policy => + { + build_without_headless_approval_policy().await + } + Ok(config) => Ok(config), + Err(headless_error) if !preserve_headless_approval_policy => { + let config = build_without_headless_approval_policy().await?; + if config.approvals_reviewer == ApprovalsReviewer::AutoReview { + Ok(config) + } else { + Err(headless_error) + } + } + Err(headless_error) => Err(headless_error), + } +} + async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> { let ExecRunArgs { in_process_start_args, diff --git a/codex-rs/exec/src/lib_tests.rs b/codex-rs/exec/src/lib_tests.rs index 79d07a0b761..2ed6005df6a 100644 --- a/codex-rs/exec/src/lib_tests.rs +++ b/codex-rs/exec/src/lib_tests.rs @@ -458,6 +458,67 @@ async fn thread_start_params_include_review_policy_when_auto_review_is_enabled() ); } +#[tokio::test] +async fn build_exec_config_retries_without_invalid_headless_policy_for_auto_review() { + let codex_home = tempdir().expect("create temp codex home"); + let cwd = tempdir().expect("create temp cwd"); + std::fs::write( + codex_home.path().join("config.toml"), + r#" +approval_policy = "on-request" +approvals_reviewer = "auto_review" +"#, + ) + .expect("write config"); + let requirements_path = codex_home.path().join("requirements.toml"); + std::fs::write( + &requirements_path, + r#" +allowed_approval_policies = ["never", "on-request"] +allowed_sandbox_modes = ["read-only", "workspace-write"] +"#, + ) + .expect("write requirements"); + let mut loader_overrides = LoaderOverrides::without_managed_config_for_tests(); + loader_overrides.system_requirements_path = Some(requirements_path); + let overrides = ConfigOverrides { + cwd: Some(cwd.path().to_path_buf()), + approval_policy: Some(AskForApproval::Never), + sandbox_mode: Some(SandboxMode::DangerFullAccess), + ..Default::default() + }; + let build_config = |overrides| { + ConfigBuilder::default() + .codex_home(codex_home.path().to_path_buf()) + .loader_overrides(loader_overrides.clone()) + .harness_overrides(overrides) + .build() + }; + + let error = build_config(overrides.clone()) + .await + .expect_err("synthetic headless approval policy should fail"); + assert!( + error + .to_string() + .contains("`approval_policy = \"never\"` cannot be used") + ); + + let config = build_exec_config( + overrides, + /*preserve_headless_approval_policy*/ false, + build_config, + ) + .await + .expect("auto-review config should retry without the synthetic approval policy"); + + assert_eq!( + config.permissions.approval_policy.value(), + AskForApproval::OnRequest + ); + assert_eq!(config.approvals_reviewer, ApprovalsReviewer::AutoReview); +} + #[tokio::test] async fn thread_start_params_include_user_thread_source() { let codex_home = tempdir().expect("create temp codex home"); From dc4a1019b70ea0c234f7ff750340bd5ca1adfb5a Mon Sep 17 00:00:00 2001 From: won Date: Sun, 31 May 2026 20:38:11 -0700 Subject: [PATCH 6/6] Preserve exec error across auto-review retry --- codex-rs/exec/src/lib.rs | 10 +-- codex-rs/exec/src/lib_tests.rs | 25 +++++++ codex-rs/exec/tests/suite/approval_policy.rs | 72 ++++---------------- 3 files changed, 43 insertions(+), 64 deletions(-) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 53c6379f1b2..06ae4e64e97 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -595,11 +595,11 @@ where } Ok(config) => Ok(config), Err(headless_error) if !preserve_headless_approval_policy => { - let config = build_without_headless_approval_policy().await?; - if config.approvals_reviewer == ApprovalsReviewer::AutoReview { - Ok(config) - } else { - Err(headless_error) + match build_without_headless_approval_policy().await { + Ok(config) if config.approvals_reviewer == ApprovalsReviewer::AutoReview => { + Ok(config) + } + Ok(_) | Err(_) => Err(headless_error), } } Err(headless_error) => Err(headless_error), diff --git a/codex-rs/exec/src/lib_tests.rs b/codex-rs/exec/src/lib_tests.rs index 2ed6005df6a..37a7f7e0729 100644 --- a/codex-rs/exec/src/lib_tests.rs +++ b/codex-rs/exec/src/lib_tests.rs @@ -519,6 +519,31 @@ allowed_sandbox_modes = ["read-only", "workspace-write"] assert_eq!(config.approvals_reviewer, ApprovalsReviewer::AutoReview); } +#[tokio::test] +async fn build_exec_config_preserves_headless_error_when_retry_fails() { + let overrides = ConfigOverrides { + approval_policy: Some(AskForApproval::Never), + ..Default::default() + }; + + let error = build_exec_config( + overrides, + /*preserve_headless_approval_policy*/ false, + |overrides| async move { + let message = if overrides.approval_policy == Some(AskForApproval::Never) { + "headless error" + } else { + "retry error" + }; + Err(std::io::Error::other(message)) + }, + ) + .await + .expect_err("failed speculative retry should preserve the original error"); + + assert_eq!(error.to_string(), "headless error"); +} + #[tokio::test] async fn thread_start_params_include_user_thread_source() { let codex_home = tempdir().expect("create temp codex home"); diff --git a/codex-rs/exec/tests/suite/approval_policy.rs b/codex-rs/exec/tests/suite/approval_policy.rs index 625a26d25f7..1097d608438 100644 --- a/codex-rs/exec/tests/suite/approval_policy.rs +++ b/codex-rs/exec/tests/suite/approval_policy.rs @@ -4,8 +4,7 @@ use core_test_support::responses; use core_test_support::test_codex_exec::test_codex_exec; -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn exec_preserves_on_request_for_auto_review_config() -> anyhow::Result<()> { +async fn run_exec_with_auto_review_config(extra_args: &[&str]) -> anyhow::Result { let test = test_codex_exec(); std::fs::write( test.home_path().join("config.toml"), @@ -23,15 +22,21 @@ approvals_reviewer = "auto_review" ]); responses::mount_sse_once(&server, body).await; - let output = test - .cmd_with_server(&server) + let mut cmd = test.cmd_with_server(&server); + let output = cmd .arg("--skip-git-repo-check") + .args(extra_args) .arg("check approval mode") .output()?; assert!(output.status.success(), "exec run failed: {output:?}"); - let stderr = String::from_utf8(output.stderr)?; + Ok(String::from_utf8(output.stderr)?) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn exec_preserves_on_request_for_auto_review_config() -> anyhow::Result<()> { + let stderr = run_exec_with_auto_review_config(&[]).await?; assert!( stderr.contains("approval: on-request"), "stderr missing preserved auto-review approval mode: {stderr}" @@ -42,33 +47,8 @@ approvals_reviewer = "auto_review" #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_bypass_preserves_never_for_auto_review_config() -> anyhow::Result<()> { - let test = test_codex_exec(); - std::fs::write( - test.home_path().join("config.toml"), - r#" -approval_policy = "on-request" -approvals_reviewer = "auto_review" -"#, - )?; - - let server = responses::start_mock_server().await; - let body = responses::sse(vec![ - responses::ev_response_created("response_1"), - responses::ev_assistant_message("response_1", "done"), - responses::ev_completed("response_1"), - ]); - responses::mount_sse_once(&server, body).await; - - let output = test - .cmd_with_server(&server) - .arg("--skip-git-repo-check") - .arg("--dangerously-bypass-approvals-and-sandbox") - .arg("check approval mode") - .output()?; - - assert!(output.status.success(), "exec run failed: {output:?}"); - - let stderr = String::from_utf8(output.stderr)?; + let stderr = + run_exec_with_auto_review_config(&["--dangerously-bypass-approvals-and-sandbox"]).await?; assert!( stderr.contains("approval: never"), "stderr missing bypass approval mode: {stderr}" @@ -79,33 +59,7 @@ approvals_reviewer = "auto_review" #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_full_auto_preserves_never_for_auto_review_config() -> anyhow::Result<()> { - let test = test_codex_exec(); - std::fs::write( - test.home_path().join("config.toml"), - r#" -approval_policy = "on-request" -approvals_reviewer = "auto_review" -"#, - )?; - - let server = responses::start_mock_server().await; - let body = responses::sse(vec![ - responses::ev_response_created("response_1"), - responses::ev_assistant_message("response_1", "done"), - responses::ev_completed("response_1"), - ]); - responses::mount_sse_once(&server, body).await; - - let output = test - .cmd_with_server(&server) - .arg("--skip-git-repo-check") - .arg("--full-auto") - .arg("check approval mode") - .output()?; - - assert!(output.status.success(), "exec run failed: {output:?}"); - - let stderr = String::from_utf8(output.stderr)?; + let stderr = run_exec_with_auto_review_config(&["--full-auto"]).await?; assert!( stderr.contains("approval: never"), "stderr missing full-auto approval mode: {stderr}"