Make Guardian v2 parent compaction reuse configurable (#39691)

## What changed

- Add `features.guardianv2.reuse_parent_compaction`, defaulting to `true` to preserve existing behavior.
- When disabled, omit parent compaction items from Guardian v2 contributor requests.
- Cover configuration parsing and disabled-reuse request construction.

GitOrigin-RevId: 0cf7c0ac7a2567c9f73a3f9724df6f6a6170e995
This commit is contained in:
felixxia-oai
2026-08-20 10:57:03 +00:00
committed by copyberry
parent 312b62ac95
commit 478dbe9df0
6 changed files with 80 additions and 10 deletions
+3
View File
@@ -1299,6 +1299,9 @@
"reasoning_effort": {
"$ref": "#/definitions/ReasoningEffort"
},
"reuse_parent_compaction": {
"type": "boolean"
},
"review_scope": {
"$ref": "#/definitions/GuardianV2ReviewScopeConfigToml"
},
@@ -32,6 +32,7 @@ pub(crate) struct GuardianV2Config {
pub(crate) reasoning_effort: ReasoningEffort,
pub(crate) max_action_tokens: usize,
pub(crate) max_classifier_instruction_tokens: usize,
pub(crate) reuse_parent_compaction: bool,
pub(crate) max_parent_compaction_tokens: usize,
pub(crate) sandboxed_exec_commands: bool,
pub(crate) transcript: TranscriptConfig,
@@ -233,6 +234,7 @@ impl GuardianV2Config {
reasoning_effort: configured.reasoning_effort.unwrap_or(ReasoningEffort::Low),
max_action_tokens,
max_classifier_instruction_tokens,
reuse_parent_compaction: configured.reuse_parent_compaction.unwrap_or(true),
max_parent_compaction_tokens,
sandboxed_exec_commands: configured
.review_scope
@@ -466,16 +466,20 @@ impl GuardianV2Extension {
.latest_tool_call
.fetch_add(/*val*/ 1, Ordering::Relaxed)
.saturating_add(1);
let latest_parent_compaction = input
.conversation_history
.items()
.filter(|item| {
matches!(
item,
ResponseItem::Compaction { .. } | ResponseItem::ContextCompaction { .. }
)
})
.last();
let latest_parent_compaction = if guardian_config.reuse_parent_compaction {
input
.conversation_history
.items()
.filter(|item| {
matches!(
item,
ResponseItem::Compaction { .. } | ResponseItem::ContextCompaction { .. }
)
})
.last()
} else {
None
};
let parent_compaction = latest_parent_compaction.and_then(|item| {
encrypted_parent_compaction(
std::iter::once(item),
@@ -1911,6 +1911,63 @@ async fn contributor_reuses_the_latest_compatible_parent_compaction() -> Result<
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn contributor_can_disable_parent_compaction_reuse() -> Result<()> {
skip_if_no_network!(Ok(()));
let oversized_compaction = ResponseItem::Compaction {
id: Some(ResponseItemId::from_server("cmp_oversized".to_owned())),
encrypted_content: "a".repeat(TruncationPolicy::Tokens(/*limit*/ 256).byte_budget()),
internal_chat_message_metadata_passthrough: None,
};
let conversation_history = vec![
oversized_compaction,
ResponseItem::Message {
id: None,
role: "user".to_owned(),
content: vec![ContentItem::InputText {
text: "Inspect the repository guidelines.".to_owned(),
}],
phase: None,
internal_chat_message_metadata_passthrough: None,
},
];
let configuration = "[features.guardianv2]\nenabled = true\nreuse_parent_compaction = false\nmax_parent_compaction_tokens = 256\n";
let (request, test, _registry) = sample_configured_conversation_history(
conversation_history,
r#"{"path":"README.md"}"#,
Some(TEST_GUARDIAN_POLICY),
configuration,
/*model_defaults*/ None,
)
.await?;
let input = request["input"]
.as_array()
.expect("Luna request input should be an array");
assert_eq!(input.len(), 3);
assert_eq!(input[2]["role"], "user");
assert!(
input
.iter()
.all(|item| item["type"] != "compaction" && item["type"] != "context_compaction")
);
let thread_store = test.codex.thread_extension_data();
let score = tokio::time::timeout(Duration::from_secs(5), async {
loop {
if let Some(score) = thread_store.get::<SecurityRiskScore>() {
return score;
}
tokio::task::yield_now().await;
}
})
.await?;
assert_eq!(score.scores.get("action_risk"), Some(&0.8));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn contributor_bounds_oversized_actions_and_fairly_truncates_nested_fields() -> Result<()> {
skip_if_no_network!(Ok(()));
+2
View File
@@ -139,6 +139,8 @@ pub struct GuardianV2ConfigToml {
#[schemars(range(min = 100, max = 100000))]
pub max_classifier_instruction_tokens: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reuse_parent_compaction: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 100, max = 100000))]
pub max_parent_compaction_tokens: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
+2
View File
@@ -160,6 +160,7 @@ max_tool_call_lag = 2
reasoning_effort = "minimal"
max_action_tokens = 512
max_classifier_instruction_tokens = 256
reuse_parent_compaction = false
max_parent_compaction_tokens = 384
[guardianv2.review_scope]
@@ -187,6 +188,7 @@ max_recent_non_user_entries = 12
reasoning_effort: Some(codex_protocol::openai_models::ReasoningEffort::Minimal),
max_action_tokens: Some(512),
max_classifier_instruction_tokens: Some(256),
reuse_parent_compaction: Some(false),
max_parent_compaction_tokens: Some(384),
review_scope: Some(crate::GuardianV2ReviewScopeConfigToml {
sandboxed_exec_commands: Some(true),