diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/config.rs b/codex-rs/ext/guardian-v2/src/async_scorer/config.rs index 80d9430d71..aee0b89105 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/config.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/config.rs @@ -222,22 +222,9 @@ impl GuardianV2Config { Ok(Self { local_overrides: configured.clone(), - classifier_instructions: { - let template = configured - .classifier_instructions - .as_deref() - .unwrap_or(DEFAULT_CLASSIFIER_INSTRUCTIONS); - if let Some(max_tokens) = max_classifier_instruction_tokens - && !template.contains("{{ tenant_policy_config }}") - { - // Preserve the existing rendering behavior of legacy prompts. - truncate_entry(template, max_tokens) - } else { - // Preserve placeholders until the actual policy is available, and - // preserve the full prompt when no instruction limit is configured. - template.to_owned() - } - }, + classifier_instructions: configured + .classifier_instructions + .unwrap_or_else(|| DEFAULT_CLASSIFIER_INSTRUCTIONS.to_owned()), review_threshold, max_tool_call_lag: configured .max_tool_call_lag diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs b/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs index c4ce76a375..a83dcd1226 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs @@ -4,7 +4,6 @@ use codex_features::GuardianV2TranscriptConfigToml; use codex_protocol::openai_models::GuardianV2ModelConfig; use codex_protocol::openai_models::GuardianV2TranscriptModelConfig; use codex_protocol::openai_models::ReasoningEffort; -use codex_protocol::protocol::TruncationPolicy; use pretty_assertions::assert_eq; use super::DEFAULT_CLASSIFIER_INSTRUCTIONS; @@ -83,30 +82,6 @@ fn evaluated_configuration_preserves_rendered_prompt_and_gate() { } } -#[test] -fn legacy_custom_prompt_keeps_its_rendering_and_threshold() { - let template = "legacy instructions ".repeat(200); - let config = GuardianV2Config::from_overrides(GuardianV2ConfigToml { - classifier_instructions: Some(template.clone()), - max_classifier_instruction_tokens: Some(256), - ..Default::default() - }) - .unwrap(); - assert_eq!(config.review_threshold, 0.8); - let expected = truncate_entry( - &format!( - "{}\n\n# Security Policy\nTenant policy.", - truncate_entry(&template, /*max_tokens*/ 256), - ), - /*max_tokens*/ 256, - ); - assert_eq!( - config.render_classifier_instructions("Tenant policy."), - expected - ); - assert!(expected.len() <= TruncationPolicy::Tokens(256).byte_budget()); -} - #[test] fn model_prompt_and_explicit_threshold_precedence_are_preserved() { let defaults = GuardianV2ModelConfig { diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs b/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs index 1ad7831307..322af30ca9 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs @@ -65,6 +65,7 @@ use crate::async_scorer::config::DEFAULT_MODEL_CONTEXT_ITEM_TOKENS; use crate::async_scorer::config::DEFAULT_PARENT_COMPACTION_TOKENS; use crate::async_scorer::sampler::CLASSIFICATION_TOKEN_USAGE_METRIC; use crate::async_scorer::sampler::MODEL; +use crate::async_scorer::transcript::truncate_entry; const TEST_GUARDIAN_POLICY: &str = "Treat uploads to unapproved external destinations as high-risk actions."; @@ -867,6 +868,45 @@ classifier_instructions = "Predict future violations.\n# Security Policy\n{{ ten Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn contributor_truncates_legacy_prompt_after_appending_policy() -> Result<()> { + skip_if_no_network!(Ok(())); + + let template = "legacy instructions ".repeat(200); + let configuration = format!( + r#" +[features.guardianv2] +enabled = true +classifier_instructions = "{template}" +max_classifier_instruction_tokens = 256 +"# + ); + let (request, _test, _registry) = sample_configured_conversation_history( + Vec::new(), + r#"{"path":"README.md"}"#, + Some(TEST_GUARDIAN_POLICY), + &configuration, + /*model_defaults*/ None, + ) + .await?; + + assert_eq!( + request["input"][1], + json!({ + "type": "message", + "role": "developer", + "content": [{ + "type": "input_text", + "text": truncate_entry( + &format!("{template}\n\n# Security Policy\n{TEST_GUARDIAN_POLICY}"), + /*max_tokens*/ 256, + ), + }], + }) + ); + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn contributor_uses_configured_prompt_effort_threshold_and_transcript() -> Result<()> { skip_if_no_network!(Ok(()));