From 9b29a48a09893cfdea7d7ea056918a9a011ccc01 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 30 Jan 2026 11:48:03 -0800 Subject: [PATCH 1/5] Plan mode prompt (#10238) # External (non-OpenAI) Pull Request Requirements Before opening this Pull Request, please read the dedicated "Contributing" markdown file or your PR may be closed: https://github.com/openai/codex/blob/main/docs/contributing.md If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes. Include a link to a bug report or enhancement request. --- .../core/templates/collaboration_mode/plan.md | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/codex-rs/core/templates/collaboration_mode/plan.md b/codex-rs/core/templates/collaboration_mode/plan.md index 395b6c368b..2ae72aba43 100644 --- a/codex-rs/core/templates/collaboration_mode/plan.md +++ b/codex-rs/core/templates/collaboration_mode/plan.md @@ -60,18 +60,14 @@ Do not ask questions that can be answered from the repo or system (for example, Every assistant turn MUST be exactly one of: A) a `request_user_input` tool call (questions/options only), OR -B) a non-final status update with no questions and no plan content, OR C) the final output: a titled, plan-only document. Rules: * No questions in free text (only via `request_user_input`). * Never mix a `request_user_input` call with plan content. -* Status updates must not include questions or plan content. * Internal tool/repo exploration is allowed privately before A, B, or C. -Status updates should be frequent during exploration. Provide 1-2 sentence updates that summarize discoveries, assumption changes, or why you are changing direction. Use Parallel tools for exploration. - ## Ask a lot, but never ask trivia You SHOULD ask many questions, but each question must: @@ -113,19 +109,15 @@ When you present the official plan, wrap it in a `` block so the Example: -# Plan title -- Step 1 -- Step 2 +plan content -The final plan must be plan-only and include: +plan content should be human and agent digestible. The final plan must be plan-only and include: * A clear title -* A TL;DR section (3–5 bullets) -* Exact file paths to change -* Exact structures or shapes to introduce or modify -* Exact function, method, type, and variable names and signatures -* Test cases +* tldr section. don't necessary call it tldr. +* Important changes or additions of signatures, structs, types. +* Test cases and scenarios * Explicit assumptions and defaults chosen where needed Do not ask "should I proceed?" in the final output. The user can easily switch out of Plan mode and request implementation if you have included a `` block in your response. Alternatively, they can decide to stay in Plan mode and continue refining the plan. From 2457bb3c404e4c092eef52ff9b2538b01623a17d Mon Sep 17 00:00:00 2001 From: Charley Cunningham Date: Fri, 30 Jan 2026 11:57:13 -0800 Subject: [PATCH 2/5] Fix deploy (#10251) Fix https://github.com/openai/codex/actions/runs/21527697445/job/62035898666 --- codex-rs/tui/src/app.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index c65ce4498d..7ed9d0b035 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -1154,6 +1154,7 @@ impl App { return Ok(AppExitInfo { token_usage: app.token_usage(), thread_id: app.chat_widget.thread_id(), + thread_name: app.chat_widget.thread_name(), update_action: app.pending_update_action, exit_reason, }); From b7351f7f53d07584bf292c1b61bb58c8aa839bb5 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 30 Jan 2026 12:22:37 -0800 Subject: [PATCH 3/5] plan prompt (#10255) # External (non-OpenAI) Pull Request Requirements Before opening this Pull Request, please read the dedicated "Contributing" markdown file or your PR may be closed: https://github.com/openai/codex/blob/main/docs/contributing.md If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes. Include a link to a bug report or enhancement request. --- codex-rs/core/templates/collaboration_mode/plan.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/templates/collaboration_mode/plan.md b/codex-rs/core/templates/collaboration_mode/plan.md index 2ae72aba43..f600bc9c7b 100644 --- a/codex-rs/core/templates/collaboration_mode/plan.md +++ b/codex-rs/core/templates/collaboration_mode/plan.md @@ -32,7 +32,6 @@ Actions that gather truth, reduce ambiguity, or validate feasibility without cha Actions that implement the plan or change repo-tracked state. Examples: * Editing or writing files -* Generating, updating, or accepting snapshots * Running formatters or linters that rewrite files * Applying patches, migrations, or codegen that updates repo-tracked files * Side-effectful commands whose purpose is to carry out the plan rather than refine it @@ -60,13 +59,13 @@ Do not ask questions that can be answered from the repo or system (for example, Every assistant turn MUST be exactly one of: A) a `request_user_input` tool call (questions/options only), OR -C) the final output: a titled, plan-only document. +B) the final output: a titled, plan-only document. Rules: * No questions in free text (only via `request_user_input`). * Never mix a `request_user_input` call with plan content. -* Internal tool/repo exploration is allowed privately before A, B, or C. +* Internal tool/repo exploration is allowed privately before A or B. ## Ask a lot, but never ask trivia From 83317ed4bff93b5cf98d3d3ec82dad66dd5b2207 Mon Sep 17 00:00:00 2001 From: Charley Cunningham Date: Fri, 30 Jan 2026 12:39:15 -0800 Subject: [PATCH 4/5] Make plan highlight use popup grey background (#10253) ## Summary - align proposed plan background with popup surface color by reusing `user_message_bg` - remove the custom blue-tinted plan background image --- codex-rs/tui/src/style.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/codex-rs/tui/src/style.rs b/codex-rs/tui/src/style.rs index 2b4ab4c4d3..fc8fd4c501 100644 --- a/codex-rs/tui/src/style.rs +++ b/codex-rs/tui/src/style.rs @@ -40,10 +40,5 @@ pub fn user_message_bg(terminal_bg: (u8, u8, u8)) -> Color { #[allow(clippy::disallowed_methods)] pub fn proposed_plan_bg(terminal_bg: (u8, u8, u8)) -> Color { - let (top, alpha) = if is_light(terminal_bg) { - ((0, 110, 150), 0.08) - } else { - ((80, 170, 220), 0.2) - }; - best_color(blend(top, terminal_bg, alpha)) + user_message_bg(terminal_bg) } From dd6c1d3787aa3c8032f6e6496e2bf25c47ddb37a Mon Sep 17 00:00:00 2001 From: daniel-oai Date: Fri, 30 Jan 2026 12:42:07 -0800 Subject: [PATCH 5/5] Skip loading codex home as project layer (#10207) Summary: - Fixes issue #9932: https://github.com/openai/codex/issues/9932 - Prevents `$CODEX_HOME` (typically `~/.codex`) from being discovered as a project `.codex` layer by skipping it during project layer traversal. We compare both normalized absolute paths and best-effort canonicalized paths to handle symlinks. - Adds regression tests for home-directory invocation and for the case where `CODEX_HOME` points to a project `.codex` directory (e.g., worktrees/editor integrations). Testing: - `cargo build -p codex-cli --bin codex` - `cargo build -p codex-rmcp-client --bin test_stdio_server` - `cargo test -p codex-core` - `cargo test --all-features` - Manual: ran `target/debug/codex` from `~` and confirmed the disabled-folder warning and trust prompt no longer appear. --- codex-rs/core/src/config_loader/mod.rs | 11 +++ codex-rs/core/src/config_loader/tests.rs | 95 ++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/codex-rs/core/src/config_loader/mod.rs b/codex-rs/core/src/config_loader/mod.rs index eba89fdb3d..838db0d5ff 100644 --- a/codex-rs/core/src/config_loader/mod.rs +++ b/codex-rs/core/src/config_loader/mod.rs @@ -25,6 +25,7 @@ use codex_protocol::config_types::TrustLevel; use codex_protocol::protocol::AskForApproval; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBufGuard; +use dunce::canonicalize as normalize_path; use serde::Deserialize; use std::io; use std::path::Path; @@ -236,6 +237,7 @@ pub async fn load_config_layers_state( &cwd, &project_trust_context.project_root, &project_trust_context, + codex_home, ) .await?; layers.extend(project_layers); @@ -671,7 +673,11 @@ async fn load_project_layers( cwd: &AbsolutePathBuf, project_root: &AbsolutePathBuf, trust_context: &ProjectTrustContext, + codex_home: &Path, ) -> io::Result> { + let codex_home_abs = AbsolutePathBuf::from_absolute_path(codex_home)?; + let codex_home_normalized = + normalize_path(codex_home_abs.as_path()).unwrap_or_else(|_| codex_home_abs.to_path_buf()); let mut dirs = cwd .as_path() .ancestors() @@ -702,6 +708,11 @@ async fn load_project_layers( let layer_dir = AbsolutePathBuf::from_absolute_path(dir)?; let decision = trust_context.decision_for_dir(&layer_dir); let dot_codex_abs = AbsolutePathBuf::from_absolute_path(&dot_codex)?; + let dot_codex_normalized = + normalize_path(dot_codex_abs.as_path()).unwrap_or_else(|_| dot_codex_abs.to_path_buf()); + if dot_codex_abs == codex_home_abs || dot_codex_normalized == codex_home_normalized { + continue; + } let config_file = dot_codex_abs.join(CONFIG_TOML_FILE)?; match tokio::fs::read_to_string(&config_file).await { Ok(contents) => { diff --git a/codex-rs/core/src/config_loader/tests.rs b/codex-rs/core/src/config_loader/tests.rs index 38d104fc19..355057f184 100644 --- a/codex-rs/core/src/config_loader/tests.rs +++ b/codex-rs/core/src/config_loader/tests.rs @@ -755,6 +755,101 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s Ok(()) } +#[tokio::test] +async fn codex_home_is_not_loaded_as_project_layer_from_home_dir() -> std::io::Result<()> { + let tmp = tempdir()?; + let home_dir = tmp.path().join("home"); + let codex_home = home_dir.join(".codex"); + tokio::fs::create_dir_all(&codex_home).await?; + tokio::fs::write(codex_home.join(CONFIG_TOML_FILE), "foo = \"user\"\n").await?; + + let cwd = AbsolutePathBuf::from_absolute_path(&home_dir)?; + let layers = load_config_layers_state( + &codex_home, + Some(cwd), + &[] as &[(String, TomlValue)], + LoaderOverrides::default(), + ) + .await?; + + let project_layers: Vec<_> = layers + .get_layers( + super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + true, + ) + .into_iter() + .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .collect(); + let expected: Vec<&ConfigLayerEntry> = Vec::new(); + assert_eq!(expected, project_layers); + assert_eq!( + layers.effective_config().get("foo"), + Some(&TomlValue::String("user".to_string())) + ); + + Ok(()) +} + +#[tokio::test] +async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Result<()> { + let tmp = tempdir()?; + let project_root = tmp.path().join("project"); + let nested = project_root.join("child"); + let project_dot_codex = project_root.join(".codex"); + let nested_dot_codex = nested.join(".codex"); + + tokio::fs::create_dir_all(&nested_dot_codex).await?; + tokio::fs::create_dir_all(project_root.join(".git")).await?; + tokio::fs::write(nested_dot_codex.join(CONFIG_TOML_FILE), "foo = \"child\"\n").await?; + + tokio::fs::create_dir_all(&project_dot_codex).await?; + make_config_for_test(&project_dot_codex, &project_root, TrustLevel::Trusted, None).await?; + let user_config_path = project_dot_codex.join(CONFIG_TOML_FILE); + let user_config_contents = tokio::fs::read_to_string(&user_config_path).await?; + tokio::fs::write( + &user_config_path, + format!("foo = \"user\"\n{user_config_contents}"), + ) + .await?; + + let cwd = AbsolutePathBuf::from_absolute_path(&nested)?; + let layers = load_config_layers_state( + &project_dot_codex, + Some(cwd), + &[] as &[(String, TomlValue)], + LoaderOverrides::default(), + ) + .await?; + + let project_layers: Vec<_> = layers + .get_layers( + super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + true, + ) + .into_iter() + .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .collect(); + + let child_config: TomlValue = toml::from_str("foo = \"child\"\n").expect("parse child config"); + assert_eq!( + vec![&ConfigLayerEntry { + name: super::ConfigLayerSource::Project { + dot_codex_folder: AbsolutePathBuf::from_absolute_path(&nested_dot_codex)?, + }, + config: child_config.clone(), + version: version_for_toml(&child_config), + disabled_reason: None, + }], + project_layers + ); + assert_eq!( + layers.effective_config().get("foo"), + Some(&TomlValue::String("child".to_string())) + ); + + Ok(()) +} + #[tokio::test] async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result<()> { let tmp = tempdir()?;