From 9a97a74e530a266aefed01586ec1ddc11225dc95 Mon Sep 17 00:00:00 2001 From: Brent Traut Date: Tue, 19 May 2026 15:07:03 -0700 Subject: [PATCH] core: scope override reloads to projects --- codex-rs/core/src/network_proxy_loader.rs | 15 +-- .../core/src/network_proxy_loader_tests.rs | 16 ++- codex-rs/core/src/session/mod.rs | 53 ++------- codex-rs/core/src/session/tests.rs | 106 ------------------ 4 files changed, 22 insertions(+), 168 deletions(-) diff --git a/codex-rs/core/src/network_proxy_loader.rs b/codex-rs/core/src/network_proxy_loader.rs index 738849f1ef..253f2d95b8 100644 --- a/codex-rs/core/src/network_proxy_loader.rs +++ b/codex-rs/core/src/network_proxy_loader.rs @@ -90,12 +90,8 @@ fn collect_layer_mtimes(stack: &ConfigLayerStack) -> Vec { .iter() .filter_map(|layer| { let path = match &layer.name { - ConfigLayerSource::System { file } | ConfigLayerSource::SystemOverride { file } => { - Some(file.clone()) - } - ConfigLayerSource::User { file, .. } | ConfigLayerSource::UserOverride { file } => { - Some(file.clone()) - } + ConfigLayerSource::System { file } => Some(file.clone()), + ConfigLayerSource::User { file, .. } => Some(file.clone()), ConfigLayerSource::Project { dot_codex_folder } => { Some(dot_codex_folder.join(CONFIG_TOML_FILE)) } @@ -116,9 +112,9 @@ fn collect_layer_mtimes(stack: &ConfigLayerStack) -> Vec { let optional_override_paths = layers .into_iter() .filter_map(|layer| match &layer.name { - ConfigLayerSource::System { file } | ConfigLayerSource::User { file, .. } => file - .parent() - .map(|parent| parent.join(CONFIG_OVERRIDE_TOML_FILE)), + ConfigLayerSource::Project { dot_codex_folder } => { + Some(dot_codex_folder.join(CONFIG_OVERRIDE_TOML_FILE)) + } _ => None, }) .filter(|path| !watched_paths.contains(path)) @@ -281,7 +277,6 @@ fn is_user_controlled_layer(layer: &ConfigLayerSource) -> bool { matches!( layer, ConfigLayerSource::User { .. } - | ConfigLayerSource::UserOverride { .. } | ConfigLayerSource::Project { .. } | ConfigLayerSource::ProjectOverride { .. } | ConfigLayerSource::SessionFlags diff --git a/codex-rs/core/src/network_proxy_loader_tests.rs b/codex-rs/core/src/network_proxy_loader_tests.rs index 858e388776..fe754502f1 100644 --- a/codex-rs/core/src/network_proxy_loader_tests.rs +++ b/codex-rs/core/src/network_proxy_loader_tests.rs @@ -8,19 +8,17 @@ use pretty_assertions::assert_eq; use tempfile::tempdir; #[tokio::test] -async fn network_proxy_reloader_notices_new_user_override_files() { +async fn network_proxy_reloader_notices_new_project_override_files() { let temp = tempdir().expect("create temp dir"); - let base_file = - AbsolutePathBuf::try_from(temp.path().join(CONFIG_TOML_FILE)).expect("base path"); - let override_file = AbsolutePathBuf::try_from(temp.path().join(CONFIG_OVERRIDE_TOML_FILE)) - .expect("override path"); + let dot_codex_folder = + AbsolutePathBuf::try_from(temp.path().join(".codex")).expect("project config folder"); + std::fs::create_dir_all(dot_codex_folder.as_path()).expect("create project config folder"); + let base_file = dot_codex_folder.join(CONFIG_TOML_FILE); + let override_file = dot_codex_folder.join(CONFIG_OVERRIDE_TOML_FILE); std::fs::write(base_file.as_path(), "").expect("write base config"); let stack = ConfigLayerStack::new( vec![codex_config::ConfigLayerEntry::new( - ConfigLayerSource::User { - file: base_file, - profile: None, - }, + ConfigLayerSource::Project { dot_codex_folder }, toml::Value::Table(Default::default()), )], codex_config::ConfigRequirements::default(), diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 14abea73c4..8f0d2f1c01 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -180,7 +180,6 @@ use crate::config::resolve_web_search_mode_for_turn; use crate::context_manager::ContextManager; use crate::context_manager::TotalTokenUsageBreakdown; use crate::thread_rollout_truncation::initial_history_has_prior_user_turns; -use codex_config::CONFIG_OVERRIDE_TOML_FILE; use codex_config::CONFIG_TOML_FILE; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStackOrdering; @@ -1513,11 +1512,6 @@ impl Session { // // Prefer `refresh_runtime_config()` when the host can already provide a materialized // config snapshot. This file-based path exists for legacy local reload flows. - enum UserConfigReloadLayer { - Base(AbsolutePathBuf), - Override(AbsolutePathBuf), - } - let config_toml_paths = { let state = self.state.lock().await; let config = &state.session_configuration.original_config_do_not_use; @@ -1529,47 +1523,25 @@ impl Session { ) .into_iter() .filter_map(|layer| match &layer.name { - ConfigLayerSource::User { file, .. } => { - Some(UserConfigReloadLayer::Base(file.clone())) - } - ConfigLayerSource::UserOverride { file } => { - Some(UserConfigReloadLayer::Override(file.clone())) - } + ConfigLayerSource::User { file, .. } => Some(file.clone()), _ => None, }) .collect::>(); - let override_path = state - .session_configuration - .codex_home - .join(CONFIG_OVERRIDE_TOML_FILE); - let has_override_path = user_config_paths.iter().any(|layer| { - matches!( - layer, - UserConfigReloadLayer::Override(path) if path == &override_path - ) - }); - let mut user_config_paths = user_config_paths; - if !has_override_path && override_path.as_path().exists() { - user_config_paths.push(UserConfigReloadLayer::Override(override_path)); - } if user_config_paths.is_empty() { - vec![UserConfigReloadLayer::Base( + vec![ state .session_configuration .codex_home .join(CONFIG_TOML_FILE), - )] + ] } else { user_config_paths } }; let mut reloaded_user_configs = Vec::with_capacity(config_toml_paths.len()); - for reload_layer in config_toml_paths { - let config_toml_path = match &reload_layer { - UserConfigReloadLayer::Base(path) | UserConfigReloadLayer::Override(path) => path, - }; - let user_config = match std::fs::read_to_string(config_toml_path.as_path()) { + for config_toml_path in config_toml_paths { + let user_config = match std::fs::read_to_string(&config_toml_path) { Ok(contents) => match toml::from_str::(&contents) { Ok(config) => config, Err(err) => { @@ -1585,21 +1557,16 @@ impl Session { return; } }; - reloaded_user_configs.push((reload_layer, user_config)); + reloaded_user_configs.push((config_toml_path, user_config)); } let next_config = { let state = self.state.lock().await; let mut config = (*state.session_configuration.original_config_do_not_use).clone(); - for (reload_layer, user_config) in reloaded_user_configs { - config.config_layer_stack = match reload_layer { - UserConfigReloadLayer::Base(config_toml_path) => config - .config_layer_stack - .with_user_config(&config_toml_path, user_config), - UserConfigReloadLayer::Override(config_toml_path) => config - .config_layer_stack - .with_user_override_config(&config_toml_path, user_config), - }; + for (config_toml_path, user_config) in reloaded_user_configs { + config.config_layer_stack = config + .config_layer_stack + .with_user_config(&config_toml_path, user_config); } config.tool_suggest = resolve_tool_suggest_config_from_layer_stack(&config.config_layer_stack); diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index a45b820691..88e9092da3 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -1269,112 +1269,6 @@ async fn reload_user_config_layer_updates_base_and_selected_profile_layers() { ); } -#[tokio::test] -async fn reload_user_config_layer_refreshes_user_override_layers() { - let (session, _turn_context) = make_session_and_context().await; - let codex_home = session.codex_home().await; - std::fs::create_dir_all(&codex_home).expect("create codex home"); - let config_toml_path = codex_home.join(CONFIG_TOML_FILE); - let override_path = codex_home.join(codex_config::CONFIG_OVERRIDE_TOML_FILE); - std::fs::write(&config_toml_path, "model = \"base\"\n").expect("write base config"); - std::fs::write(&override_path, "model = \"override-old\"\n").expect("write override"); - let config = ConfigBuilder::without_managed_config_for_tests() - .codex_home(codex_home.to_path_buf()) - .build() - .await - .expect("load override config"); - { - let mut state = session.state.lock().await; - state.session_configuration.original_config_do_not_use = Arc::new(config); - } - std::fs::write(&override_path, "model = \"override-new\"\n").expect("update override"); - - session.reload_user_config_layer().await; - - let config = session.get_config().await; - assert_eq!( - config - .config_layer_stack - .effective_user_config() - .expect("merged user config") - .get("model") - .and_then(toml::Value::as_str), - Some("override-new") - ); -} - -#[tokio::test] -async fn reload_user_config_layer_discovers_new_user_override_layers() { - let (session, _turn_context) = make_session_and_context().await; - let codex_home = session.codex_home().await; - std::fs::create_dir_all(&codex_home).expect("create codex home"); - let config_toml_path = codex_home.join(CONFIG_TOML_FILE); - let override_path = codex_home.join(codex_config::CONFIG_OVERRIDE_TOML_FILE); - std::fs::write(&config_toml_path, "model = \"base\"\n").expect("write base config"); - let config = ConfigBuilder::without_managed_config_for_tests() - .codex_home(codex_home.to_path_buf()) - .build() - .await - .expect("load base config"); - { - let mut state = session.state.lock().await; - state.session_configuration.original_config_do_not_use = Arc::new(config); - } - std::fs::write(&override_path, "model = \"override-new\"\n").expect("write override"); - - session.reload_user_config_layer().await; - - let config = session.get_config().await; - assert_eq!( - config - .config_layer_stack - .effective_user_config() - .expect("merged user config") - .get("model") - .and_then(toml::Value::as_str), - Some("override-new") - ); -} - -#[tokio::test] -async fn reload_user_config_layer_keeps_selected_non_profile_user_above_new_override() { - let (session, _turn_context) = make_session_and_context().await; - let codex_home = session.codex_home().await; - std::fs::create_dir_all(&codex_home).expect("create codex home"); - let base_config_path = codex_home.join(CONFIG_TOML_FILE); - let override_path = codex_home.join(codex_config::CONFIG_OVERRIDE_TOML_FILE); - let selected_config_path = codex_home.join("selected.toml"); - std::fs::write(&base_config_path, "model = \"base\"\n").expect("write base config"); - std::fs::write(&selected_config_path, "model = \"selected\"\n").expect("write selected config"); - let config = ConfigBuilder::without_managed_config_for_tests() - .codex_home(codex_home.to_path_buf()) - .loader_overrides(LoaderOverrides { - user_config_path: Some(selected_config_path.abs()), - ..LoaderOverrides::without_managed_config_for_tests() - }) - .build() - .await - .expect("load selected config"); - { - let mut state = session.state.lock().await; - state.session_configuration.original_config_do_not_use = Arc::new(config); - } - std::fs::write(&override_path, "model = \"override\"\n").expect("write override"); - - session.reload_user_config_layer().await; - - let config = session.get_config().await; - assert_eq!( - config - .config_layer_stack - .effective_user_config() - .expect("merged user config") - .get("model") - .and_then(toml::Value::as_str), - Some("selected") - ); -} - #[tokio::test] async fn reload_user_config_layer_refreshes_hooks() -> anyhow::Result<()> { let session = make_session_with_config(|config| {