diff --git a/codex-rs/app-server/src/config_manager_service.rs b/codex-rs/app-server/src/config_manager_service.rs index 0b4d87a06f..1605e372b1 100644 --- a/codex-rs/app-server/src/config_manager_service.rs +++ b/codex-rs/app-server/src/config_manager_service.rs @@ -16,7 +16,6 @@ use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerMetadata; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::ConfigRequirementsToml; use codex_config::ShellEnvironmentPolicyFilterRepresentation; use codex_config::config_toml::ConfigToml; @@ -160,11 +159,7 @@ impl ConfigManager { .collect(), layers: params.include_layers.then(|| { layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .iter() + .all_layers_high_to_low() .map(|layer| config_layer_to_api(layer.as_layer())) .collect() }), diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 200df63399..8c29bda7a1 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -4,7 +4,6 @@ use codex_arg0::Arg0DispatchPaths; use codex_code_mode::CodeModeSessionProvider; use codex_code_mode::WebSocketCodeModeSessionProvider; -use codex_config::ConfigLayerStackOrdering; use codex_config::LoaderOverrides; use codex_config::NoopThreadConfigLoader; use codex_config::RemoteThreadConfigLoader; @@ -355,10 +354,7 @@ fn app_text_range(range: &CoreTextRange) -> AppTextRange { fn project_config_warning(config: &Config) -> Option { let mut disabled_folders = Vec::new(); - for layer in config.config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) { + for layer in config.config_layer_stack.all_layers_low_to_high() { let ConfigLayerSource::Project { dot_codex_folder } = &layer.name else { continue; }; diff --git a/codex-rs/app-server/src/request_processors/bedrock_auth.rs b/codex-rs/app-server/src/request_processors/bedrock_auth.rs index 1b7fa3a162..ac21094a4c 100644 --- a/codex-rs/app-server/src/request_processors/bedrock_auth.rs +++ b/codex-rs/app-server/src/request_processors/bedrock_auth.rs @@ -30,7 +30,6 @@ pub(super) async fn set_user_model_provider_to_bedrock( }; if let Some((overriding_layer, effective_provider)) = layers .layers_high_to_low() - .into_iter() .filter(|layer| layer.name.precedence() > user_precedence) .find_map(|layer| { layer diff --git a/codex-rs/cli/src/doctor/title.rs b/codex-rs/cli/src/doctor/title.rs index 09dc80c0a2..b5413ff979 100644 --- a/codex-rs/cli/src/doctor/title.rs +++ b/codex-rs/cli/src/doctor/title.rs @@ -3,7 +3,6 @@ use std::path::Path; use std::path::PathBuf; use codex_config::ConfigLayerSource; -use codex_config::ConfigLayerStackOrdering; use codex_core::config::Config; use codex_git_utils::get_git_repo_root; use unicode_segmentation::UnicodeSegmentation; @@ -171,11 +170,7 @@ fn terminal_title_project_root(config: &Config, cwd: &Path) -> Option dot_codex_folder .as_path() diff --git a/codex-rs/config/src/cloud_config_layers_tests.rs b/codex-rs/config/src/cloud_config_layers_tests.rs index c2bc7383bc..95cb38db72 100644 --- a/codex-rs/config/src/cloud_config_layers_tests.rs +++ b/codex-rs/config/src/cloud_config_layers_tests.rs @@ -1,7 +1,6 @@ use super::*; use crate::CONFIG_TOML_FILE; use crate::ConfigLayerStack; -use crate::ConfigLayerStackOrdering; use crate::ConfigRequirements; use crate::ConfigRequirementsToml; use crate::config_toml::ConfigToml; @@ -121,11 +120,7 @@ review_model = "system-review" assert_eq!( stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false - ) - .iter() + .layers_low_to_high() .map(|layer| layer.name.clone()) .collect::>(), vec![ diff --git a/codex-rs/config/src/diagnostics.rs b/codex-rs/config/src/diagnostics.rs index 274db98cd0..0fb9ff8764 100644 --- a/codex-rs/config/src/diagnostics.rs +++ b/codex-rs/config/src/diagnostics.rs @@ -4,7 +4,6 @@ use crate::ConfigLayerEntry; use crate::ConfigLayerSource; use crate::ConfigLayerStack; -use crate::ConfigLayerStackOrdering; use crate::format_config_layer_source; use codex_utils_absolute_path::AbsolutePathBufGuard; use serde::de::DeserializeOwned; @@ -175,14 +174,8 @@ pub async fn first_layer_config_error( // When the merged config fails schema validation, we surface the first concrete // per-file error to point users at a specific file and range rather than an // opaque merged-layer failure. - first_layer_config_error_for_entries::( - layers.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ), - config_toml_file, - ) - .await + first_layer_config_error_for_entries::(layers.layers_low_to_high(), config_toml_file) + .await } pub async fn first_layer_config_error_from_entries( diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs index ef82161f85..519def4232 100644 --- a/codex-rs/config/src/lib.rs +++ b/codex-rs/config/src/lib.rs @@ -153,7 +153,6 @@ pub use skills_config::SkillConfig; pub use skills_config::SkillsConfig; pub use state::ConfigLayerEntry; pub use state::ConfigLayerStack; -pub use state::ConfigLayerStackOrdering; pub use state::ConfigLoadOptions; pub use state::LoaderOverrides; pub use strict_config::config_error_from_ignored_toml_fields; diff --git a/codex-rs/config/src/loader/README.md b/codex-rs/config/src/loader/README.md index b711ebbb61..6b4129f602 100644 --- a/codex-rs/config/src/loader/README.md +++ b/codex-rs/config/src/loader/README.md @@ -14,7 +14,7 @@ Exported from `codex_config::loader`: - `ConfigLayerStack` - `effective_config() -> toml::Value` - `origins() -> HashMap` - - `layers_high_to_low() -> Vec` + - `layers_high_to_low() -> impl Iterator` - `with_user_config(user_config) -> ConfigLayerStack` - `ConfigLayerEntry` (one layer’s `{name, config, version, disabled_reason}`; `name` carries source metadata) - `ConfigLoadOptions` (user-facing load behavior such as strict config validation) @@ -68,7 +68,7 @@ let layers = load_config_layers_state( let effective = layers.effective_config(); let origins = layers.origins(); -let layers_for_ui = layers.layers_high_to_low(); +let layers_for_ui = layers.layers_high_to_low().collect::>(); ``` ## Internal layout diff --git a/codex-rs/config/src/state.rs b/codex-rs/config/src/state.rs index 0d44173a2d..9c0c1d8b56 100644 --- a/codex-rs/config/src/state.rs +++ b/codex-rs/config/src/state.rs @@ -233,12 +233,6 @@ impl ConfigLayerEntry { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ConfigLayerStackOrdering { - LowestPrecedenceFirst, - HighestPrecedenceFirst, -} - #[derive(Debug, Clone, Default, PartialEq)] pub struct ConfigLayerStack { /// Layers are listed from lowest precedence (base) to highest (top), so @@ -323,34 +317,16 @@ impl ConfigLayerStack { Some(file) } - /// Returns all user config layers in the requested precedence order. - /// - /// With profile-v2 enabled, `LowestPrecedenceFirst` returns the base user - /// config before the profile overlay, while `HighestPrecedenceFirst` returns - /// the profile overlay before the base user config. - pub fn get_user_layers( - &self, - ordering: ConfigLayerStackOrdering, - include_disabled: bool, - ) -> Vec<&ConfigLayerEntry> { - self.get_layers(ordering, include_disabled) - .into_iter() - .filter(|layer| matches!(layer.name, ConfigLayerSource::User { .. })) - .collect() - } - /// Returns the merged config from enabled user layers only. /// /// When profile config is active, this includes the base user config followed /// by the profile override config. pub fn effective_user_config(&self) -> Option { - let user_layers = self.get_user_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ); - if user_layers.is_empty() { - return None; - } + let mut user_layers = self + .layers_low_to_high() + .filter(|layer| matches!(layer.name, ConfigLayerSource::User { .. })) + .peekable(); + user_layers.peek()?; let mut merged = TomlValue::Table(toml::map::Map::new()); for layer in user_layers { @@ -468,10 +444,7 @@ impl ConfigLayerStack { /// tracked separately. pub fn effective_config(&self) -> TomlValue { let mut merged = TomlValue::Table(toml::map::Map::new()); - for layer in self.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in self.layers_low_to_high() { merge_toml_values(&mut merged, &layer.config); } merged @@ -484,10 +457,7 @@ impl ConfigLayerStack { let mut origins = HashMap::new(); let mut path = Vec::new(); - for layer in self.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in self.layers_low_to_high() { let config = normalized_with_key_aliases(&layer.config, &[]); record_origins(&config, &layer.metadata(), &mut path, &mut origins); } @@ -495,33 +465,35 @@ impl ConfigLayerStack { origins } - /// Returns config layers from highest precedence to lowest precedence. + /// Returns enabled config layers from lowest precedence to highest. /// /// Requirement sources are tracked separately and are not included here. - pub fn layers_high_to_low(&self) -> Vec<&ConfigLayerEntry> { - self.get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ false, - ) + pub fn layers_low_to_high(&self) -> impl DoubleEndedIterator { + self.all_layers_low_to_high() + .filter(|layer| !layer.is_disabled()) } - /// Returns config layers in the requested precedence order. + /// Returns enabled config layers from highest precedence to lowest. /// /// Requirement sources are tracked separately and are not included here. - pub fn get_layers( - &self, - ordering: ConfigLayerStackOrdering, - include_disabled: bool, - ) -> Vec<&ConfigLayerEntry> { - let mut layers: Vec<&ConfigLayerEntry> = self - .layers - .iter() - .filter(|layer| include_disabled || !layer.is_disabled()) - .collect(); - if ordering == ConfigLayerStackOrdering::HighestPrecedenceFirst { - layers.reverse(); - } - layers + pub fn layers_high_to_low(&self) -> impl DoubleEndedIterator { + self.layers_low_to_high().rev() + } + + /// Returns all config layers, including disabled layers, from lowest + /// precedence to highest. + /// + /// Requirement sources are tracked separately and are not included here. + pub fn all_layers_low_to_high(&self) -> impl DoubleEndedIterator { + self.layers.iter() + } + + /// Returns all config layers, including disabled layers, from highest + /// precedence to lowest. + /// + /// Requirement sources are tracked separately and are not included here. + pub fn all_layers_high_to_low(&self) -> impl DoubleEndedIterator { + self.all_layers_low_to_high().rev() } } diff --git a/codex-rs/config/src/state_tests.rs b/codex-rs/config/src/state_tests.rs index 71871f121a..45fa8b9518 100644 --- a/codex-rs/config/src/state_tests.rs +++ b/codex-rs/config/src/state_tests.rs @@ -250,6 +250,63 @@ approval_policy = "on-request" ); } +#[test] +fn layer_iterators_preserve_precedence_and_disabled_layers() { + let temp_dir = TempDir::new().expect("tempdir"); + let user_source = ConfigLayerSource::User { + file: test_user_config_path(&temp_dir, "config.toml"), + profile: None, + }; + let project_source = ConfigLayerSource::Project { + dot_codex_folder: test_user_config_path(&temp_dir, ".codex"), + }; + let session_source = ConfigLayerSource::SessionFlags; + let empty_config = TomlValue::Table(toml::map::Map::new()); + let stack = ConfigLayerStack::new( + vec![ + ConfigLayerEntry::new(user_source.clone(), empty_config.clone()), + ConfigLayerEntry::new_disabled( + project_source.clone(), + empty_config.clone(), + "project is untrusted", + ), + ConfigLayerEntry::new(session_source.clone(), empty_config), + ], + ConfigRequirements::default(), + ConfigRequirementsToml::default(), + ) + .expect("layer stack should be valid"); + + assert_eq!( + stack + .layers_low_to_high() + .map(|layer| &layer.name) + .collect::>(), + vec![&user_source, &session_source] + ); + assert_eq!( + stack + .layers_high_to_low() + .map(|layer| &layer.name) + .collect::>(), + vec![&session_source, &user_source] + ); + assert_eq!( + stack + .all_layers_low_to_high() + .map(|layer| &layer.name) + .collect::>(), + vec![&user_source, &project_source, &session_source] + ); + assert_eq!( + stack + .all_layers_high_to_low() + .map(|layer| &layer.name) + .collect::>(), + vec![&session_source, &project_source, &user_source] + ); +} + #[test] fn with_user_config_updates_matching_user_layer_without_replacing_active_profile() { let temp_dir = TempDir::new().expect("tempdir"); diff --git a/codex-rs/core-skills/src/config_rules.rs b/codex-rs/core-skills/src/config_rules.rs index 8a64adfa69..fe410ae61b 100644 --- a/codex-rs/core-skills/src/config_rules.rs +++ b/codex-rs/core-skills/src/config_rules.rs @@ -2,7 +2,6 @@ use std::collections::HashSet; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::SkillConfig; use codex_config::SkillsConfig; pub use codex_skills::SkillConfigRule; @@ -15,10 +14,7 @@ use crate::SkillMetadata; pub fn skill_config_rules_from_stack(config_layer_stack: &ConfigLayerStack) -> SkillConfigRules { let mut entries = Vec::new(); - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) { + for layer in config_layer_stack.all_layers_low_to_high() { if !matches!( layer.name, ConfigLayerSource::User { .. } | ConfigLayerSource::SessionFlags diff --git a/codex-rs/core-skills/src/loader.rs b/codex-rs/core-skills/src/loader.rs index bb043ab971..18a80f7d7b 100644 --- a/codex-rs/core-skills/src/loader.rs +++ b/codex-rs/core-skills/src/loader.rs @@ -19,7 +19,6 @@ use crate::model::SkillToolDependency; use crate::system::system_cache_root_dir; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::default_project_root_markers; use codex_config::merge_toml_values; use codex_config::project_root_markers_from_config; @@ -296,10 +295,7 @@ fn skill_roots_from_layer_stack_inner( ) -> Vec { let mut roots = Vec::new(); - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) { + for layer in config_layer_stack.all_layers_high_to_low() { let Some(config_folder) = layer.config_folder() else { continue; }; @@ -429,10 +425,7 @@ async fn repo_agents_skill_roots( fn project_root_markers_from_stack(config_layer_stack: &ConfigLayerStack) -> Vec { let mut merged = TomlValue::Table(toml::map::Map::new()); - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in config_layer_stack.layers_low_to_high() { if matches!(layer.name, ConfigLayerSource::Project { .. }) { continue; } diff --git a/codex-rs/core/src/agent/role.rs b/codex-rs/core/src/agent/role.rs index 1e14f789e6..54a1b52bdd 100644 --- a/codex-rs/core/src/agent/role.rs +++ b/codex-rs/core/src/agent/role.rs @@ -15,7 +15,6 @@ use anyhow::anyhow; use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::config_toml::ConfigToml; use codex_config::loader::resolve_relative_paths_in_config_toml; use codex_exec_server::LOCAL_FS; @@ -236,11 +235,7 @@ mod reload { fn existing_layers(config: &Config) -> Vec { config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect() } diff --git a/codex-rs/core/src/agent/role_tests.rs b/codex-rs/core/src/agent/role_tests.rs index 6f359eb38d..72e4486c40 100644 --- a/codex-rs/core/src/agent/role_tests.rs +++ b/codex-rs/core/src/agent/role_tests.rs @@ -2,7 +2,6 @@ use super::*; use crate::SkillsService; use crate::config::ConfigBuilder; use crate::skills_load_input_from_config; -use codex_config::ConfigLayerStackOrdering; use codex_core_plugins::PluginsManager; use codex_protocol::config_types::ServiceTier; use codex_protocol::openai_models::ReasoningEffort; @@ -39,11 +38,7 @@ async fn write_role_config(home: &TempDir, name: &str, contents: &str) -> PathBu fn session_flags_layer_count(config: &Config) -> usize { config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .filter(|layer| layer.name == ConfigLayerSource::SessionFlags) .count() } @@ -302,11 +297,7 @@ writable_roots = ["./sandbox-root"] let role_layer = config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .rfind(|layer| layer.name == ConfigLayerSource::SessionFlags) .expect("expected a session flags layer"); let sandbox_workspace_write = role_layer diff --git a/codex-rs/core/src/agents_md.rs b/codex-rs/core/src/agents_md.rs index 37e0c0dd2d..fc7eb30439 100644 --- a/codex-rs/core/src/agents_md.rs +++ b/codex-rs/core/src/agents_md.rs @@ -19,7 +19,6 @@ use crate::config::Config; use crate::context::UserInstructions as ContextUserInstructions; use crate::environment_selection::TurnEnvironmentSnapshot; use codex_config::ConfigLayerSource; -use codex_config::ConfigLayerStackOrdering; use codex_config::default_project_root_markers; use codex_config::merge_toml_values; use codex_config::project_root_markers_from_config; @@ -160,10 +159,7 @@ async fn agents_md_paths( let dir = cwd.clone(); let mut merged = TomlValue::Table(toml::map::Map::new()); - for layer in config.config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in config.config_layer_stack.layers_low_to_high() { if matches!(layer.name, ConfigLayerSource::Project { .. }) { continue; } diff --git a/codex-rs/core/src/config/agent_roles.rs b/codex-rs/core/src/config/agent_roles.rs index 483cd3a98d..f2fa797128 100644 --- a/codex-rs/core/src/config/agent_roles.rs +++ b/codex-rs/core/src/config/agent_roles.rs @@ -1,6 +1,5 @@ use super::AgentRoleConfig; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::config_toml::AgentRoleToml; use codex_config::config_toml::AgentsToml; use codex_config::config_toml::ConfigToml; @@ -22,11 +21,8 @@ pub(crate) async fn load_agent_roles( config_layer_stack: &ConfigLayerStack, startup_warnings: &mut Vec, ) -> std::io::Result> { - let layers = config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ); - if layers.is_empty() { + let mut layers = config_layer_stack.layers_low_to_high().peekable(); + if layers.peek().is_none() { return load_agent_roles_without_layers(fs, cfg).await; } diff --git a/codex-rs/core/src/config/config_loader_tests.rs b/codex-rs/core/src/config/config_loader_tests.rs index f2cabb7538..8ad9b0c4ff 100644 --- a/codex-rs/core/src/config/config_loader_tests.rs +++ b/codex-rs/core/src/config/config_loader_tests.rs @@ -9,7 +9,6 @@ use codex_config::CloudConfigBundleLoader; use codex_config::ConfigError; use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerSource; -use codex_config::ConfigLayerStackOrdering; use codex_config::ConfigLoadError; use codex_config::ConfigLoadOptions; use codex_config::ConfigRequirements; @@ -844,7 +843,6 @@ async fn returns_empty_when_all_layers_missing() { ); let num_system_layers = layers .layers_high_to_low() - .iter() .filter(|layer| matches!(layer.name, ConfigLayerSource::System { .. })) .count(); assert_eq!( @@ -896,10 +894,10 @@ approval_policy = "on-request" .await .expect("load layers"); - let user_layers = layers.get_user_layers( - super::ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ); + let user_layers = layers + .layers_low_to_high() + .filter(|layer| matches!(layer.name, ConfigLayerSource::User { .. })) + .collect::>(); assert_eq!(user_layers.len(), 2); assert_eq!( user_layers[0].name, @@ -962,7 +960,6 @@ async fn includes_thread_config_layers_in_stack() -> anyhow::Result<()> { let layer_sources = layers .layers_high_to_low() - .into_iter() .map(|layer| layer.name.clone()) .collect::>(); assert_eq!( @@ -1048,7 +1045,6 @@ flag = false assert_eq!(nested.get("flag"), Some(&TomlValue::Boolean(false))); let mdm_layer = state .layers_high_to_low() - .into_iter() .find(|layer| { matches!( layer.name, @@ -2292,11 +2288,7 @@ model_provider = "cloud-provider" ); assert_eq!( layers - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) - .iter() + .layers_low_to_high() .map(|layer| layer.name.clone()) .collect::>(), vec![ @@ -2634,7 +2626,6 @@ async fn project_layers_prefer_closest_cwd() -> std::io::Result<()> { let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter_map(|layer| match &layer.name { ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), _ => None, @@ -2716,7 +2707,6 @@ async fn linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_h let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers.len(), 2); @@ -2797,7 +2787,6 @@ async fn linked_worktree_project_layers_use_root_repo_hooks_without_worktree_con let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers.len(), 1); @@ -2867,7 +2856,6 @@ async fn nested_project_root_markers_do_not_redirect_regular_repo_hooks() -> std let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers.len(), 2); @@ -3054,7 +3042,6 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); let expected_project_layer = ConfigLayerEntry::new( @@ -3093,11 +3080,7 @@ async fn codex_home_is_not_loaded_as_project_layer_from_home_dir() -> std::io::R .await?; let project_layers: Vec<_> = layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); let expected: Vec<&ConfigLayerEntry> = Vec::new(); @@ -3158,11 +3141,7 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul .await?; let project_layers: Vec<_> = layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); @@ -3232,11 +3211,7 @@ profile = "ignored" ) .await?; let project_layers_untrusted: Vec<_> = layers_untrusted - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers_untrusted.len(), 1); @@ -3278,11 +3253,7 @@ profile = "ignored" ) .await?; let project_layers_unknown: Vec<_> = layers_unknown - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers_unknown.len(), 1); @@ -3371,7 +3342,6 @@ wire_api = "responses" let project_layer = layers .layers_high_to_low() - .into_iter() .find(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .expect("expected project layer"); @@ -3472,11 +3442,7 @@ async fn project_trust_does_not_match_configured_alias_for_canonical_cwd() -> st .await?; let project_layers: Vec<_> = layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers.len(), 1); @@ -3636,11 +3602,7 @@ async fn invalid_project_config_ignored_when_untrusted_or_unknown() -> std::io:: ) .await?; let project_layers: Vec<_> = layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!( @@ -3704,11 +3666,7 @@ async fn project_layer_without_config_toml_is_disabled_when_untrusted_or_unknown ) .await?; let project_layers: Vec<_> = layers - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_high_to_low() .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!( @@ -3811,7 +3769,6 @@ async fn project_root_markers_supports_alternate_markers() -> std::io::Result<() let project_layers: Vec<_> = layers .layers_high_to_low() - .into_iter() .filter_map(|layer| match &layer.name { ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), _ => None, diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 3fefc95b35..1c78429ea7 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -2369,11 +2369,7 @@ async fn managed_unrestricted_permission_profile_still_enables_network_requireme let layers = config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(); let mut requirements = config.config_layer_stack.requirements().clone(); diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index f6667ae082..0c7ef1d40b 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -9,7 +9,6 @@ use crate::windows_sandbox::resolve_windows_sandbox_private_desktop; use codex_config::CloudConfigBundleLoader; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::ConfigRequirements; use codex_config::ConfigRequirementsToml; use codex_config::ConstrainedWithSource; @@ -1769,21 +1768,13 @@ impl Config { ) -> std::io::Result { let mut layers = refreshed_config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .filter(|layer| !is_session_layer(&layer.name)) .cloned() .collect::>(); layers.extend( self.config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .filter(|layer| is_session_layer(&layer.name)) .cloned(), ); @@ -2368,11 +2359,8 @@ fn resolve_tool_suggest_config_from_config( } }; - let layers = config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ); - if layers.is_empty() { + let mut layers = config_layer_stack.layers_low_to_high().peekable(); + if layers.peek().is_none() { for disabled_tool in tool_suggest .into_iter() .flat_map(|tool_suggest| tool_suggest.disabled_tools.iter().cloned()) @@ -2467,11 +2455,7 @@ fn resolve_permission_config_syntax( } let session_flags_select_profiles = config_layer_stack - .get_layers( - ConfigLayerStackOrdering::HighestPrecedenceFirst, - /*include_disabled*/ false, - ) - .into_iter() + .layers_high_to_low() .find(|layer| matches!(layer.name, ConfigLayerSource::SessionFlags)) .and_then(|layer| { layer @@ -2486,10 +2470,7 @@ fn resolve_permission_config_syntax( } let mut selection = None; - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in config_layer_stack.layers_low_to_high() { let Ok(layer_selection) = layer.config.clone().try_into::() else { continue; }; diff --git a/codex-rs/core/src/exec_policy.rs b/codex-rs/core/src/exec_policy.rs index 92b54d135f..c065917d41 100644 --- a/codex-rs/core/src/exec_policy.rs +++ b/codex-rs/core/src/exec_policy.rs @@ -7,7 +7,6 @@ use arc_swap::ArcSwap; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_execpolicy::AmendError; use codex_execpolicy::Decision; use codex_execpolicy::Error as ExecPolicyRuleError; @@ -181,11 +180,7 @@ pub(crate) fn child_uses_parent_exec_policy(parent_config: &Config, child_config fn exec_policy_config_folders(config: &Config) -> Vec { config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) - .into_iter() + .layers_low_to_high() .filter_map(codex_config::ConfigLayerEntry::config_folder) .collect() } @@ -639,10 +634,7 @@ pub async fn load_exec_policy(config_stack: &ConfigLayerStack) -> Result = child_config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(); layers.push(ConfigLayerEntry::new( @@ -190,11 +185,7 @@ async fn child_does_not_use_parent_exec_policy_when_requirements_exec_policy_dif child_config.config_layer_stack = ConfigLayerStack::new( child_config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(), requirements, diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index f0c2e2bbb5..b4cf21c84b 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -2136,7 +2136,6 @@ fn project_mcp_tool_approval_config_folder( config .config_layer_stack .layers_high_to_low() - .into_iter() .find_map(|layer| { if !matches!(layer.name, ConfigLayerSource::Project { .. }) { return None; diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 7d340b16ff..41495ef318 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -191,7 +191,6 @@ use crate::context_manager::ContextManager; use crate::thread_rollout_truncation::initial_history_has_prior_user_turns; use codex_config::CONFIG_TOML_FILE; use codex_config::ConfigLayerSource; -use codex_config::ConfigLayerStackOrdering; use codex_config::types::McpServerConfig; use codex_model_provider_info::ModelProviderInfo; use codex_protocol::error::CodexErr; @@ -1740,11 +1739,7 @@ impl Session { let config = &state.session_configuration.original_config_do_not_use; let user_config_paths = config .config_layer_stack - .get_user_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .filter_map(|layer| match &layer.name { ConfigLayerSource::User { file, .. } => Some(file.clone()), _ => None, diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index c037f81590..df385256b5 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -18,7 +18,6 @@ use crate::test_support::models_manager_with_provider; use crate::tools::format_exec_output_str; use crate::tools::registry::ToolRegistry; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID; use codex_config::LoaderOverrides; use codex_config::NetworkConstraints; @@ -1269,11 +1268,7 @@ async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> an let layers = config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(); let mut requirements = config.config_layer_stack.requirements().clone(); @@ -1899,11 +1894,7 @@ async fn refresh_mcp_config_replaces_managed_server_and_plugin_requirements() { requirements_toml.plugins = Some(plugin_requirements.clone()); let layers = next_config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(); next_config.config_layer_stack = ConfigLayerStack::new(layers, requirements, requirements_toml) @@ -8617,11 +8608,7 @@ async fn record_context_updates_emits_environment_item_for_network_changes() { )); let layers = config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .into_iter() + .all_layers_low_to_high() .cloned() .collect(); config.config_layer_stack = ConfigLayerStack::new( diff --git a/codex-rs/hooks/src/config_rules.rs b/codex-rs/hooks/src/config_rules.rs index 2935cabc80..050932ecbb 100644 --- a/codex-rs/hooks/src/config_rules.rs +++ b/codex-rs/hooks/src/config_rules.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::HookStateToml; use codex_config::TomlValue; @@ -21,10 +20,7 @@ pub fn hook_states_from_stack( }; let mut states: HashMap = HashMap::new(); - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) { + for layer in config_layer_stack.all_layers_low_to_high() { if !matches!( layer.name, ConfigLayerSource::User { .. } | ConfigLayerSource::SessionFlags diff --git a/codex-rs/hooks/src/engine/discovery.rs b/codex-rs/hooks/src/engine/discovery.rs index adce766908..79f904bed8 100644 --- a/codex-rs/hooks/src/engine/discovery.rs +++ b/codex-rs/hooks/src/engine/discovery.rs @@ -7,7 +7,6 @@ use codex_config::CONFIG_TOML_FILE; use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::HookEventsToml; use codex_config::HookHandlerConfig; use codex_config::HookStateToml; @@ -98,10 +97,7 @@ pub(crate) fn discover_handlers( policy, ); - for layer in config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ false, - ) { + for layer in config_layer_stack.layers_low_to_high() { let (hook_source, is_managed) = hook_metadata_for_config_layer_source(&layer.name); let policy_path = config_toml_source_path(layer); let policy_source = HookHandlerSource { diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index aabbc4b855..60649f0d16 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -134,7 +134,6 @@ use codex_app_server_protocol::TurnError as AppServerTurnError; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::WriteStatus; use codex_config::CloudConfigBundleLoader; -use codex_config::ConfigLayerStackOrdering; use codex_config::LoaderOverrides; use codex_config::types::ApprovalsReviewer; use codex_config::types::MemoriesToml; diff --git a/codex-rs/tui/src/app/config_persistence.rs b/codex-rs/tui/src/app/config_persistence.rs index 949e078111..2b01c7a010 100644 --- a/codex-rs/tui/src/app/config_persistence.rs +++ b/codex-rs/tui/src/app/config_persistence.rs @@ -30,22 +30,18 @@ pub(super) fn resume_model_settings_for_overrides( config: &Config, harness_overrides: &ConfigOverrides, ) -> crate::app_server_session::ResumeModelSettings { - let has_layer_override = config - .config_layer_stack - .layers_high_to_low() - .into_iter() - .any(|layer| { - matches!( - &layer.name, - ConfigLayerSource::SessionFlags - | ConfigLayerSource::User { - profile: Some(_), - .. - } - ) && ["model", "model_provider", "model_reasoning_effort"] - .iter() - .any(|key| layer.config.get(*key).is_some()) - }); + let has_layer_override = config.config_layer_stack.layers_high_to_low().any(|layer| { + matches!( + &layer.name, + ConfigLayerSource::SessionFlags + | ConfigLayerSource::User { + profile: Some(_), + .. + } + ) && ["model", "model_provider", "model_reasoning_effort"] + .iter() + .any(|key| layer.config.get(*key).is_some()) + }); if harness_overrides.model.is_some() || harness_overrides.model_provider.is_some() || has_layer_override diff --git a/codex-rs/tui/src/app/startup_prompts.rs b/codex-rs/tui/src/app/startup_prompts.rs index 9bddc5e907..3c98a1a262 100644 --- a/codex-rs/tui/src/app/startup_prompts.rs +++ b/codex-rs/tui/src/app/startup_prompts.rs @@ -69,10 +69,7 @@ pub(super) fn emit_skill_load_warnings(app_event_tx: &AppEventSender, errors: &[ pub(super) fn emit_project_config_warnings(app_event_tx: &AppEventSender, config: &Config) { let mut disabled_folders = Vec::new(); - for layer in config.config_layer_stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) { + for layer in config.config_layer_stack.all_layers_low_to_high() { let ConfigLayerSource::Project { dot_codex_folder } = &layer.name else { continue; }; diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index a161321da8..92eae23843 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -116,7 +116,6 @@ use codex_app_server_protocol::TurnCompletedNotification; use codex_app_server_protocol::TurnPlanStepStatus; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::UserInput; -use codex_config::ConfigLayerStackOrdering; use codex_config::Constrained; use codex_config::ConstraintResult; use codex_config::types::ApprovalsReviewer; diff --git a/codex-rs/tui/src/chatwidget/status_surfaces.rs b/codex-rs/tui/src/chatwidget/status_surfaces.rs index 7120462330..fa17bb385b 100644 --- a/codex-rs/tui/src/chatwidget/status_surfaces.rs +++ b/codex-rs/tui/src/chatwidget/status_surfaces.rs @@ -448,11 +448,7 @@ impl ChatWidget { self.config .config_layer_stack - .get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ) - .iter() + .all_layers_low_to_high() .find_map(|layer| match &layer.name { ConfigLayerSource::Project { dot_codex_folder } => { dot_codex_folder.as_path().parent().map(Path::to_path_buf) diff --git a/codex-rs/tui/src/debug_config.rs b/codex-rs/tui/src/debug_config.rs index 5a5e31dc1b..8eba68d792 100644 --- a/codex-rs/tui/src/debug_config.rs +++ b/codex-rs/tui/src/debug_config.rs @@ -6,7 +6,6 @@ use codex_config::CONFIG_TOML_FILE; use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStack; -use codex_config::ConfigLayerStackOrdering; use codex_config::ManagedHooksRequirementsToml; use codex_config::NetworkConstraints; use codex_config::NetworkDomainPermissionToml; @@ -131,14 +130,11 @@ fn render_debug_config_lines( .bold() .into(), ); - let layers = stack.get_layers( - ConfigLayerStackOrdering::LowestPrecedenceFirst, - /*include_disabled*/ true, - ); - if layers.is_empty() { + let mut layers = stack.all_layers_low_to_high().peekable(); + if layers.peek().is_none() { lines.push(" ".dim().into()); } else { - for (index, layer) in layers.iter().enumerate() { + for (index, layer) in layers.enumerate() { let source = format_config_layer_source(&layer.name, CONFIG_TOML_FILE); let status = if layer.is_disabled() { "disabled"