From 1dae5788e177e8417505ab89422a17916123a7a1 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 28 Apr 2026 17:17:47 -0700 Subject: [PATCH 1/2] core tests: migrate rmcp turns to profiles (#20037) ## Summary - Removes `SandboxPolicy` from the RMCP client test suite. - Adds shared read-only user-turn helpers that submit `PermissionProfile::read_only()` plus the legacy compatibility projection required by the current `Op::UserTurn` shape. - Keeps sandbox metadata assertions intact by deriving the expected legacy `sandboxPolicy` value from the same read-only profile used for the turn. ## Verification - `cargo check -p codex-core --tests` - `just fmt` --- codex-rs/core/tests/suite/rmcp_client.rs | 322 +++++------------------ 1 file changed, 72 insertions(+), 250 deletions(-) diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index a2e15654e6..0947f4fba7 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -29,6 +29,7 @@ use codex_mcp::MCP_SANDBOX_STATE_META_CAPABILITY; use codex_models_manager::manager::RefreshStrategy; use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::models::PermissionProfile; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::InputModality; use codex_protocol::openai_models::ModelInfo; @@ -41,7 +42,6 @@ use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::McpInvocation; use codex_protocol::protocol::McpToolCallBeginEvent; use codex_protocol::protocol::Op; -use codex_protocol::protocol::SandboxPolicy; use codex_protocol::user_input::UserInput; use codex_utils_cargo_bin::cargo_bin; use core_test_support::assert_regex_match; @@ -53,6 +53,7 @@ use core_test_support::skip_if_no_network; use core_test_support::stdio_server_bin; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::test_codex; +use core_test_support::test_codex::turn_permission_fields; use core_test_support::wait_for_event; use core_test_support::wait_for_event_with_timeout; use reqwest::Client; @@ -92,6 +93,39 @@ fn assert_wall_time_header(output: &str) { assert_eq!(marker, "Output:"); } +fn read_only_user_turn(fixture: &TestCodex, text: impl Into) -> Op { + read_only_user_turn_with_model(fixture, text, fixture.session_configured.model.clone()) +} + +fn read_only_user_turn_with_model( + fixture: &TestCodex, + text: impl Into, + model: String, +) -> Op { + let cwd = fixture.cwd.path().to_path_buf(); + let (sandbox_policy, permission_profile) = + turn_permission_fields(PermissionProfile::read_only(), cwd.as_path()); + Op::UserTurn { + items: vec![UserInput::Text { + text: text.into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd, + approval_policy: AskForApproval::Never, + approvals_reviewer: None, + sandbox_policy, + permission_profile, + model, + effort: None, + summary: None, + service_tier: None, + collaboration_mode: None, + personality: None, + environments: None, + } +} + #[derive(Debug, PartialEq, Eq)] enum McpCallEvent { Begin(String), @@ -321,28 +355,9 @@ async fn call_cwd_tool( ) .await; - let session_model = fixture.session_configured.model.clone(); fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp cwd tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.config.cwd.to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(fixture, "call the rmcp cwd tool")) .await?; wait_for_event(&fixture.codex, |ev| { @@ -455,29 +470,9 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> { }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(&fixture, "call the rmcp echo tool")) .await?; let begin_event = wait_for_event(&fixture.codex, |ev| { @@ -797,9 +792,11 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta() -> anyhow::Result<()> sleep(Duration::from_millis(200)).await; } - let sandbox_policy = SandboxPolicy::new_read_only_policy(); fixture - .submit_turn_with_policy("call the rmcp sandbox_meta tool", sandbox_policy.clone()) + .submit_turn_with_permission_profile( + "call the rmcp sandbox_meta tool", + PermissionProfile::read_only(), + ) .await?; let request = call_mock.single_request(); @@ -824,6 +821,8 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta() -> anyhow::Result<()> let sandbox_meta = meta .get(MCP_SANDBOX_STATE_META_CAPABILITY) .expect("sandbox state metadata should be present"); + let (sandbox_policy, _) = + turn_permission_fields(PermissionProfile::read_only(), fixture.config.cwd.as_path()); let expected_sandbox_policy = serde_json::to_value(&sandbox_policy)?; assert_eq!( sandbox_meta.get("sandboxPolicy"), @@ -888,29 +887,12 @@ async fn stdio_mcp_parallel_tool_calls_default_false_runs_serially() -> anyhow:: }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp sync tool twice".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn( + &fixture, + "call the rmcp sync tool twice", + )) .await?; let mut call_events = Vec::new(); @@ -1022,29 +1004,12 @@ async fn stdio_mcp_parallel_tool_calls_opt_in_runs_concurrently() -> anyhow::Res }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp sync tool twice".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn( + &fixture, + "call the rmcp sync tool twice", + )) .await?; wait_for_event(&fixture.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -1121,31 +1086,11 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> { }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - wait_for_mcp_tool(&fixture, &tool_name).await?; fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp image tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(&fixture, "call the rmcp image tool")) .await?; // Wait for tool begin/end and final completion. @@ -1274,31 +1219,14 @@ async fn stdio_image_responses_preserve_original_detail_metadata() -> anyhow::Re }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - wait_for_mcp_tool(&fixture, &tool_name).await?; fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp image_scenario tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn( + &fixture, + "call the rmcp image_scenario tool", + )) .await?; wait_for_event(&fixture.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -1435,25 +1363,11 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp image tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: text_only_model_slug.to_string(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn_with_model( + &fixture, + "call the rmcp image tool", + text_only_model_slug.to_string(), + )) .await?; wait_for_event(&fixture.codex, |ev| { @@ -1541,29 +1455,9 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> { }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(&fixture, "call the rmcp echo tool")) .await?; let begin_event = wait_for_event(&fixture.codex, |ev| { @@ -1680,28 +1574,9 @@ async fn stdio_server_propagates_explicit_local_env_var_source() -> anyhow::Resu .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(&fixture, "call the rmcp echo tool")) .await?; wait_for_event(&fixture.codex, |ev| { @@ -1791,28 +1666,9 @@ async fn remote_stdio_env_var_source_does_not_copy_local_env() -> anyhow::Result .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn(&fixture, "call the rmcp echo tool")) .await?; wait_for_event(&fixture.codex, |ev| { @@ -1992,30 +1848,13 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> { }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - // Phase 4: submit the user turn that should trigger the MCP tool call. fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp streamable http echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn( + &fixture, + "call the rmcp streamable http echo tool", + )) .await?; // Phase 5: assert Codex begins the expected tool invocation. @@ -2196,8 +2035,6 @@ async fn streamable_http_with_oauth_round_trip_impl() -> anyhow::Result<()> { }) .build_remote_aware(&server) .await?; - let session_model = fixture.session_configured.model.clone(); - // Phase 5: wait for MCP discovery to publish the expected tool before the // turn is submitted, which keeps failures tied to server startup/discovery. wait_for_mcp_tool(&fixture, &tool_name).await?; @@ -2205,25 +2042,10 @@ async fn streamable_http_with_oauth_round_trip_impl() -> anyhow::Result<()> { // Phase 6: submit the user turn that should invoke the OAuth-backed tool. fixture .codex - .submit(Op::UserTurn { - items: vec![UserInput::Text { - text: "call the rmcp streamable http oauth echo tool".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: fixture.cwd.path().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::new_read_only_policy(), - permission_profile: None, - model: session_model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - environments: None, - }) + .submit(read_only_user_turn( + &fixture, + "call the rmcp streamable http oauth echo tool", + )) .await?; // Phase 7: assert Codex begins the expected tool invocation. From 3e430a84317aa3768260ba9c5a59a03d9d7a899f Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 28 Apr 2026 17:17:54 -0700 Subject: [PATCH 2/2] core tests: migrate apply patch turns to profiles --- codex-rs/core/tests/suite/apply_patch_cli.rs | 219 +++---------------- 1 file changed, 25 insertions(+), 194 deletions(-) diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index 4bb3be6631..2d931efb38 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -13,11 +13,9 @@ use std::sync::atomic::Ordering; use std::time::Duration; use codex_features::Feature; -use codex_protocol::protocol::AskForApproval; +use codex_protocol::models::PermissionProfile; +use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::EventMsg; -use codex_protocol::protocol::Op; -use codex_protocol::protocol::SandboxPolicy; -use codex_protocol::user_input::UserInput; #[cfg(target_os = "linux")] use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0; use core_test_support::assert_regex_match; @@ -59,6 +57,15 @@ async fn apply_patch_harness_with( Box::pin(TestCodexHarness::with_remote_aware_builder(builder)).await } +fn restrictive_workspace_write_profile() -> PermissionProfile { + PermissionProfile::workspace_write_with( + &[], + NetworkSandboxPolicy::Restricted, + /*exclude_tmpdir_env_var*/ true, + /*exclude_slash_tmp*/ true, + ) +} + pub async fn mount_apply_patch( harness: &TestCodexHarness, call_id: &str, @@ -354,28 +361,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff( let call_id = "apply-move-no-change"; mount_apply_patch(&harness, call_id, patch, "ok", model_output).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "rename without content change".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("rename without content change").await?; let mut saw_turn_diff = false; wait_for_event(&codex, |event| match event { @@ -641,16 +627,10 @@ async fn apply_patch_cli_rejects_path_traversal_outside_workspace( let call_id = "apply-path-traversal"; mount_apply_patch(&harness, call_id, patch, "fail", model_output).await; - let sandbox_policy = SandboxPolicy::WorkspaceWrite { - writable_roots: vec![], - network_access: false, - exclude_tmpdir_env_var: true, - exclude_slash_tmp: true, - }; harness - .submit_with_policy( + .submit_with_permission_profile( "attempt to escape workspace via apply_patch", - sandbox_policy, + restrictive_workspace_write_profile(), ) .await?; @@ -696,14 +676,11 @@ async fn apply_patch_cli_rejects_move_path_traversal_outside_workspace( let call_id = "apply-move-traversal"; mount_apply_patch(&harness, call_id, patch, "fail", model_output).await; - let sandbox_policy = SandboxPolicy::WorkspaceWrite { - writable_roots: vec![], - network_access: false, - exclude_tmpdir_env_var: true, - exclude_slash_tmp: true, - }; harness - .submit_with_policy("attempt move traversal via apply_patch", sandbox_policy) + .submit_with_permission_profile( + "attempt move traversal via apply_patch", + restrictive_workspace_write_profile(), + ) .await?; let out = harness.apply_patch_output(call_id, model_output).await; @@ -992,27 +969,7 @@ async fn apply_patch_custom_tool_streaming_emits_updated_changes() -> Result<()> ) .await; - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "create streamed file".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: test.session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("create streamed file").await?; let mut updates = Vec::new(); wait_for_event(&codex, |event| match event { @@ -1090,28 +1047,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<( ]; mount_sse_sequence(harness.server(), bodies).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "apply via shell heredoc with cd".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("apply via shell heredoc with cd").await?; let mut saw_turn_diff = None; let mut saw_patch_begin = false; @@ -1176,28 +1112,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() -> ]; mount_sse_sequence(harness.server(), bodies).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "apply patch via shell".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("apply patch via shell").await?; let mut saw_turn_diff = false; wait_for_event(&codex, |event| match event { @@ -1333,28 +1248,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff( let patch = format!("*** Begin Patch\n*** Add File: {file}\n+hello\n*** End Patch\n"); mount_apply_patch(&harness, call_id, patch.as_str(), "ok", model_output).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "emit diff".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("emit diff").await?; let mut saw_turn_diff = None; wait_for_event(&codex, |event| match event { @@ -1402,28 +1296,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change( let patch = "*** Begin Patch\n*** Update File: old.txt\n*** Move to: new.txt\n@@\n-old\n+new\n*** End Patch"; mount_apply_patch(&harness, call_id, patch, "ok", model_output).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "rename with change".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("rename with change").await?; let mut last_diff: Option = None; wait_for_event(&codex, |event| match event { @@ -1480,28 +1353,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()> ]); mount_sse_sequence(harness.server(), vec![s1, s2, s3]).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "aggregate diffs".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("aggregate diffs").await?; let mut last_diff: Option = None; wait_for_event(&codex, |event| match event { @@ -1558,28 +1410,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result ]; mount_sse_sequence(harness.server(), responses).await; - let model = test.session_configured.model.clone(); - codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "apply patch twice with failure".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: harness.cwd().to_path_buf(), - approval_policy: AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: SandboxPolicy::DangerFullAccess, - permission_profile: None, - model, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - }) - .await?; + harness.submit("apply patch twice with failure").await?; let mut last_diff: Option = None; wait_for_event_with_timeout(