Extract Guardian review policy into a dedicated crate (#44227)

## What changed

Move assessment parsing and schema, model selection, review outcomes, retry handling, and rejection circuit breakers into `codex-guardian-reviewer`. Update core to use the extracted APIs while retaining session execution and decision enforcement in the host.

## Testing

Move existing assessment, circuit breaker, and retry tests into the new crate, including coverage for transient errors, cancellation, and deadlines.

GitOrigin-RevId: 96ec9989a0066acb012bf3bec0b9f7d8bf11a4ef
This commit is contained in:
jif authored and copyberry committed 2026-09-09 17:31:37 +00:00
1 parent a2e83a783e
commit e8e7103cb9
19 files changed
+730 -570

No files matched your search

-105
View File
@@ -146,111 +146,6 @@ impl codex_extension_api::ContextContributor for GuardianMemoryContextProbe {
}
}
#[test]
fn guardian_rejection_circuit_breaker_interrupts_after_three_consecutive_denials() {
let mut circuit_breaker = GuardianRejectionCircuitBreaker::default();
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::InterruptTurn {
consecutive_denials: 3,
recent_denials: 3,
}
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
}
#[test]
fn guardian_rejection_circuit_breaker_interrupts_cyber_models_after_one_denial() {
let mut circuit_breaker = GuardianRejectionCircuitBreaker::default();
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::CyberModel),
GuardianRejectionCircuitBreakerAction::InterruptTurn {
consecutive_denials: 1,
recent_denials: 1,
}
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::CyberModel),
GuardianRejectionCircuitBreakerAction::Continue
);
}
#[test]
fn guardian_rejection_circuit_breaker_resets_consecutive_denials_on_non_denial() {
let mut circuit_breaker = GuardianRejectionCircuitBreaker::default();
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
circuit_breaker.record_non_denial("turn-1");
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::InterruptTurn {
consecutive_denials: 3,
recent_denials: 4,
}
);
}
#[test]
fn auto_review_rejection_circuit_breaker_interrupts_after_ten_recent_denials() {
let mut circuit_breaker = GuardianRejectionCircuitBreaker::default();
for _ in 0..9 {
assert_eq!(
circuit_breaker
.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
circuit_breaker.record_non_denial("turn-1");
}
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::InterruptTurn {
consecutive_denials: 1,
recent_denials: 10,
}
);
}
#[test]
fn auto_review_rejection_circuit_breaker_forgets_denials_outside_recent_review_window() {
let mut circuit_breaker = GuardianRejectionCircuitBreaker::default();
for _ in 0..9 {
assert_eq!(
circuit_breaker
.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
circuit_breaker.record_non_denial("turn-1");
}
for _ in 0..(AUTO_REVIEW_DENIAL_WINDOW_SIZE - 18) {
circuit_breaker.record_non_denial("turn-1");
}
assert_eq!(
circuit_breaker.record_denial("turn-1", GuardianRejectionCircuitBreakerPolicy::Standard),
GuardianRejectionCircuitBreakerAction::Continue
);
}
async fn guardian_test_session_and_turn(
server: &wiremock::MockServer,
) -> (Arc<Session>, Arc<TurnContext>) {