Improve Guardian retries and review failure reporting (#44482)

## Why

Transient rate limits can end automatic approval reviews prematurely, and review failures currently report high risk even when no assessment completed.

## What changed

- Retry rate limits and recoverable exhausted-stream errors, while excluding non-transient HTTP failures.
- Preserve server retry delays after stream retries are exhausted and honor them within the review deadline. Scope retry advice to the current turn so reused sessions cannot apply stale delays.
- Keep failed reviews denied, but leave risk and authorization unset and explain that the review could not complete without declaring the action unsafe.

## Testing

Add an integration test covering rate-limit recovery through approval and tool execution, asserting that the action executes exactly once after approval. Update failure assertions to check absent assessment fields and the review-failure explanation.

GitOrigin-RevId: 1163cfde35c6b8eb23b6f24f1f24461b86ded838
This commit is contained in:
jif authored and copyberry committed 2026-09-10 09:50:59 +00:00
1 parent 03f014564d
commit 5d3fe48b08
11 files changed
+226 -42

No files matched your search

@@ -18,8 +18,6 @@ use codex_app_server_protocol::ApprovalsReviewer;
use codex_app_server_protocol::AskForApproval;
use codex_app_server_protocol::GuardianApprovalReview;
use codex_app_server_protocol::GuardianApprovalReviewStatus;
use codex_app_server_protocol::GuardianRiskLevel;
use codex_app_server_protocol::GuardianUserAuthorization;
use codex_app_server_protocol::ItemGuardianApprovalReviewCompletedNotification;
use codex_app_server_protocol::ThreadCompactStartParams;
use codex_app_server_protocol::ThreadCompactStartResponse;
@@ -445,8 +443,8 @@ async fn guardians_retain_evidence_after_compaction_and_discard_it_after_rollbac
assessment.review,
GuardianApprovalReview {
status: GuardianApprovalReviewStatus::Denied,
risk_level: Some(GuardianRiskLevel::High),
user_authorization: Some(GuardianUserAuthorization::Unknown),
risk_level: None,
user_authorization: None,
rationale: Some(format!("Automatic approval review failed: {reason}")),
}
);
@@ -475,9 +473,9 @@ async fn guardians_retain_evidence_after_compaction_and_discard_it_after_rollbac
})
.expect("declined tool result");
assert!(
output
.to_string()
.contains("This action was rejected due to unacceptable risk."),
output.to_string().contains(
"This is a review failure, not a determination that the action is unsafe."
),
"tool must not execute: {output}",
);
assert_eq!(