Centralize Guardian root context preparation (#57425)

## Why

Verified answers omitted because of request-size limits must be marked as missing. Legacy checkpoint messages also need to match retained excerpts after truncation so workers receive only one copy.

## What changed

Move root message preparation into `codex-guardian-context::prepare_root_context`, including handoff selection, shared message limits, legacy matching, and incomplete-context markers. Pass original legacy text into preparation and mark verified answers incomplete when oversized records are excluded.

## Testing

Add a handoff regression scenario for an ID-less checkpoint with a retained excerpt and an oversized verified answer. Assert that the worker receives one capped user message, both incomplete-evidence markers, and an incomplete authorization version.

GitOrigin-RevId: bd3f5fe164ae2fddba440e9269fd63b52e8b12f8
This commit is contained in:
ioanbudea-OAI
2026-10-27 10:29:14 +00:00
committed by copyberry
parent 124e4e5bfa
commit 5c6ef3d1a6
6 changed files with 278 additions and 183 deletions
@@ -290,6 +290,41 @@ pub(crate) async fn handoff_scenario() -> Result<Vec<ResponsesRequest>> {
);
assert_eq!(root_messages(&leaf).await, expected);
// An old ID-less checkpoint stores an excerpt beside the original legacy backup.
// The worker must receive one capped copy and preserve both evidence gaps.
let original = "Never publish project details. ".repeat(200);
let excerpt = codex_guardian_context::truncate_text(&original, /*max_tokens*/ 900);
let history = serde_json::from_value(json!([{"type":"compacted", "payload": {
"message":"Old ID-less checkpoint.", "replacement_history":[],
"guardian_history":[{"type":"message", "role":"user",
"content":[{"type":"input_text", "text":original}]}],
"retained_context":{
"user_messages":[{"turn_id":"", "text":excerpt, "complete":false}],
"user_messages_incomplete":true, "incomplete":false,
"verified_answers":[{"turn_id":"large", "call_id":"large", "questions":[{
"question":"Publish?", "answer":"Only if conditions hold. ".repeat(200)
}]}]
}
}}]))?;
test.codex =
crate::suite::guardian_checkpoint_migration::resume(&test, &test.codex, history).await?;
let snapshot = leaf.guardian_root_snapshot().await.expect("migrated root");
assert_eq!(
(
snapshot.messages,
snapshot.authorization_version.retained_context_complete
),
(
vec![
GuardianRootMessage::RetainedContextScope,
GuardianRootMessage::IncompleteRootInstructions,
GuardianRootMessage::IncompleteVerifiedAnswers,
GuardianRootMessage::User(excerpt),
],
false
)
);
let shutdown = test
.thread_manager
.shutdown_all_threads_bounded(Duration::from_secs(/*secs*/ 10))