diff --git a/AGENTS.md b/AGENTS.md index af25482795..e5520d3266 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,6 @@ In the codex-rs folder where the rust code lives: - You operate in a sandbox where `CODEX_SANDBOX_NETWORK_DISABLED=1` will be set whenever you use the `shell` tool. Any existing code that uses `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` was authored with this fact in mind. It is often used to early exit out of tests that the author knew you would not be able to run given your sandbox limitations. - Similarly, when you spawn a process using Seatbelt (`/usr/bin/sandbox-exec`), `CODEX_SANDBOX=seatbelt` will be set on the child process. Integration tests that want to run Seatbelt themselves cannot be run under Seatbelt, so checks for `CODEX_SANDBOX=seatbelt` are also often used to early exit out of tests, as appropriate. -Before creating a pull request with changes to `codex-rs`, run `just fmt` (in `codex-rs` directory) to format the code and `just fix` (in `codex-rs` directory) to fix any linter issues in the code, ensure the test suite passes by running `cargo test --all-features` in the `codex-rs` directory. - -When making individual changes prefer running tests on individual files or projects first. +Before finalizing a change to `codex-rs`, run `just fmt` (in `codex-rs` directory) to format the code and `just fix` (in `codex-rs` directory) to fix any linter issues in the code. Additionally, run the tests: +1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`. +2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test --all-features`. diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index 262d219d6d..87df66dd23 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -166,7 +166,7 @@ impl ApplyPatchAction { panic!("path must be absolute"); } - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] let filename = path .file_name() .expect("path should not be empty") @@ -179,7 +179,7 @@ impl ApplyPatchAction { *** End Patch"#, ); let changes = HashMap::from([(path.to_path_buf(), ApplyPatchFileChange::Add { content })]); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] Self { changes, cwd: path @@ -682,8 +682,6 @@ pub fn print_summary( #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] - use super::*; use pretty_assertions::assert_eq; use std::fs; diff --git a/codex-rs/apply-patch/src/parser.rs b/codex-rs/apply-patch/src/parser.rs index 44c5b14619..ff9dfd6f8f 100644 --- a/codex-rs/apply-patch/src/parser.rs +++ b/codex-rs/apply-patch/src/parser.rs @@ -427,7 +427,6 @@ fn parse_update_file_chunk( } #[test] -#[allow(clippy::unwrap_used)] fn test_parse_patch() { assert_eq!( parse_patch_text("bad", ParseMode::Strict), diff --git a/codex-rs/chatgpt/tests/apply_command_e2e.rs b/codex-rs/chatgpt/tests/apply_command_e2e.rs index f1a35e1521..2aa8b809bb 100644 --- a/codex-rs/chatgpt/tests/apply_command_e2e.rs +++ b/codex-rs/chatgpt/tests/apply_command_e2e.rs @@ -1,5 +1,3 @@ -#![expect(clippy::expect_used)] - use codex_chatgpt::apply_command::apply_diff_from_task; use codex_chatgpt::get_task::GetTaskResponse; use std::path::Path; diff --git a/codex-rs/clippy.toml b/codex-rs/clippy.toml new file mode 100644 index 0000000000..9e614bec42 --- /dev/null +++ b/codex-rs/clippy.toml @@ -0,0 +1,2 @@ +allow-expect-in-tests = true +allow-unwrap-in-tests = true \ No newline at end of file diff --git a/codex-rs/common/src/config_override.rs b/codex-rs/common/src/config_override.rs index c9b18edc7c..6b77099524 100644 --- a/codex-rs/common/src/config_override.rs +++ b/codex-rs/common/src/config_override.rs @@ -142,7 +142,6 @@ fn parse_toml_value(raw: &str) -> Result { } #[cfg(all(test, feature = "cli"))] -#[allow(clippy::expect_used, clippy::unwrap_used)] mod tests { use super::*; diff --git a/codex-rs/core/src/apply_patch.rs b/codex-rs/core/src/apply_patch.rs index 21e80406e5..fcccb40f8c 100644 --- a/codex-rs/core/src/apply_patch.rs +++ b/codex-rs/core/src/apply_patch.rs @@ -8,7 +8,6 @@ use crate::safety::assess_patch_safety; use codex_apply_patch::ApplyPatchAction; use codex_apply_patch::ApplyPatchFileChange; use std::collections::HashMap; -use std::path::Path; use std::path::PathBuf; pub const CODEX_APPLY_PATCH_ARG1: &str = "--codex-run-as-apply-patch"; @@ -45,12 +44,10 @@ pub(crate) async fn apply_patch( call_id: &str, action: ApplyPatchAction, ) -> InternalApplyPatchInvocation { - let writable_roots_snapshot = sess.get_writable_roots().to_vec(); - match assess_patch_safety( &action, sess.get_approval_policy(), - &writable_roots_snapshot, + sess.get_sandbox_policy(), sess.get_cwd(), ) { SafetyCheck::AutoApprove { .. } => { @@ -124,30 +121,3 @@ pub(crate) fn convert_apply_patch_to_protocol( } result } - -pub(crate) fn get_writable_roots(cwd: &Path) -> Vec { - let mut writable_roots = Vec::new(); - if cfg!(target_os = "macos") { - // On macOS, $TMPDIR is private to the user. - writable_roots.push(std::env::temp_dir()); - - // Allow pyenv to update its shims directory. Without this, any tool - // that happens to be managed by `pyenv` will fail with an error like: - // - // pyenv: cannot rehash: $HOME/.pyenv/shims isn't writable - // - // which is emitted every time `pyenv` tries to run `rehash` (for - // example, after installing a new Python package that drops an entry - // point). Although the sandbox is intentionally read‑only by default, - // writing to the user's local `pyenv` directory is safe because it - // is already user‑writable and scoped to the current user account. - if let Ok(home_dir) = std::env::var("HOME") { - let pyenv_dir = PathBuf::from(home_dir).join(".pyenv"); - writable_roots.push(pyenv_dir); - } - } - - writable_roots.push(cwd.to_path_buf()); - - writable_roots -} diff --git a/codex-rs/core/src/bash.rs b/codex-rs/core/src/bash.rs index b9cd444356..5b94daf252 100644 --- a/codex-rs/core/src/bash.rs +++ b/codex-rs/core/src/bash.rs @@ -132,7 +132,6 @@ fn parse_plain_command_from_node(cmd: tree_sitter::Node, src: &str) -> Option Option>> { diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 135782bc31..3d26bd0880 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -609,8 +609,6 @@ fn try_parse_retry_after(err: &Error) -> Option { #[cfg(test)] mod tests { - #![allow(clippy::expect_used, clippy::unwrap_used)] - use super::*; use serde_json::json; use tokio::sync::mpsc; diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index d8684648f8..c3489200c9 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -187,7 +187,6 @@ impl Stream for ResponseStream { #[cfg(test)] mod tests { - #![allow(clippy::expect_used)] use crate::model_family::find_family_for_model; use super::*; diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 098ff115fa..194ac6a922 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -1,5 +1,5 @@ // Poisoned mutex should fail the program -#![allow(clippy::unwrap_used)] +#![expect(clippy::unwrap_used)] use std::borrow::Cow; use std::collections::HashMap; @@ -31,12 +31,11 @@ use tracing::warn; use uuid::Uuid; use crate::ModelProviderInfo; +use crate::apply_patch; use crate::apply_patch::ApplyPatchExec; use crate::apply_patch::CODEX_APPLY_PATCH_ARG1; use crate::apply_patch::InternalApplyPatchInvocation; use crate::apply_patch::convert_apply_patch_to_protocol; -use crate::apply_patch::get_writable_roots; -use crate::apply_patch::{self}; use crate::client::ModelClient; use crate::client_common::Prompt; use crate::client_common::ResponseEvent; @@ -230,7 +229,6 @@ pub(crate) struct Session { approval_policy: AskForApproval, sandbox_policy: SandboxPolicy, shell_environment_policy: ShellEnvironmentPolicy, - writable_roots: Vec, disable_response_storage: bool, tools_config: ToolsConfig, @@ -409,8 +407,6 @@ impl Session { state.history.record_items(&restored_items); } - let writable_roots = get_writable_roots(&cwd); - // Handle MCP manager result and record any startup failures. let (mcp_connection_manager, failed_clients) = match mcp_res { Ok((mgr, failures)) => (mgr, failures), @@ -463,7 +459,6 @@ impl Session { sandbox_policy, shell_environment_policy: config.shell_environment_policy.clone(), cwd, - writable_roots, mcp_connection_manager, notify, state: Mutex::new(state), @@ -507,14 +502,14 @@ impl Session { Ok(sess) } - pub(crate) fn get_writable_roots(&self) -> &[PathBuf] { - &self.writable_roots - } - pub(crate) fn get_approval_policy(&self) -> AskForApproval { self.approval_policy } + pub(crate) fn get_sandbox_policy(&self) -> &SandboxPolicy { + &self.sandbox_policy + } + pub(crate) fn get_cwd(&self) -> &Path { &self.cwd } diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index f9c15b9eed..b17bb80815 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -765,7 +765,6 @@ pub fn log_dir(cfg: &Config) -> std::io::Result { #[cfg(test)] mod tests { - #![allow(clippy::expect_used, clippy::unwrap_used)] use crate::config_types::HistoryPersistence; use super::*; diff --git a/codex-rs/core/src/exec_env.rs b/codex-rs/core/src/exec_env.rs index 2957f3da15..88246b063c 100644 --- a/codex-rs/core/src/exec_env.rs +++ b/codex-rs/core/src/exec_env.rs @@ -70,8 +70,6 @@ where #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used, clippy::expect_used)] - use super::*; use crate::config_types::ShellEnvironmentPolicyInherit; use maplit::hashmap; diff --git a/codex-rs/core/src/git_info.rs b/codex-rs/core/src/git_info.rs index 52d029f669..09a59b7b8c 100644 --- a/codex-rs/core/src/git_info.rs +++ b/codex-rs/core/src/git_info.rs @@ -99,9 +99,6 @@ async fn run_git_command_with_timeout(args: &[&str], cwd: &Path) -> Option) -> bool { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; fn vec_str(args: &[&str]) -> Vec { diff --git a/codex-rs/core/src/mcp_connection_manager.rs b/codex-rs/core/src/mcp_connection_manager.rs index 2e33c8754b..b5813a0462 100644 --- a/codex-rs/core/src/mcp_connection_manager.rs +++ b/codex-rs/core/src/mcp_connection_manager.rs @@ -281,7 +281,6 @@ fn is_valid_mcp_server_name(server_name: &str) -> bool { } #[cfg(test)] -#[allow(clippy::unwrap_used)] mod tests { use super::*; use mcp_types::ToolInputSchema; diff --git a/codex-rs/core/src/model_provider_info.rs b/codex-rs/core/src/model_provider_info.rs index 98f07deb1e..00eabc539c 100644 --- a/codex-rs/core/src/model_provider_info.rs +++ b/codex-rs/core/src/model_provider_info.rs @@ -322,7 +322,6 @@ pub fn create_oss_provider_with_base_url(base_url: &str) -> ModelProviderInfo { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; use pretty_assertions::assert_eq; diff --git a/codex-rs/core/src/models.rs b/codex-rs/core/src/models.rs index da1e31b706..5e67fc381a 100644 --- a/codex-rs/core/src/models.rs +++ b/codex-rs/core/src/models.rs @@ -266,7 +266,6 @@ impl std::ops::Deref for FunctionCallOutputPayload { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; #[test] diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs index ad794c38f1..7c65880433 100644 --- a/codex-rs/core/src/openai_tools.rs +++ b/codex-rs/core/src/openai_tools.rs @@ -470,7 +470,6 @@ pub(crate) fn get_openai_tools( } #[cfg(test)] -#[allow(clippy::expect_used)] mod tests { use crate::model_family::find_family_for_model; use mcp_types::ToolInputSchema; diff --git a/codex-rs/core/src/project_doc.rs b/codex-rs/core/src/project_doc.rs index 9f46159d1d..3591b5b1d8 100644 --- a/codex-rs/core/src/project_doc.rs +++ b/codex-rs/core/src/project_doc.rs @@ -134,8 +134,6 @@ async fn load_first_candidate( #[cfg(test)] mod tests { - #![allow(clippy::expect_used, clippy::unwrap_used)] - use super::*; use crate::config::ConfigOverrides; use crate::config::ConfigToml; diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs index 0767ccdb95..4d7479bee7 100644 --- a/codex-rs/core/src/protocol.rs +++ b/codex-rs/core/src/protocol.rs @@ -183,10 +183,31 @@ pub enum SandboxPolicy { /// not modified by the agent. #[derive(Debug, Clone, PartialEq, Eq)] pub struct WritableRoot { + /// Absolute path, by construction. pub root: PathBuf, + + /// Also absolute paths, by construction. pub read_only_subpaths: Vec, } +impl WritableRoot { + pub(crate) fn is_path_writable(&self, path: &Path) -> bool { + // Check if the path is under the root. + if !path.starts_with(&self.root) { + return false; + } + + // Check if the path is under any of the read-only subpaths. + for subpath in &self.read_only_subpaths { + if path.starts_with(subpath) { + return false; + } + } + + true + } +} + impl FromStr for SandboxPolicy { type Err = serde_json::Error; @@ -724,7 +745,6 @@ pub struct Chunk { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; /// Serialize Event to verify that its JSON representation has the expected diff --git a/codex-rs/core/src/safety.rs b/codex-rs/core/src/safety.rs index 860a728def..066be93424 100644 --- a/codex-rs/core/src/safety.rs +++ b/codex-rs/core/src/safety.rs @@ -21,7 +21,7 @@ pub enum SafetyCheck { pub fn assess_patch_safety( action: &ApplyPatchAction, policy: AskForApproval, - writable_roots: &[PathBuf], + sandbox_policy: &SandboxPolicy, cwd: &Path, ) -> SafetyCheck { if action.is_empty() { @@ -45,7 +45,7 @@ pub fn assess_patch_safety( // is possible that paths in the patch are hard links to files outside the // writable roots, so we should still run `apply_patch` in a sandbox in that // case. - if is_write_patch_constrained_to_writable_paths(action, writable_roots, cwd) + if is_write_patch_constrained_to_writable_paths(action, sandbox_policy, cwd) || policy == AskForApproval::OnFailure { // Only auto‑approve when we can actually enforce a sandbox. Otherwise @@ -171,13 +171,19 @@ pub fn get_platform_sandbox() -> Option { fn is_write_patch_constrained_to_writable_paths( action: &ApplyPatchAction, - writable_roots: &[PathBuf], + sandbox_policy: &SandboxPolicy, cwd: &Path, ) -> bool { // Early‑exit if there are no declared writable roots. - if writable_roots.is_empty() { - return false; - } + let writable_roots = match sandbox_policy { + SandboxPolicy::ReadOnly => { + return false; + } + SandboxPolicy::DangerFullAccess => { + return true; + } + SandboxPolicy::WorkspaceWrite { .. } => sandbox_policy.get_writable_roots_with_cwd(cwd), + }; // Normalize a path by removing `.` and resolving `..` without touching the // filesystem (works even if the file does not exist). @@ -209,15 +215,9 @@ fn is_write_patch_constrained_to_writable_paths( None => return false, }; - writable_roots.iter().any(|root| { - let root_abs = if root.is_absolute() { - root.clone() - } else { - normalize(&cwd.join(root)).unwrap_or_else(|| cwd.join(root)) - }; - - abs.starts_with(&root_abs) - }) + writable_roots + .iter() + .any(|writable_root| writable_root.is_path_writable(&abs)) }; for (path, change) in action.changes() { @@ -245,40 +245,58 @@ fn is_write_patch_constrained_to_writable_paths( #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; #[test] fn test_writable_roots_constraint() { - let cwd = std::env::current_dir().unwrap(); + use tempfile::TempDir; + + // Use a temporary directory as our workspace to avoid touching + // the real current working directory. + let tmp = TempDir::new().unwrap(); + let cwd = tmp.path().to_path_buf(); let parent = cwd.parent().unwrap().to_path_buf(); - // Helper to build a single‑entry map representing a patch that adds a - // file at `p`. + // Helper to build a single‑entry patch that adds a file at `p`. let make_add_change = |p: PathBuf| ApplyPatchAction::new_add_for_test(&p, "".to_string()); let add_inside = make_add_change(cwd.join("inner.txt")); let add_outside = make_add_change(parent.join("outside.txt")); + // Policy limited to the workspace only; exclude system temp roots so + // only `cwd` is writable by default. + let policy_workspace_only = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; + assert!(is_write_patch_constrained_to_writable_paths( &add_inside, - &[PathBuf::from(".")], + &policy_workspace_only, &cwd, )); - let add_outside_2 = make_add_change(parent.join("outside.txt")); assert!(!is_write_patch_constrained_to_writable_paths( - &add_outside_2, - &[PathBuf::from(".")], + &add_outside, + &policy_workspace_only, &cwd, )); - // With parent dir added as writable root, it should pass. + // With the parent dir explicitly added as a writable root, the + // outside write should be permitted. + let policy_with_parent = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![parent.clone()], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; assert!(is_write_patch_constrained_to_writable_paths( &add_outside, - &[PathBuf::from("..")], + &policy_with_parent, &cwd, - )) + )); } #[test] diff --git a/codex-rs/core/src/seatbelt.rs b/codex-rs/core/src/seatbelt.rs index ff9dbaa7f9..31aa770d2e 100644 --- a/codex-rs/core/src/seatbelt.rs +++ b/codex-rs/core/src/seatbelt.rs @@ -122,7 +122,6 @@ fn create_seatbelt_command_args( #[cfg(test)] mod tests { - #![expect(clippy::expect_used)] use super::MACOS_SEATBELT_BASE_POLICY; use super::create_seatbelt_command_args; use crate::protocol::SandboxPolicy; diff --git a/codex-rs/core/src/shell.rs b/codex-rs/core/src/shell.rs index 4a4e0146da..0cde3b4a78 100644 --- a/codex-rs/core/src/shell.rs +++ b/codex-rs/core/src/shell.rs @@ -98,7 +98,6 @@ mod tests { use std::process::Command; #[tokio::test] - #[expect(clippy::unwrap_used)] async fn test_current_shell_detects_zsh() { let shell = Command::new("sh") .arg("-c") @@ -129,7 +128,6 @@ mod tests { assert_eq!(actual_cmd, None); } - #[expect(clippy::unwrap_used)] #[tokio::test] async fn test_run_with_profile_escaping_and_execution() { let shell_path = "/bin/zsh"; diff --git a/codex-rs/core/src/turn_diff_tracker.rs b/codex-rs/core/src/turn_diff_tracker.rs index 7026d7bb32..5e73fa0650 100644 --- a/codex-rs/core/src/turn_diff_tracker.rs +++ b/codex-rs/core/src/turn_diff_tracker.rs @@ -466,7 +466,6 @@ fn is_windows_drive_or_unc_root(p: &std::path::Path) -> bool { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; use pretty_assertions::assert_eq; use tempfile::tempdir; diff --git a/codex-rs/core/src/user_agent.rs b/codex-rs/core/src/user_agent.rs index a0cf387069..ddcfd4b7f1 100644 --- a/codex-rs/core/src/user_agent.rs +++ b/codex-rs/core/src/user_agent.rs @@ -13,7 +13,6 @@ pub fn get_codex_user_agent(originator: Option<&str>) -> String { } #[cfg(test)] -#[allow(clippy::unwrap_used)] mod tests { use super::*; diff --git a/codex-rs/core/src/user_notification.rs b/codex-rs/core/src/user_notification.rs index e7479f89cd..0a3cb49e78 100644 --- a/codex-rs/core/src/user_notification.rs +++ b/codex-rs/core/src/user_notification.rs @@ -20,7 +20,6 @@ pub(crate) enum UserNotification { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; #[test] diff --git a/codex-rs/core/tests/cli_stream.rs b/codex-rs/core/tests/cli_stream.rs index c8570577ce..219e99787d 100644 --- a/codex-rs/core/tests/cli_stream.rs +++ b/codex-rs/core/tests/cli_stream.rs @@ -1,5 +1,3 @@ -#![expect(clippy::unwrap_used)] - use assert_cmd::Command as AssertCommand; use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use std::time::Duration; diff --git a/codex-rs/core/tests/client.rs b/codex-rs/core/tests/client.rs index 10c6c66fb5..2f7977eeb6 100644 --- a/codex-rs/core/tests/client.rs +++ b/codex-rs/core/tests/client.rs @@ -1,5 +1,3 @@ -#![allow(clippy::expect_used, clippy::unwrap_used)] - use codex_core::ConversationManager; use codex_core::ModelProviderInfo; use codex_core::NewConversation; @@ -27,10 +25,12 @@ fn sse_completed(id: &str) -> String { load_sse_fixture_with_id("tests/fixtures/completed_template.json", id) } +#[expect(clippy::unwrap_used)] fn assert_message_role(request_body: &serde_json::Value, role: &str) { assert_eq!(request_body["role"].as_str().unwrap(), role); } +#[expect(clippy::expect_used)] fn assert_message_starts_with(request_body: &serde_json::Value, text: &str) { let content = request_body["content"][0]["text"] .as_str() @@ -42,6 +42,7 @@ fn assert_message_starts_with(request_body: &serde_json::Value, text: &str) { ); } +#[expect(clippy::expect_used)] fn assert_message_ends_with(request_body: &serde_json::Value, text: &str) { let content = request_body["content"][0]["text"] .as_str() @@ -55,8 +56,6 @@ fn assert_message_ends_with(request_body: &serde_json::Value, text: &str) { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn includes_session_id_and_model_headers_in_request() { - #![allow(clippy::unwrap_used)] - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { println!( "Skipping test because it cannot execute when network is disabled in a Codex sandbox." @@ -129,8 +128,6 @@ async fn includes_session_id_and_model_headers_in_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn includes_base_instructions_override_in_request() { - #![allow(clippy::unwrap_used)] - // Mock server let server = MockServer::start().await; @@ -187,8 +184,6 @@ async fn includes_base_instructions_override_in_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn originator_config_override_is_used() { - #![allow(clippy::unwrap_used)] - // Mock server let server = MockServer::start().await; @@ -238,8 +233,6 @@ async fn originator_config_override_is_used() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn chatgpt_auth_sends_correct_request() { - #![allow(clippy::unwrap_used)] - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { println!( "Skipping test because it cannot execute when network is disabled in a Codex sandbox." @@ -320,8 +313,6 @@ async fn chatgpt_auth_sends_correct_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn includes_user_instructions_message_in_request() { - #![allow(clippy::unwrap_used)] - let server = MockServer::start().await; let first = ResponseTemplate::new(200) @@ -382,8 +373,6 @@ async fn includes_user_instructions_message_in_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn azure_overrides_assign_properties_used_for_responses_url() { - #![allow(clippy::unwrap_used)] - let existing_env_var_with_random_value = if cfg!(windows) { "USERNAME" } else { "USER" }; // Mock server @@ -460,8 +449,6 @@ async fn azure_overrides_assign_properties_used_for_responses_url() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn env_var_overrides_loaded_auth() { - #![allow(clippy::unwrap_used)] - let existing_env_var_with_random_value = if cfg!(windows) { "USERNAME" } else { "USER" }; // Mock server diff --git a/codex-rs/core/tests/common/lib.rs b/codex-rs/core/tests/common/lib.rs index 04b6dc4b62..0a9c8d5aa8 100644 --- a/codex-rs/core/tests/common/lib.rs +++ b/codex-rs/core/tests/common/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::expect_used)] +#![expect(clippy::expect_used)] use tempfile::TempDir; diff --git a/codex-rs/core/tests/exec.rs b/codex-rs/core/tests/exec.rs index ff5d2b7d54..f011aa7a0a 100644 --- a/codex-rs/core/tests/exec.rs +++ b/codex-rs/core/tests/exec.rs @@ -1,5 +1,4 @@ #![cfg(target_os = "macos")] -#![expect(clippy::unwrap_used, clippy::expect_used)] use std::collections::HashMap; @@ -24,6 +23,7 @@ fn skip_test() -> bool { false } +#[expect(clippy::expect_used)] async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result { let sandbox_type = get_platform_sandbox().expect("should be able to get sandbox type"); assert_eq!(sandbox_type, SandboxType::MacosSeatbelt); @@ -68,7 +68,6 @@ async fn truncates_output_lines() { let tmp = TempDir::new().expect("should be able to create temp dir"); let cmd = vec!["seq", "300"]; - #[expect(clippy::unwrap_used)] let output = run_test_cmd(tmp, cmd).await.unwrap(); let expected_output = (1..=256) diff --git a/codex-rs/core/tests/live_cli.rs b/codex-rs/core/tests/live_cli.rs index d79e242c4d..e77fe6998e 100644 --- a/codex-rs/core/tests/live_cli.rs +++ b/codex-rs/core/tests/live_cli.rs @@ -17,7 +17,7 @@ fn require_api_key() -> String { /// Helper that spawns the binary inside a TempDir with minimal flags. Returns (Assert, TempDir). fn run_live(prompt: &str) -> (assert_cmd::assert::Assert, TempDir) { - #![allow(clippy::unwrap_used)] + #![expect(clippy::unwrap_used)] use std::io::Read; use std::io::Write; use std::thread; @@ -113,7 +113,6 @@ fn run_live(prompt: &str) -> (assert_cmd::assert::Assert, TempDir) { #[ignore] #[test] fn live_create_file_hello_txt() { - #![allow(clippy::unwrap_used)] if std::env::var("OPENAI_API_KEY").is_err() { eprintln!("skipping live_create_file_hello_txt – OPENAI_API_KEY not set"); return; diff --git a/codex-rs/core/tests/prompt_caching.rs b/codex-rs/core/tests/prompt_caching.rs index 0c2552ee05..d637eb674e 100644 --- a/codex-rs/core/tests/prompt_caching.rs +++ b/codex-rs/core/tests/prompt_caching.rs @@ -1,5 +1,3 @@ -#![allow(clippy::expect_used, clippy::unwrap_used)] - use codex_core::ConversationManager; use codex_core::ModelProviderInfo; use codex_core::built_in_model_providers; @@ -24,7 +22,6 @@ fn sse_completed(id: &str) -> String { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn prefixes_context_and_instructions_once_and_consistently_across_requests() { - #![allow(clippy::unwrap_used)] use pretty_assertions::assert_eq; let server = MockServer::start().await; diff --git a/codex-rs/core/tests/seatbelt.rs b/codex-rs/core/tests/seatbelt.rs index fb2e7452f1..638ac48bb8 100644 --- a/codex-rs/core/tests/seatbelt.rs +++ b/codex-rs/core/tests/seatbelt.rs @@ -1,5 +1,4 @@ #![cfg(target_os = "macos")] -#![expect(clippy::expect_used)] //! Tests for the macOS sandboxing that are specific to Seatbelt. //! Tests that apply to both Mac and Linux sandboxing should go in sandbox.rs. @@ -160,6 +159,7 @@ async fn read_only_forbids_all_writes() { .await; } +#[expect(clippy::expect_used)] fn create_test_scenario(tmp: &TempDir) -> TestScenario { let repo_parent = tmp.path().to_path_buf(); let repo_root = repo_parent.join("repo"); @@ -177,6 +177,7 @@ fn create_test_scenario(tmp: &TempDir) -> TestScenario { } } +#[expect(clippy::expect_used)] /// Note that `path` must be absolute. async fn touch(path: &Path, policy: &SandboxPolicy) -> bool { assert!(path.is_absolute(), "Path must be absolute: {path:?}"); diff --git a/codex-rs/core/tests/stream_error_allows_next_turn.rs b/codex-rs/core/tests/stream_error_allows_next_turn.rs index d590c43312..415e75a426 100644 --- a/codex-rs/core/tests/stream_error_allows_next_turn.rs +++ b/codex-rs/core/tests/stream_error_allows_next_turn.rs @@ -25,7 +25,6 @@ fn sse_completed(id: &str) -> String { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn continue_after_stream_error() { - #![allow(clippy::unwrap_used)] if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { println!( "Skipping test because it cannot execute when network is disabled in a Codex sandbox." diff --git a/codex-rs/core/tests/stream_no_completed.rs b/codex-rs/core/tests/stream_no_completed.rs index 5fa8ab8ba1..3fb3f642d7 100644 --- a/codex-rs/core/tests/stream_no_completed.rs +++ b/codex-rs/core/tests/stream_no_completed.rs @@ -33,8 +33,6 @@ fn sse_completed(id: &str) -> String { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn retries_on_early_close() { - #![allow(clippy::unwrap_used)] - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { println!( "Skipping test because it cannot execute when network is disabled in a Codex sandbox." diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 22cf130462..5d64fe620b 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -191,7 +191,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { self.answer_started = true; } print!("{delta}"); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] std::io::stdout().flush().expect("could not flush stdout"); } EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent { delta }) => { @@ -207,7 +207,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { self.reasoning_started = true; } print!("{delta}"); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] std::io::stdout().flush().expect("could not flush stdout"); } EventMsg::AgentReasoningSectionBreak(_) => { @@ -215,7 +215,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { return CodexStatus::Running; } println!(); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] std::io::stdout().flush().expect("could not flush stdout"); } EventMsg::AgentReasoningRawContent(AgentReasoningRawContentEvent { text }) => { @@ -224,7 +224,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { } if !self.raw_reasoning_started { print!("{text}"); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] std::io::stdout().flush().expect("could not flush stdout"); } else { println!(); @@ -241,7 +241,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { self.raw_reasoning_started = true; } print!("{delta}"); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] std::io::stdout().flush().expect("could not flush stdout"); } EventMsg::AgentMessage(AgentMessageEvent { message }) => { diff --git a/codex-rs/exec/src/event_processor_with_json_output.rs b/codex-rs/exec/src/event_processor_with_json_output.rs index 76985518e6..11e9732e99 100644 --- a/codex-rs/exec/src/event_processor_with_json_output.rs +++ b/codex-rs/exec/src/event_processor_with_json_output.rs @@ -28,7 +28,7 @@ impl EventProcessor for EventProcessorWithJsonOutput { .into_iter() .map(|(key, value)| (key.to_string(), value)) .collect::>(); - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] let config_json = serde_json::to_string(&entries).expect("Failed to serialize config summary to JSON"); println!("{config_json}"); diff --git a/codex-rs/exec/tests/sandbox.rs b/codex-rs/exec/tests/sandbox.rs index 22caf8abc6..a98e0a9760 100644 --- a/codex-rs/exec/tests/sandbox.rs +++ b/codex-rs/exec/tests/sandbox.rs @@ -1,6 +1,4 @@ #![cfg(unix)] -#![expect(clippy::expect_used)] - use codex_core::protocol::SandboxPolicy; use codex_core::spawn::StdioPolicy; use std::collections::HashMap; @@ -180,6 +178,7 @@ async fn allow_unix_socketpair_recvfrom() { const IN_SANDBOX_ENV_VAR: &str = "IN_SANDBOX"; +#[expect(clippy::expect_used)] pub async fn run_code_under_sandbox( test_selector: &str, policy: &SandboxPolicy, diff --git a/codex-rs/execpolicy/src/execv_checker.rs b/codex-rs/execpolicy/src/execv_checker.rs index 3c9084e825..242ea6d177 100644 --- a/codex-rs/execpolicy/src/execv_checker.rs +++ b/codex-rs/execpolicy/src/execv_checker.rs @@ -140,7 +140,6 @@ fn is_executable_file(path: &str) -> bool { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use tempfile::TempDir; use super::*; diff --git a/codex-rs/execpolicy/tests/bad.rs b/codex-rs/execpolicy/tests/bad.rs index 5c2999a0cb..8b6e195fb0 100644 --- a/codex-rs/execpolicy/tests/bad.rs +++ b/codex-rs/execpolicy/tests/bad.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use codex_execpolicy::NegativeExamplePassedCheck; use codex_execpolicy::get_default_policy; diff --git a/codex-rs/execpolicy/tests/cp.rs b/codex-rs/execpolicy/tests/cp.rs index 14fd24410f..aa19f0b5d5 100644 --- a/codex-rs/execpolicy/tests/cp.rs +++ b/codex-rs/execpolicy/tests/cp.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] extern crate codex_execpolicy; use codex_execpolicy::ArgMatcher; @@ -12,6 +11,7 @@ use codex_execpolicy::Result; use codex_execpolicy::ValidExec; use codex_execpolicy::get_default_policy; +#[expect(clippy::expect_used)] fn setup() -> Policy { get_default_policy().expect("failed to load default policy") } diff --git a/codex-rs/execpolicy/tests/good.rs b/codex-rs/execpolicy/tests/good.rs index 645728afbd..3b7313a335 100644 --- a/codex-rs/execpolicy/tests/good.rs +++ b/codex-rs/execpolicy/tests/good.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use codex_execpolicy::PositiveExampleFailedCheck; use codex_execpolicy::get_default_policy; diff --git a/codex-rs/execpolicy/tests/head.rs b/codex-rs/execpolicy/tests/head.rs index 8d7a165cca..3c32ccfbfe 100644 --- a/codex-rs/execpolicy/tests/head.rs +++ b/codex-rs/execpolicy/tests/head.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use codex_execpolicy::ArgMatcher; use codex_execpolicy::ArgType; use codex_execpolicy::Error; @@ -13,6 +12,7 @@ use codex_execpolicy::get_default_policy; extern crate codex_execpolicy; +#[expect(clippy::expect_used)] fn setup() -> Policy { get_default_policy().expect("failed to load default policy") } diff --git a/codex-rs/execpolicy/tests/literal.rs b/codex-rs/execpolicy/tests/literal.rs index 629aeb9cfe..d849371e3b 100644 --- a/codex-rs/execpolicy/tests/literal.rs +++ b/codex-rs/execpolicy/tests/literal.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use codex_execpolicy::ArgType; use codex_execpolicy::Error; use codex_execpolicy::ExecCall; diff --git a/codex-rs/execpolicy/tests/ls.rs b/codex-rs/execpolicy/tests/ls.rs index 854ec3bf58..e52316c06e 100644 --- a/codex-rs/execpolicy/tests/ls.rs +++ b/codex-rs/execpolicy/tests/ls.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] extern crate codex_execpolicy; use codex_execpolicy::ArgType; @@ -12,6 +11,7 @@ use codex_execpolicy::Result; use codex_execpolicy::ValidExec; use codex_execpolicy::get_default_policy; +#[expect(clippy::expect_used)] fn setup() -> Policy { get_default_policy().expect("failed to load default policy") } diff --git a/codex-rs/execpolicy/tests/pwd.rs b/codex-rs/execpolicy/tests/pwd.rs index 339908e928..fdf5a4f1a5 100644 --- a/codex-rs/execpolicy/tests/pwd.rs +++ b/codex-rs/execpolicy/tests/pwd.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] extern crate codex_execpolicy; use std::vec; @@ -12,6 +11,7 @@ use codex_execpolicy::PositionalArg; use codex_execpolicy::ValidExec; use codex_execpolicy::get_default_policy; +#[expect(clippy::expect_used)] fn setup() -> Policy { get_default_policy().expect("failed to load default policy") } diff --git a/codex-rs/execpolicy/tests/sed.rs b/codex-rs/execpolicy/tests/sed.rs index 064e539305..bf35bf6d46 100644 --- a/codex-rs/execpolicy/tests/sed.rs +++ b/codex-rs/execpolicy/tests/sed.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] extern crate codex_execpolicy; use codex_execpolicy::ArgType; @@ -13,6 +12,7 @@ use codex_execpolicy::Result; use codex_execpolicy::ValidExec; use codex_execpolicy::get_default_policy; +#[expect(clippy::expect_used)] fn setup() -> Policy { get_default_policy().expect("failed to load default policy") } diff --git a/codex-rs/linux-sandbox/tests/landlock.rs b/codex-rs/linux-sandbox/tests/landlock.rs index 76de2547a6..8faa02d158 100644 --- a/codex-rs/linux-sandbox/tests/landlock.rs +++ b/codex-rs/linux-sandbox/tests/landlock.rs @@ -1,6 +1,4 @@ #![cfg(target_os = "linux")] -#![expect(clippy::unwrap_used, clippy::expect_used)] - use codex_core::config_types::ShellEnvironmentPolicy; use codex_core::error::CodexErr; use codex_core::error::SandboxErr; @@ -35,7 +33,7 @@ fn create_env_from_core_vars() -> HashMap { create_env(&policy) } -#[allow(clippy::print_stdout)] +#[expect(clippy::print_stdout, clippy::expect_used, clippy::unwrap_used)] async fn run_cmd(cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u64) { let params = ExecParams { command: cmd.iter().map(|elm| elm.to_string()).collect(), @@ -132,6 +130,7 @@ async fn test_timeout() { /// does NOT succeed (i.e. returns a non‑zero exit code) **unless** the binary /// is missing in which case we silently treat it as an accepted skip so the /// suite remains green on leaner CI images. +#[expect(clippy::expect_used)] async fn assert_network_blocked(cmd: &[&str]) { let cwd = std::env::current_dir().expect("cwd should exist"); let params = ExecParams { diff --git a/codex-rs/login/src/lib.rs b/codex-rs/login/src/lib.rs index d4358d2735..c40c1e6b52 100644 --- a/codex-rs/login/src/lib.rs +++ b/codex-rs/login/src/lib.rs @@ -427,7 +427,6 @@ pub struct AuthDotJson { #[cfg(test)] mod tests { - #![expect(clippy::expect_used, clippy::unwrap_used)] use super::*; use crate::token_data::IdTokenInfo; use crate::token_data::KnownPlan; diff --git a/codex-rs/login/src/token_data.rs b/codex-rs/login/src/token_data.rs index 1cb537fa24..2ac549fda2 100644 --- a/codex-rs/login/src/token_data.rs +++ b/codex-rs/login/src/token_data.rs @@ -155,7 +155,6 @@ mod tests { use serde::Serialize; #[test] - #[expect(clippy::expect_used, clippy::unwrap_used)] fn id_token_info_parses_email_and_plan() { #[derive(Serialize)] struct Header { diff --git a/codex-rs/login/tests/login_server_e2e.rs b/codex-rs/login/tests/login_server_e2e.rs index 3fe0e32041..3d00480051 100644 --- a/codex-rs/login/tests/login_server_e2e.rs +++ b/codex-rs/login/tests/login_server_e2e.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unwrap_used)] +#![expect(clippy::unwrap_used)] use std::net::SocketAddr; use std::net::TcpListener; use std::thread; diff --git a/codex-rs/mcp-server/src/codex_tool_config.rs b/codex-rs/mcp-server/src/codex_tool_config.rs index 1d77eb4b82..548f29334f 100644 --- a/codex-rs/mcp-server/src/codex_tool_config.rs +++ b/codex-rs/mcp-server/src/codex_tool_config.rs @@ -236,7 +236,6 @@ mod tests { #[test] fn verify_codex_tool_json_schema() { let tool = create_tool_for_codex_tool_call_param(); - #[expect(clippy::expect_used)] let tool_json = serde_json::to_value(&tool).expect("tool serializes"); let expected_tool_json = serde_json::json!({ "name": "codex", @@ -305,7 +304,6 @@ mod tests { #[test] fn verify_codex_tool_reply_json_schema() { let tool = create_tool_for_codex_tool_call_reply_param(); - #[expect(clippy::expect_used)] let tool_json = serde_json::to_value(&tool).expect("tool serializes"); let expected_tool_json = serde_json::json!({ "description": "Continue a Codex session by providing the session id and prompt.", diff --git a/codex-rs/mcp-server/src/json_to_toml.rs b/codex-rs/mcp-server/src/json_to_toml.rs index ae33382a1d..04f1f30615 100644 --- a/codex-rs/mcp-server/src/json_to_toml.rs +++ b/codex-rs/mcp-server/src/json_to_toml.rs @@ -28,7 +28,6 @@ pub(crate) fn json_to_toml(v: JsonValue) -> TomlValue { } #[cfg(test)] -#[allow(clippy::unwrap_used)] mod tests { use super::*; use pretty_assertions::assert_eq; diff --git a/codex-rs/mcp-server/src/mcp_protocol.rs b/codex-rs/mcp-server/src/mcp_protocol.rs index 0528e18a39..26c6655f3b 100644 --- a/codex-rs/mcp-server/src/mcp_protocol.rs +++ b/codex-rs/mcp-server/src/mcp_protocol.rs @@ -293,8 +293,6 @@ pub enum ClientNotification { } #[cfg(test)] -#[allow(clippy::expect_used)] -#[allow(clippy::unwrap_used)] mod tests { use std::path::PathBuf; diff --git a/codex-rs/mcp-server/src/outgoing_message.rs b/codex-rs/mcp-server/src/outgoing_message.rs index f13b8b324f..f408ca0a45 100644 --- a/codex-rs/mcp-server/src/outgoing_message.rs +++ b/codex-rs/mcp-server/src/outgoing_message.rs @@ -101,7 +101,7 @@ impl OutgoingMessageSender { event: &Event, meta: Option, ) { - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] let event_json = serde_json::to_value(event).expect("Event must serialize"); let params = if let Ok(params) = serde_json::to_value(OutgoingNotificationParams { @@ -244,8 +244,6 @@ pub(crate) struct OutgoingError { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] - use codex_core::protocol::EventMsg; use codex_core::protocol::SessionConfiguredEvent; use pretty_assertions::assert_eq; diff --git a/codex-rs/mcp-server/src/wire_format.rs b/codex-rs/mcp-server/src/wire_format.rs index 95d55e8bf5..034c72a1c2 100644 --- a/codex-rs/mcp-server/src/wire_format.rs +++ b/codex-rs/mcp-server/src/wire_format.rs @@ -222,7 +222,6 @@ pub struct ApplyPatchApprovalResponse { pub decision: ReviewDecision, } -#[allow(clippy::unwrap_used)] #[cfg(test)] mod tests { use super::*; diff --git a/codex-rs/mcp-server/tests/codex_message_processor_flow.rs b/codex-rs/mcp-server/tests/codex_message_processor_flow.rs index 7b7845609e..2cc55c6d62 100644 --- a/codex-rs/mcp-server/tests/codex_message_processor_flow.rs +++ b/codex-rs/mcp-server/tests/codex_message_processor_flow.rs @@ -1,5 +1,3 @@ -#![allow(clippy::expect_used, clippy::unwrap_used)] - use std::path::Path; use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; diff --git a/codex-rs/mcp-server/tests/codex_tool.rs b/codex-rs/mcp-server/tests/codex_tool.rs index fc992a8cd1..92f11eaa4f 100644 --- a/codex-rs/mcp-server/tests/codex_tool.rs +++ b/codex-rs/mcp-server/tests/codex_tool.rs @@ -304,7 +304,7 @@ async fn test_codex_tool_passes_base_instructions() { } async fn codex_tool_passes_base_instructions() -> anyhow::Result<()> { - #![allow(clippy::unwrap_used)] + #![expect(clippy::unwrap_used)] let server = create_mock_chat_completions_server(vec![create_final_assistant_message_sse_response( diff --git a/codex-rs/mcp-server/tests/create_conversation.rs b/codex-rs/mcp-server/tests/create_conversation.rs index 41052ea7b3..2349b0b9e4 100644 --- a/codex-rs/mcp-server/tests/create_conversation.rs +++ b/codex-rs/mcp-server/tests/create_conversation.rs @@ -1,5 +1,3 @@ -#![allow(clippy::expect_used, clippy::unwrap_used)] - use std::path::Path; use mcp_test_support::McpProcess; diff --git a/codex-rs/mcp-server/tests/send_message.rs b/codex-rs/mcp-server/tests/send_message.rs index f06c1587e3..bf2966ef05 100644 --- a/codex-rs/mcp-server/tests/send_message.rs +++ b/codex-rs/mcp-server/tests/send_message.rs @@ -1,5 +1,3 @@ -#![allow(clippy::expect_used)] - use std::path::Path; use std::thread::sleep; use std::time::Duration; diff --git a/codex-rs/mcp-types/tests/initialize.rs b/codex-rs/mcp-types/tests/initialize.rs index c69586f030..04778f2a90 100644 --- a/codex-rs/mcp-types/tests/initialize.rs +++ b/codex-rs/mcp-types/tests/initialize.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use mcp_types::ClientCapabilities; use mcp_types::ClientRequest; use mcp_types::Implementation; diff --git a/codex-rs/mcp-types/tests/progress_notification.rs b/codex-rs/mcp-types/tests/progress_notification.rs index b518e4a91c..396efca2bd 100644 --- a/codex-rs/mcp-types/tests/progress_notification.rs +++ b/codex-rs/mcp-types/tests/progress_notification.rs @@ -1,4 +1,3 @@ -#![expect(clippy::expect_used)] use mcp_types::JSONRPCMessage; use mcp_types::ProgressNotificationParams; use mcp_types::ProgressToken; diff --git a/codex-rs/ollama/src/client.rs b/codex-rs/ollama/src/client.rs index 6f4621135c..55c86e31f8 100644 --- a/codex-rs/ollama/src/client.rs +++ b/codex-rs/ollama/src/client.rs @@ -53,7 +53,7 @@ impl OllamaClient { /// Build a client from a provider definition and verify the server is reachable. async fn try_from_provider(provider: &ModelProviderInfo) -> io::Result { - #![allow(clippy::expect_used)] + #![expect(clippy::expect_used)] let base_url = provider .base_url .as_ref() @@ -235,7 +235,6 @@ impl OllamaClient { #[cfg(test)] mod tests { - #![allow(clippy::expect_used, clippy::unwrap_used)] use super::*; // Happy-path tests using a mock HTTP server; skip if sandbox network is disabled. diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 256289b21f..ee054e037a 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -188,7 +188,7 @@ impl App<'_> { } } - #[allow(clippy::expect_used)] + #[expect(clippy::expect_used)] let deadline = next_deadline.expect("deadline set"); let now = Instant::now(); let timeout = if deadline > now { diff --git a/codex-rs/tui/src/bottom_pane/chat_composer_history.rs b/codex-rs/tui/src/bottom_pane/chat_composer_history.rs index a744a409f3..93accfcd1a 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer_history.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer_history.rs @@ -182,7 +182,6 @@ impl ChatComposerHistory { #[cfg(test)] mod tests { - #![expect(clippy::expect_used)] use super::*; use crate::app_event::AppEvent; use codex_core::protocol::Op; diff --git a/codex-rs/tui/src/bottom_pane/textarea.rs b/codex-rs/tui/src/bottom_pane/textarea.rs index f0cfa614f9..d29698d7df 100644 --- a/codex-rs/tui/src/bottom_pane/textarea.rs +++ b/codex-rs/tui/src/bottom_pane/textarea.rs @@ -801,7 +801,7 @@ impl TextArea { } } - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used)] fn wrapped_lines(&self, width: u16) -> Ref<'_, Vec>> { // Ensure cache is ready (potentially mutably borrow, then drop) { @@ -926,7 +926,6 @@ impl TextArea { } #[cfg(test)] -#[allow(clippy::unwrap_used)] mod tests { use super::*; // crossterm types are intentionally not imported here to avoid unused warnings @@ -1432,7 +1431,6 @@ mod tests { // Seed the RNG based on the current day in Pacific Time (PST/PDT). This // keeps the fuzz test deterministic within a day while still varying // day-to-day to improve coverage. - #[allow(clippy::unwrap_used)] let pst_today_seed: u64 = (chrono::Utc::now() - chrono::Duration::hours(8)) .date_naive() .and_hms_opt(0, 0, 0) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 7cd7e6fad9..c10b94b79a 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -59,6 +59,7 @@ use crate::streaming::controller::AppEventHistorySink; use crate::streaming::controller::StreamController; use codex_core::ConversationManager; use codex_file_search::FileMatch; +use uuid::Uuid; // Track information about an in-flight exec command. struct RunningCommand { @@ -86,6 +87,7 @@ pub(crate) struct ChatWidget<'a> { interrupts: InterruptManager, // Whether a redraw is needed after handling the current event needs_redraw: bool, + session_id: Option, } struct UserMessage { @@ -125,6 +127,7 @@ impl ChatWidget<'_> { fn on_session_configured(&mut self, event: codex_core::protocol::SessionConfiguredEvent) { self.bottom_pane .set_history_metadata(event.history_log_id, event.history_entry_count); + self.session_id = Some(event.session_id); self.add_to_history(&history_cell::new_session_info(&self.config, event, true)); if let Some(user_message) = self.initial_user_message.take() { self.submit_user_message(user_message); @@ -528,6 +531,7 @@ impl ChatWidget<'_> { task_complete_pending: false, interrupts: InterruptManager::new(), needs_redraw: false, + session_id: None, } } @@ -675,6 +679,7 @@ impl ChatWidget<'_> { self.add_to_history(&history_cell::new_status_output( &self.config, &self.total_token_usage, + &self.session_id, )); } diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 3336024272..bf03a8ed05 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1,5 +1,3 @@ -#![allow(clippy::unwrap_used, clippy::expect_used, unnameable_test_items)] - use super::*; use crate::app_event::AppEvent; use crate::app_event_sender::AppEventSender; @@ -143,6 +141,7 @@ fn make_chatwidget_manual() -> ( task_complete_pending: false, interrupts: InterruptManager::new(), needs_redraw: false, + session_id: None, }; (widget, rx, op_rx) } diff --git a/codex-rs/tui/src/citation_regex.rs b/codex-rs/tui/src/citation_regex.rs index e5355ec2b8..ff5147ca83 100644 --- a/codex-rs/tui/src/citation_regex.rs +++ b/codex-rs/tui/src/citation_regex.rs @@ -1,4 +1,4 @@ -#![allow(clippy::expect_used)] +#![expect(clippy::expect_used)] use regex_lite::Regex; diff --git a/codex-rs/tui/src/diff_render.rs b/codex-rs/tui/src/diff_render.rs index 5a3199c04b..152a8e2862 100644 --- a/codex-rs/tui/src/diff_render.rs +++ b/codex-rs/tui/src/diff_render.rs @@ -357,7 +357,6 @@ fn style_del() -> Style { Style::default().fg(Color::Red) } -#[allow(clippy::expect_used)] #[cfg(test)] mod tests { use super::*; diff --git a/codex-rs/tui/src/file_search.rs b/codex-rs/tui/src/file_search.rs index 9fb010a095..8a05b2c165 100644 --- a/codex-rs/tui/src/file_search.rs +++ b/codex-rs/tui/src/file_search.rs @@ -31,10 +31,7 @@ use std::time::Duration; use crate::app_event::AppEvent; use crate::app_event_sender::AppEventSender; -#[allow(clippy::unwrap_used)] const MAX_FILE_SEARCH_RESULTS: NonZeroUsize = NonZeroUsize::new(8).unwrap(); - -#[allow(clippy::unwrap_used)] const NUM_FILE_SEARCH_THREADS: NonZeroUsize = NonZeroUsize::new(2).unwrap(); /// How long to wait after a keystroke before firing the first search when none @@ -84,7 +81,7 @@ impl FileSearchManager { /// Call whenever the user edits the `@` token. pub fn on_user_query(&self, query: String) { { - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used)] let mut st = self.state.lock().unwrap(); if query == st.latest_query { // No change, nothing to do. @@ -125,7 +122,7 @@ impl FileSearchManager { // `active_search` is cleared. thread::sleep(FILE_SEARCH_DEBOUNCE); loop { - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used)] if state.lock().unwrap().active_search.is_none() { break; } @@ -137,7 +134,7 @@ impl FileSearchManager { let cancellation_token = Arc::new(AtomicBool::new(false)); let token = cancellation_token.clone(); let query = { - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used)] let mut st = state.lock().unwrap(); let query = st.latest_query.clone(); st.is_search_scheduled = false; @@ -188,7 +185,7 @@ impl FileSearchManager { // that we are clearing the ActiveSearch that corresponds to the // cancellation token we were given. { - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used)] let mut st = search_state.lock().unwrap(); if let Some(active_search) = &st.active_search { if Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token) { diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index ba645a7e50..a4cebe981d 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -37,6 +37,7 @@ use std::path::PathBuf; use std::time::Duration; use std::time::Instant; use tracing::error; +use uuid::Uuid; #[derive(Clone)] pub(crate) struct CommandOutput { @@ -483,7 +484,11 @@ pub(crate) fn new_diff_output(message: String) -> PlainHistoryCell { PlainHistoryCell { lines } } -pub(crate) fn new_status_output(config: &Config, usage: &TokenUsage) -> PlainHistoryCell { +pub(crate) fn new_status_output( + config: &Config, + usage: &TokenUsage, + session_id: &Option, +) -> PlainHistoryCell { let mut lines: Vec> = Vec::new(); lines.push(Line::from("/status".magenta())); @@ -521,6 +526,13 @@ pub(crate) fn new_status_output(config: &Config, usage: &TokenUsage) -> PlainHis sandbox_name.into(), ])); + if let Some(session_id) = session_id { + lines.push(Line::from(vec![ + " • Session ID: ".into(), + session_id.to_string().into(), + ])); + } + lines.push(Line::from("")); // 👤 Account (only if ChatGPT tokens exist), shown under the first block diff --git a/codex-rs/tui/src/insert_history.rs b/codex-rs/tui/src/insert_history.rs index 971c376234..ced667af8d 100644 --- a/codex-rs/tui/src/insert_history.rs +++ b/codex-rs/tui/src/insert_history.rs @@ -379,7 +379,6 @@ fn slice_line_spans( #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; #[test] diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index a661242d3e..f1a7d99b79 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -295,7 +295,6 @@ fn restore() { } } -#[allow(clippy::unwrap_used)] fn should_show_login_screen(config: &Config) -> bool { if config.model_provider.requires_openai_auth { // Reading the OpenAI API key is an async operation because it may need diff --git a/codex-rs/tui/src/text_formatting.rs b/codex-rs/tui/src/text_formatting.rs index 208dc700a8..b77c6e5baa 100644 --- a/codex-rs/tui/src/text_formatting.rs +++ b/codex-rs/tui/src/text_formatting.rs @@ -102,7 +102,6 @@ pub(crate) fn truncate_text(text: &str, max_graphemes: usize) -> String { #[cfg(test)] mod tests { - #![allow(clippy::unwrap_used)] use super::*; use pretty_assertions::assert_eq;