From 8d3992d830fd9db59180c00fa7baea8e7a568e3a Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 28 Apr 2026 17:11:17 -0700 Subject: [PATCH 1/2] core tests: migrate plan item turns to profiles (#20026) ## Why The core item tests still had a cluster of plan-mode `Op::UserTurn` literals that used `SandboxPolicy::DangerFullAccess` and omitted `permission_profile`. These tests are validating emitted item lifecycle events, so keeping them on the legacy sandbox-only turn shape adds noise to the broader permissions migration without testing legacy behavior. ## What Changed - Adds a local `disabled_plan_turn()` helper that preserves the existing `std::env::current_dir()` turn cwd behavior. - Uses `turn_permission_fields(PermissionProfile::Disabled, cwd)` to populate both the compatibility `sandbox_policy` and canonical `permission_profile` fields. - Replaces the plan-mode hand-built turns in `codex-rs/core/tests/suite/items.rs`, removing all `SandboxPolicy` references from that file and reducing remaining `codex-rs/core/tests` `SandboxPolicy` files from 16 to 15. ## Verification - `cargo check -p codex-core --tests` --- codex-rs/core/tests/suite/items.rs | 152 +++++++++++------------------ 1 file changed, 57 insertions(+), 95 deletions(-) diff --git a/codex-rs/core/tests/suite/items.rs b/codex-rs/core/tests/suite/items.rs index db4f7d7584..053f03ca90 100644 --- a/codex-rs/core/tests/suite/items.rs +++ b/codex-rs/core/tests/suite/items.rs @@ -6,7 +6,9 @@ use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Settings; use codex_protocol::items::AgentMessageContent; use codex_protocol::items::TurnItem; +use codex_protocol::models::PermissionProfile; use codex_protocol::models::WebSearchAction; +use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ItemCompletedEvent; use codex_protocol::protocol::ItemStartedEvent; @@ -33,12 +35,42 @@ use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; 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_match; use pretty_assertions::assert_eq; use std::path::Path; use std::path::PathBuf; +fn disabled_plan_turn( + text: &str, + model: String, + collaboration_mode: CollaborationMode, +) -> anyhow::Result { + let cwd = std::env::current_dir()?; + let (sandbox_policy, permission_profile) = + turn_permission_fields(PermissionProfile::Disabled, cwd.as_path()); + Ok(Op::UserTurn { + environments: None, + 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: Some(collaboration_mode), + personality: None, + }) +} + fn image_generation_artifact_path(codex_home: &Path, session_id: &str, call_id: &str) -> PathBuf { fn sanitize(value: &str) -> String { let mut sanitized: String = value @@ -529,25 +561,11 @@ async fn plan_mode_emits_plan_item_from_proposed_plan_block() -> anyhow::Result< }; codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "please plan".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: std::env::current_dir()?, - approval_policy: codex_protocol::protocol::AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: Some(collaboration_mode), - personality: None, - }) + .submit(disabled_plan_turn( + "please plan", + session_configured.model.clone(), + collaboration_mode, + )?) .await?; let plan_delta = wait_for_event_match(&codex, |ev| match ev { @@ -608,25 +626,11 @@ async fn plan_mode_strips_plan_from_agent_messages() -> anyhow::Result<()> { }; codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "please plan".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: std::env::current_dir()?, - approval_policy: codex_protocol::protocol::AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: Some(collaboration_mode), - personality: None, - }) + .submit(disabled_plan_turn( + "please plan", + session_configured.model.clone(), + collaboration_mode, + )?) .await?; let mut agent_deltas = Vec::new(); @@ -719,25 +723,11 @@ async fn plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done }; codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "please plan with citations".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: std::env::current_dir()?, - approval_policy: codex_protocol::protocol::AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: Some(collaboration_mode), - personality: None, - }) + .submit(disabled_plan_turn( + "please plan with citations", + session_configured.model.clone(), + collaboration_mode, + )?) .await?; let mut agent_started = None; @@ -908,25 +898,11 @@ async fn plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_p }; codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "please plan".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: std::env::current_dir()?, - approval_policy: codex_protocol::protocol::AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: Some(collaboration_mode), - personality: None, - }) + .submit(disabled_plan_turn( + "please plan", + session_configured.model.clone(), + collaboration_mode, + )?) .await?; let mut agent_started = None; @@ -1024,25 +1000,11 @@ async fn plan_mode_handles_missing_plan_close_tag() -> anyhow::Result<()> { }; codex - .submit(Op::UserTurn { - environments: None, - items: vec![UserInput::Text { - text: "please plan".into(), - text_elements: Vec::new(), - }], - final_output_json_schema: None, - cwd: std::env::current_dir()?, - approval_policy: codex_protocol::protocol::AskForApproval::Never, - approvals_reviewer: None, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, - permission_profile: None, - model: session_configured.model.clone(), - effort: None, - summary: None, - service_tier: None, - collaboration_mode: Some(collaboration_mode), - personality: None, - }) + .submit(disabled_plan_turn( + "please plan", + session_configured.model.clone(), + collaboration_mode, + )?) .await?; let mut plan_delta = None; From 33d0b61573a8af9ecac6824749b89beeae9d408b Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 28 Apr 2026 17:11:20 -0700 Subject: [PATCH 2/2] core tests: migrate tools tests to permission profiles --- codex-rs/core/tests/suite/tools.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/codex-rs/core/tests/suite/tools.rs b/codex-rs/core/tests/suite/tools.rs index 46195e4dd0..b856074540 100644 --- a/codex-rs/core/tests/suite/tools.rs +++ b/codex-rs/core/tests/suite/tools.rs @@ -9,7 +9,6 @@ use std::time::Instant; use anyhow::Context; use anyhow::Result; -use codex_config::Constrained; use codex_config::types::McpServerConfig; use codex_config::types::McpServerTransportConfig; use codex_core::sandboxing::SandboxPermissions; @@ -21,7 +20,6 @@ use codex_protocol::permissions::FileSystemSandboxEntry; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::AskForApproval; -use codex_protocol::protocol::SandboxPolicy; use codex_protocol::protocol::TurnEnvironmentSelection; use core_test_support::assert_regex_match; use core_test_support::responses::ev_assistant_message; @@ -547,14 +545,9 @@ async fn shell_enforces_glob_deny_read_policy() -> Result<()> { skip_if_sandbox!(Ok(())); let server = start_mock_server().await; - let read_only_policy = SandboxPolicy::new_read_only_policy(); - let read_only_policy_for_config = read_only_policy.clone(); let mut builder = test_codex() .with_model("gpt-5.4") .with_config(move |config| { - config - .set_legacy_sandbox_policy(read_only_policy_for_config) - .expect("set sandbox policy"); let mut file_system_sandbox_policy = FileSystemSandboxPolicy::default(); file_system_sandbox_policy .entries @@ -564,11 +557,13 @@ async fn shell_enforces_glob_deny_read_policy() -> Result<()> { }, access: FileSystemAccessMode::None, }); - config.permissions.permission_profile = - Constrained::allow_any(PermissionProfile::from_runtime_permissions( + config + .permissions + .set_permission_profile(PermissionProfile::from_runtime_permissions( &file_system_sandbox_policy, NetworkSandboxPolicy::Restricted, - )); + )) + .expect("set permission profile"); }); let fixture = builder.build(&server).await?; @@ -608,8 +603,9 @@ async fn shell_enforces_glob_deny_read_policy() -> Result<()> { ]; let mock = mount_sse_sequence(&server, responses).await; + let permission_profile = fixture.session_configured.permission_profile.clone(); fixture - .submit_turn_with_policy("read the fixture files", read_only_policy) + .submit_turn_with_permission_profile("read the fixture files", permission_profile) .await?; let output_text = mock