From 01890edc1e11a0bd6722820fc5c97f2959a2b607 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Fri, 8 May 2026 11:18:26 -0400 Subject: [PATCH] flatten project hook trust metadata --- codex-rs/config/src/lib.rs | 1 - codex-rs/config/src/loader/mod.rs | 20 ++++++------- codex-rs/config/src/state.rs | 28 ++++++++++--------- .../core/src/config/config_loader_tests.rs | 15 ++++------ codex-rs/hooks/src/engine/discovery.rs | 16 ++++------- codex-rs/hooks/src/engine/mod_tests.rs | 8 ++---- 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs index e6d739a556..e88c736db0 100644 --- a/codex-rs/config/src/lib.rs +++ b/codex-rs/config/src/lib.rs @@ -115,7 +115,6 @@ pub use state::ConfigLayerEntry; pub use state::ConfigLayerStack; pub use state::ConfigLayerStackOrdering; pub use state::LoaderOverrides; -pub use state::ProjectTrustScope; pub use thread_config::NoopThreadConfigLoader; pub use thread_config::RemoteThreadConfigLoader; pub use thread_config::SessionThreadConfig; diff --git a/codex-rs/config/src/loader/mod.rs b/codex-rs/config/src/loader/mod.rs index 23fbcfa058..8d15e37b87 100644 --- a/codex-rs/config/src/loader/mod.rs +++ b/codex-rs/config/src/loader/mod.rs @@ -22,7 +22,6 @@ use crate::project_root_markers::project_root_markers_from_config; use crate::state::ConfigLayerEntry; use crate::state::ConfigLayerStack; use crate::state::LoaderOverrides; -use crate::state::ProjectTrustScope; use crate::thread_config::ThreadConfigContext; use crate::thread_config::ThreadConfigLoader; use codex_app_server_protocol::ConfigLayerSource; @@ -718,7 +717,8 @@ fn project_layer_entry( dot_codex_folder: &AbsolutePathBuf, config: TomlValue, disabled_reason: Option, - project_trust_scope: ProjectTrustScope, + project_trust_key: String, + project_root: AbsolutePathBuf, ) -> ConfigLayerEntry { let source = ConfigLayerSource::Project { dot_codex_folder: dot_codex_folder.clone(), @@ -729,7 +729,7 @@ fn project_layer_entry( } else { ConfigLayerEntry::new(source, config) }; - entry.with_project_trust_scope(project_trust_scope) + entry.with_project_trust(project_trust_key, project_root) } fn sanitize_project_config(config: &mut TomlValue) -> Vec { @@ -997,10 +997,7 @@ async fn load_project_layers( let decision = trust_context.decision_for_dir(&dir); let disabled_reason = trust_context.disabled_reason_for_decision(&decision); - let project_trust_scope = ProjectTrustScope { - trust_key: decision.trust_key.clone(), - project_root: project_root.clone(), - }; + let project_trust_key = decision.trust_key.clone(); 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 { @@ -1025,7 +1022,8 @@ async fn load_project_layers( &dot_codex_abs, TomlValue::Table(toml::map::Map::new()), disabled_reason.clone(), - project_trust_scope.clone(), + project_trust_key.clone(), + project_root.clone(), )); continue; } @@ -1044,7 +1042,8 @@ async fn load_project_layers( &dot_codex_abs, config, disabled_reason.clone(), - project_trust_scope.clone(), + project_trust_key.clone(), + project_root.clone(), ); layers.push(entry); } @@ -1057,7 +1056,8 @@ async fn load_project_layers( &dot_codex_abs, TomlValue::Table(toml::map::Map::new()), disabled_reason, - project_trust_scope, + project_trust_key, + project_root.clone(), )); } else { let config_file_display = config_file.as_path().display(); diff --git a/codex-rs/config/src/state.rs b/codex-rs/config/src/state.rs index 03b88ea486..b8f79729c1 100644 --- a/codex-rs/config/src/state.rs +++ b/codex-rs/config/src/state.rs @@ -66,14 +66,8 @@ pub struct ConfigLayerEntry { pub raw_toml: Option, pub version: String, pub disabled_reason: Option, - pub project_trust_scope: Option, -} - -/// Worktree-aware trust scope resolved while loading one project layer. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectTrustScope { - pub trust_key: String, - pub project_root: AbsolutePathBuf, + pub project_trust_key: Option, + pub project_root: Option, } impl ConfigLayerEntry { @@ -85,7 +79,8 @@ impl ConfigLayerEntry { raw_toml: None, version, disabled_reason: None, - project_trust_scope: None, + project_trust_key: None, + project_root: None, } } @@ -97,7 +92,8 @@ impl ConfigLayerEntry { raw_toml: Some(raw_toml), version, disabled_reason: None, - project_trust_scope: None, + project_trust_key: None, + project_root: None, } } @@ -113,12 +109,18 @@ impl ConfigLayerEntry { raw_toml: None, version, disabled_reason: Some(disabled_reason.into()), - project_trust_scope: None, + project_trust_key: None, + project_root: None, } } - pub fn with_project_trust_scope(mut self, project_trust_scope: ProjectTrustScope) -> Self { - self.project_trust_scope = Some(project_trust_scope); + pub fn with_project_trust( + mut self, + project_trust_key: String, + project_root: AbsolutePathBuf, + ) -> Self { + self.project_trust_key = Some(project_trust_key); + self.project_root = Some(project_root); self } diff --git a/codex-rs/core/src/config/config_loader_tests.rs b/codex-rs/core/src/config/config_loader_tests.rs index 098a5f2626..0172a09ed7 100644 --- a/codex-rs/core/src/config/config_loader_tests.rs +++ b/codex-rs/core/src/config/config_loader_tests.rs @@ -330,7 +330,8 @@ async fn returns_empty_when_all_layers_missing() { raw_toml: None, version: version_for_toml(&TomlValue::Table(toml::map::Map::new())), disabled_reason: None, - project_trust_scope: None, + project_trust_key: None, + project_root: None, }, user_layer, ); @@ -1499,10 +1500,8 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s raw_toml: None, version: version_for_toml(&TomlValue::Table(toml::map::Map::new())), disabled_reason: None, - project_trust_scope: Some(codex_config::ProjectTrustScope { - trust_key: project_root.display().to_string(), - project_root: AbsolutePathBuf::from_absolute_path(project_root.clone())?, - }), + project_trust_key: Some(project_root.display().to_string()), + project_root: Some(AbsolutePathBuf::from_absolute_path(project_root.clone())?), }], project_layers ); @@ -1607,10 +1606,8 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul raw_toml: None, version: version_for_toml(&child_config), disabled_reason: None, - project_trust_scope: Some(codex_config::ProjectTrustScope { - trust_key: project_root.display().to_string(), - project_root: AbsolutePathBuf::from_absolute_path(project_root.clone())?, - }), + project_trust_key: Some(project_root.display().to_string()), + project_root: Some(AbsolutePathBuf::from_absolute_path(project_root.clone())?), }], project_layers ); diff --git a/codex-rs/hooks/src/engine/discovery.rs b/codex-rs/hooks/src/engine/discovery.rs index d1573b962b..e8914ced5a 100644 --- a/codex-rs/hooks/src/engine/discovery.rs +++ b/codex-rs/hooks/src/engine/discovery.rs @@ -507,20 +507,16 @@ fn hook_key_sources_for_config_layer( source_path: &AbsolutePathBuf, ) -> (String, Option) { let legacy_key_source = source_path.display().to_string(); - let Some(project_trust_scope) = layer.project_trust_scope.as_ref() else { + let (Some(project_trust_key), Some(project_root)) = ( + layer.project_trust_key.as_ref(), + layer.project_root.as_ref(), + ) else { return (legacy_key_source, None); }; - let Ok(relative_path) = source_path - .as_path() - .strip_prefix(project_trust_scope.project_root.as_path()) - else { + let Ok(relative_path) = source_path.as_path().strip_prefix(project_root.as_path()) else { return (legacy_key_source, None); }; - let key_source = format!( - "project:{}:{}", - project_trust_scope.trust_key, - relative_path.display() - ); + let key_source = format!("project:{project_trust_key}:{}", relative_path.display()); (key_source, Some(legacy_key_source)) } diff --git a/codex-rs/hooks/src/engine/mod_tests.rs b/codex-rs/hooks/src/engine/mod_tests.rs index 6200a5be22..471066a8d6 100644 --- a/codex-rs/hooks/src/engine/mod_tests.rs +++ b/codex-rs/hooks/src/engine/mod_tests.rs @@ -14,7 +14,6 @@ use codex_config::HookEventsToml; use codex_config::HookHandlerConfig; use codex_config::ManagedHooksRequirementsToml; use codex_config::MatcherGroup; -use codex_config::ProjectTrustScope; use codex_config::RequirementSource; use codex_config::TomlValue; use codex_plugin::PluginHookSource; @@ -368,7 +367,7 @@ fn user_disablement_does_not_filter_managed_layer_hooks() { } #[test] -fn project_hook_keys_share_project_trust_scope_across_worktrees() { +fn project_hook_keys_share_project_trust_across_worktrees() { let trust_key = test_path_buf("/repo").display().to_string(); let project_a = test_project_layer("/tmp/worktree-a", &trust_key); let project_b = test_project_layer("/tmp/worktree-b", &trust_key); @@ -482,10 +481,7 @@ fn test_project_layer(project_root: &str, trust_key: &str) -> ConfigLayerEntry { ConfigLayerSource::Project { dot_codex_folder }, config_with_pre_tool_use_hook("python3 /tmp/project.py"), ) - .with_project_trust_scope(ProjectTrustScope { - trust_key: trust_key.to_string(), - project_root, - }) + .with_project_trust(trust_key.to_string(), project_root) } fn expected_project_hook_key(trust_key: &str) -> String {