Reuse guardian session across approvals (#14668)

## Summary
- reuse a guardian subagent session across approvals so reviews keep a
stable prompt cache key and avoid one-shot startup overhead
- clear the guardian child history before each review so prior guardian
decisions do not leak into later approvals
- include the `smart_approvals` -> `guardian_approval` feature flag
rename in the same PR to minimize release latency on a very tight
timeline
- add regression coverage for prompt-cache-key reuse without
prior-review prompt bleed

## Request
- Bug/enhancement request: internal guardian prompt-cache and latency
improvement request

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Charley Cunningham
2026-03-15 22:56:18 -07:00
committed by GitHub
parent ba463a9dc7
commit 6fdeb1d602
26 changed files with 3132 additions and 1504 deletions

View File

@@ -2807,7 +2807,7 @@ async fn set_feature_enabled_updates_profile() -> anyhow::Result<()> {
ConfigEditsBuilder::new(codex_home.path())
.with_profile(Some("dev"))
.set_feature_enabled("smart_approvals", true)
.set_feature_enabled("guardian_approval", true)
.apply()
.await?;
@@ -2822,14 +2822,14 @@ async fn set_feature_enabled_updates_profile() -> anyhow::Result<()> {
profile
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
Some(&true),
);
assert_eq!(
parsed
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
None,
);
@@ -2843,13 +2843,13 @@ async fn set_feature_enabled_persists_default_false_feature_disable_in_profile()
ConfigEditsBuilder::new(codex_home.path())
.with_profile(Some("dev"))
.set_feature_enabled("smart_approvals", true)
.set_feature_enabled("guardian_approval", true)
.apply()
.await?;
ConfigEditsBuilder::new(codex_home.path())
.with_profile(Some("dev"))
.set_feature_enabled("smart_approvals", false)
.set_feature_enabled("guardian_approval", false)
.apply()
.await?;
@@ -2864,14 +2864,14 @@ async fn set_feature_enabled_persists_default_false_feature_disable_in_profile()
profile
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
Some(&false),
);
assert_eq!(
parsed
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
None,
);
@@ -2883,13 +2883,13 @@ async fn set_feature_enabled_profile_disable_overrides_root_enable() -> anyhow::
let codex_home = TempDir::new()?;
ConfigEditsBuilder::new(codex_home.path())
.set_feature_enabled("smart_approvals", true)
.set_feature_enabled("guardian_approval", true)
.apply()
.await?;
ConfigEditsBuilder::new(codex_home.path())
.with_profile(Some("dev"))
.set_feature_enabled("smart_approvals", false)
.set_feature_enabled("guardian_approval", false)
.apply()
.await?;
@@ -2904,14 +2904,14 @@ async fn set_feature_enabled_profile_disable_overrides_root_enable() -> anyhow::
parsed
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
Some(&true),
);
assert_eq!(
profile
.features
.as_ref()
.and_then(|features| features.entries.get("smart_approvals")),
.and_then(|features| features.entries.get("guardian_approval")),
Some(&false),
);
@@ -5518,7 +5518,7 @@ async fn approvals_reviewer_stays_manual_only_when_guardian_feature_is_enabled()
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"[features]
smart_approvals = true
guardian_approval = true
"#,
)?;
@@ -5533,7 +5533,8 @@ smart_approvals = true
}
#[tokio::test]
async fn approvals_reviewer_can_be_set_in_config_without_smart_approvals() -> std::io::Result<()> {
async fn approvals_reviewer_can_be_set_in_config_without_guardian_approval() -> std::io::Result<()>
{
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
@@ -5552,7 +5553,8 @@ async fn approvals_reviewer_can_be_set_in_config_without_smart_approvals() -> st
}
#[tokio::test]
async fn approvals_reviewer_can_be_set_in_profile_without_smart_approvals() -> std::io::Result<()> {
async fn approvals_reviewer_can_be_set_in_profile_without_guardian_approval() -> std::io::Result<()>
{
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
@@ -5577,12 +5579,12 @@ approvals_reviewer = "guardian_subagent"
}
#[tokio::test]
async fn guardian_approval_alias_is_migrated_to_smart_approvals() -> std::io::Result<()> {
async fn smart_approvals_alias_is_migrated_to_guardian_approval() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"[features]
guardian_approval = true
smart_approvals = true
"#,
)?;
@@ -5600,22 +5602,22 @@ guardian_approval = true
);
let serialized = tokio::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE)).await?;
assert!(serialized.contains("smart_approvals = true"));
assert!(serialized.contains("guardian_approval = true"));
assert!(serialized.contains("approvals_reviewer = \"guardian_subagent\""));
assert!(!serialized.contains("guardian_approval"));
assert!(!serialized.contains("smart_approvals"));
Ok(())
}
#[tokio::test]
async fn guardian_approval_alias_is_migrated_in_profiles() -> std::io::Result<()> {
async fn smart_approvals_alias_is_migrated_in_profiles() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"profile = "guardian"
[profiles.guardian.features]
guardian_approval = true
smart_approvals = true
"#,
)?;
@@ -5634,15 +5636,51 @@ guardian_approval = true
let serialized = tokio::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE)).await?;
assert!(serialized.contains("[profiles.guardian.features]"));
assert!(serialized.contains("smart_approvals = true"));
assert!(serialized.contains("guardian_approval = true"));
assert!(serialized.contains("approvals_reviewer = \"guardian_subagent\""));
assert!(!serialized.contains("guardian_approval"));
assert!(!serialized.contains("smart_approvals"));
Ok(())
}
#[tokio::test]
async fn guardian_approval_alias_migration_preserves_existing_approvals_reviewer()
async fn smart_approvals_alias_migration_preserves_disabled_profile_override() -> std::io::Result<()>
{
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"[features]
guardian_approval = true
[profiles.guardian.features]
smart_approvals = false
"#,
)?;
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.harness_overrides(ConfigOverrides {
config_profile: Some("guardian".to_string()),
..Default::default()
})
.build()
.await?;
assert!(!config.features.enabled(Feature::GuardianApproval));
assert_eq!(config.features.legacy_feature_usages().count(), 0);
assert_eq!(config.approvals_reviewer, ApprovalsReviewer::User);
let serialized = tokio::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE)).await?;
assert!(serialized.contains("[profiles.guardian.features]"));
assert!(serialized.contains("guardian_approval = false"));
assert!(!serialized.contains("smart_approvals"));
Ok(())
}
#[tokio::test]
async fn smart_approvals_alias_migration_preserves_existing_approvals_reviewer()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
@@ -5650,7 +5688,7 @@ async fn guardian_approval_alias_migration_preserves_existing_approvals_reviewer
r#"approvals_reviewer = "user"
[features]
guardian_approval = true
smart_approvals = true
"#,
)?;
@@ -5664,9 +5702,38 @@ guardian_approval = true
assert_eq!(config.approvals_reviewer, ApprovalsReviewer::User);
let serialized = tokio::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE)).await?;
assert!(serialized.contains("smart_approvals = true"));
assert!(serialized.contains("guardian_approval = true"));
assert!(serialized.contains("approvals_reviewer = \"user\""));
assert!(!serialized.contains("guardian_approval"));
assert!(!serialized.contains("smart_approvals"));
Ok(())
}
#[tokio::test]
async fn smart_approvals_alias_migration_does_not_override_canonical_disabled_flag()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"[features]
guardian_approval = false
smart_approvals = true
"#,
)?;
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.build()
.await?;
assert!(!config.features.enabled(Feature::GuardianApproval));
assert_eq!(config.approvals_reviewer, ApprovalsReviewer::User);
let serialized = tokio::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE)).await?;
assert!(serialized.contains("guardian_approval = false"));
assert!(!serialized.contains("approvals_reviewer = \"guardian_subagent\""));
assert!(!serialized.contains("smart_approvals"));
Ok(())
}