Persist Guardian V2 risk scores without restoring them (#40884)

## What changed

- Append accepted Guardian V2 classification results to rollout history for non-ephemeral threads.
- Keep resumed and forked threads from restoring a persisted score into active Guardian state.

## Testing

- Verify that asynchronous scoring records the resulting `SecurityRiskScore` rollout item.
- Seed resume and fork tests with a persisted score and verify that approvals ignore it.

GitOrigin-RevId: 40d63a2ea7e7cf6398402c6aed7d5e5727dc9d68
This commit is contained in:
jif authored and copyberry committed 2026-08-26 13:54:36 +00:00
1 parent f5420174da
commit 10d5a603ae
6 files changed
+70 -23

No files matched your search

@@ -29,6 +29,7 @@ use codex_extension_api::ToolName;
use codex_extension_api::ToolPayload;
use codex_extension_api::ToolStartInput;
use codex_features::Feature;
use codex_history::RolloutItem;
use codex_login::AgentIdentityAuthPolicy;
use codex_login::AuthManager;
use codex_model_provider::create_model_provider;
@@ -796,6 +797,19 @@ impl GuardianV2Extension {
.latest_scored_tool_call
.fetch_max(tool_call_index, Ordering::Release);
classification_finished_at = Some(Instant::now());
if !config.ephemeral
&& let Err(error) = thread
.append_rollout_items(&[RolloutItem::SecurityRiskScore(score)])
.await
{
tracing::warn!(
%thread_id,
%turn_id,
%call_id,
%error,
"failed to persist Guardian V2 classification result"
);
}
Ok("success")
}
.await;
@@ -22,6 +22,7 @@ use codex_extension_api::ToolName;
use codex_extension_api::ToolPayload;
use codex_extension_api::ToolStartInput;
use codex_features::Feature;
use codex_history::RolloutItem;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::ExternalAuth;
@@ -1726,6 +1727,27 @@ async fn contributor_samples_tool_calls_with_the_existing_luna_pool() -> Result<
}
);
assert!(score.sampled_at.is_some());
test.codex.ensure_rollout_materialized().await;
let persisted_score = tokio::time::timeout(Duration::from_secs(5), async {
loop {
if let Some(persisted_score) = test
.codex
.load_history(/*include_archived*/ false)
.await?
.items
.into_iter()
.find_map(|item| match item {
RolloutItem::SecurityRiskScore(score) => Some(score),
_ => None,
})
{
return Ok::<_, anyhow::Error>(persisted_score);
}
tokio::task::yield_now().await;
}
})
.await??;
assert_eq!(&persisted_score, score.as_ref());
assert_eq!(
registry
.fast_approval_decision(