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 authored and copyberry committed 2026-08-20 10:57:03 +00:00
1 parent 312b62ac95
commit 478dbe9df0
6 files changed
+80 -10

No files matched your search

@@ -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(()));