Improve Guardian report diagnostics (#42033)

## Why

Reports from a large thread tree can exceed the rollout attachment limit, making
the threads involved in a failed Guardian review harder to diagnose.

## What changed

- Prioritize descendants with retained failed reviews when selecting bounded
  report rollouts, while always preserving the reported thread.
- Include each selected thread's available Guardian trunk rollout alongside its
  conversation rollout.
- Add a thread index attachment with selected filenames, bounded omission details,
  retained failure threads, and the process-wide discarded-record count.
- Link failed-review records to their reviewed turn and target item when available.

## Testing

- Cover failed-review prioritization, bounded omission metadata, discarded-record
  accounting, and turn/item linkage in Guardian report records.

GitOrigin-RevId: ae7916c27a3c279e899cd5a6ead8a4b7e0cc26a1
This commit is contained in:
jif
2026-09-01 11:14:20 +00:00
committed by copyberry
parent 8209978616
commit 2e5ee418ad
13 changed files with 293 additions and 66 deletions
+8 -1
View File
@@ -3,6 +3,8 @@
use super::GuardianAssessmentOutcome;
use super::approval_request::format_guardian_action_pretty;
use super::approval_request::guardian_request_target_item_id;
use super::approval_request::guardian_request_turn_id;
use super::prompt::parse_guardian_assessment;
use super::review_session::GuardianReviewSessionOutcome;
use super::review_session::GuardianReviewSessionParams;
@@ -20,6 +22,7 @@ const MAX_RECORD_BYTES: usize = 4 * 1024 * 1024;
struct ReviewFeedbackRecord<'a> {
reviewed_thread_id: ThreadId,
reviewed_turn_id: &'a str,
target_item_id: Option<&'a str>,
reviewer_thread_id: ThreadId,
model: &'a str,
status: &'a str,
@@ -64,7 +67,11 @@ pub(super) async fn record_failed_review(
let history = reviewer.clone_history().await;
ReviewFeedbackRecord {
reviewed_thread_id: params.parent_session.thread_id(),
reviewed_turn_id: &params.parent_context.turn().sub_id,
reviewed_turn_id: guardian_request_turn_id(
&params.request,
&params.parent_context.turn().sub_id,
),
target_item_id: guardian_request_target_item_id(&params.request),
reviewer_thread_id: reviewer.thread_id(),
model: &params.model,
status,
+4 -1
View File
@@ -11,6 +11,7 @@ fn oversized_context_keeps_the_action_and_decision() -> anyhow::Result<()> {
ReviewFeedbackRecord {
reviewed_thread_id: thread_id,
reviewed_turn_id: "parent-turn",
target_item_id: Some("push-call"),
reviewer_thread_id,
model: "review-model",
status: "denied",
@@ -22,7 +23,8 @@ fn oversized_context_keeps_the_action_and_decision() -> anyhow::Result<()> {
context_omitted: false,
}
.store();
let contents = codex_feedback::guardian_review_failures_attachment(&[thread_id])
let contents = codex_feedback::guardian_review_failures(&[thread_id])
.attachment
.expect("failed-review record")
.buffer;
assert_eq!(
@@ -30,6 +32,7 @@ fn oversized_context_keeps_the_action_and_decision() -> anyhow::Result<()> {
json!({
"reviewed_thread_id": thread_id,
"reviewed_turn_id": "parent-turn",
"target_item_id": "push-call",
"reviewer_thread_id": reviewer_thread_id,
"model": "review-model",
"status": "denied",
+8 -1
View File
@@ -3619,11 +3619,15 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
.send(())
.expect("second guardian review gate should still be open");
assert_eq!(second_review.await?, ReviewDecision::Approved);
let feedback = codex_feedback::guardian_review_failures_attachment(&[session.thread_id()])
let feedback = codex_feedback::guardian_review_failures(&[session.thread_id()])
.attachment
.expect("failed ephemeral review survives cleanup and subsequent allowed reviews");
let record: serde_json::Value = serde_json::from_slice(&feedback.buffer)?;
assert_eq!(
serde_json::json!({
"reviewed_thread_id": record["reviewed_thread_id"],
"reviewed_turn_id": record["reviewed_turn_id"],
"target_item_id": record["target_item_id"],
"reviewer_thread_id": record["reviewer_thread_id"],
"status": record["status"],
"decision": record["decision"],
@@ -3632,6 +3636,9 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
)?["command"],
}),
serde_json::json!({
"reviewed_thread_id": session.thread_id(),
"reviewed_turn_id": turn.sub_id,
"target_item_id": "shell-guardian-3",
"reviewer_thread_id": failed_ephemeral_request_body["client_metadata"]["thread_id"],
"status": "invalid_decision",
"decision": "not valid guardian json",