mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Persist Guardian review failures for reports across restarts (#51651)
## Why Failed Guardian review evidence was process-local, so restarting Codex lost the context needed for later diagnostic reports. ## What changed - Persist failed-review records in SQLite before reviewer cleanup, retaining at most 64 records globally, eight per thread, and 8 MiB of evidence. Delete persisted evidence when its reviewed thread is deleted. - Recover and deduplicate persisted and in-memory records for the reported task tree when uploading `auto-review-failures.jsonl`. - Bound persistence attempts to 250 ms and retain an in-memory fallback when storage is unavailable. Keep capture gated by user opt-in and non-ephemeral sessions, and export evidence only when logs are included. ## Testing Add coverage for persistence across reopen and process restart, retention limits, cascading deletion, busy-storage fallback, and report uploads with and without logs, including SQLite log failures. GitOrigin-RevId: ea2da1818d4a5da3da29f211042ca45e834342bc
This commit is contained in:
@@ -2207,10 +2207,31 @@ async fn guardian_denial_rejects_tool_call_with_rationale(
|
||||
assert!(guardian_request.body_contains_text(&command));
|
||||
assert_eq!(guardian_request.body_json()["model"], "gpt-5.6-luna");
|
||||
|
||||
let feedback = codex_feedback::guardian_review_failures(&[test.session_configured.thread_id])
|
||||
.attachment
|
||||
.expect("failed Guardian review");
|
||||
test.codex.shutdown_and_wait().await?;
|
||||
let state_db = codex_state::StateRuntime::init(
|
||||
test.config.sqlite.clone(),
|
||||
test.config.model_provider_id.clone(),
|
||||
)
|
||||
.await?;
|
||||
let durable = state_db.list_guardian_review_records().await?;
|
||||
assert_eq!(
|
||||
durable.len(),
|
||||
1,
|
||||
"the Guardian capture must persist, not just fall back to memory"
|
||||
);
|
||||
let feedback = codex_feedback::guardian_review_failures(
|
||||
Some(&state_db),
|
||||
&[test.session_configured.thread_id],
|
||||
)
|
||||
.await
|
||||
.attachment
|
||||
.expect("failed Guardian review");
|
||||
let record: serde_json::Value = serde_json::from_slice(&feedback.buffer)?;
|
||||
assert_eq!(
|
||||
record,
|
||||
serde_json::from_slice::<serde_json::Value>(&durable[0].record)?
|
||||
);
|
||||
assert_eq!(record["instructions"], guardian_request.instructions_text());
|
||||
assert!(
|
||||
guardian_request.body_contains_text(record["action"].as_str().expect("reviewed action"))
|
||||
);
|
||||
|
||||
@@ -1389,7 +1389,11 @@ async fn run_guardian_subagent_review(
|
||||
.thread_manager
|
||||
.list_agent_subtree_thread_ids(root_thread_id)
|
||||
.await?;
|
||||
let failures = codex_feedback::guardian_review_failures(&feedback_thread_ids);
|
||||
let failures = codex_feedback::guardian_review_failures(
|
||||
test.codex.state_db().as_deref(),
|
||||
&feedback_thread_ids,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(failures.thread_ids, vec![worker_thread_id]);
|
||||
let feedback = failures.attachment.expect("failed worker review");
|
||||
let record: Value = serde_json::from_slice(&feedback.buffer)?;
|
||||
|
||||
Reference in New Issue
Block a user