mirror of
https://github.com/openai/codex.git
synced 2026-11-04 00:24:43 +00:00
Reuse parent compactions in Guardian review sessions (#37513)
## What changed - Add the under-development `guardian_reuse_parent_compaction` feature. - Restart Guardian review sessions after parent history rewrites and seed them with the latest encrypted compaction that has a response item ID. - Keep the existing reviewer when a rewritten history has no reusable compaction, preserving authorization and restriction context held by that session. ## Testing - Cover reuse-key invalidation and compaction eligibility. - Verify review-session behavior across parent compaction and a subsequent summary-free history reset. GitOrigin-RevId: 891805d3c3dca34ddda6e3bfc5097be4ff164267
This commit is contained in:
1 parent
208f05b233
commit
c2bcb9a26b
4 files changed
+260
-33
No files matched your search
@@ -570,6 +570,9 @@
|
|||||||
"guardian_approval": {
|
"guardian_approval": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"guardian_reuse_parent_compaction": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"guardianv2": {
|
"guardianv2": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@@ -5235,6 +5238,9 @@
|
|||||||
"guardian_approval": {
|
"guardian_approval": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"guardian_reuse_parent_compaction": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"guardianv2": {
|
"guardianv2": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ struct GuardianReviewForkSnapshot {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
struct GuardianReviewSessionReuseKey {
|
struct GuardianReviewSessionReuseKey {
|
||||||
// Only include settings that affect spawned-session behavior so reuse
|
// Only include settings that affect spawned-session behavior and parent
|
||||||
// invalidation remains explicit and does not depend on unrelated config
|
// history rewrites that invalidate existing reviewer context.
|
||||||
// bookkeeping.
|
parent_history_version: u64,
|
||||||
model: Option<String>,
|
model: Option<String>,
|
||||||
model_provider_id: String,
|
model_provider_id: String,
|
||||||
model_provider: ModelProviderInfo,
|
model_provider: ModelProviderInfo,
|
||||||
@@ -184,8 +184,17 @@ impl GuardianReviewSessionReuseKey {
|
|||||||
fn from_spawn_config(
|
fn from_spawn_config(
|
||||||
spawn_config: &Config,
|
spawn_config: &Config,
|
||||||
user_instructions: Option<UserInstructions>,
|
user_instructions: Option<UserInstructions>,
|
||||||
|
parent_history_version: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
parent_history_version: if spawn_config
|
||||||
|
.features
|
||||||
|
.enabled(Feature::GuardianReuseParentCompaction)
|
||||||
|
{
|
||||||
|
parent_history_version
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
model: spawn_config.model.clone(),
|
model: spawn_config.model.clone(),
|
||||||
model_provider_id: spawn_config.model_provider_id.clone(),
|
model_provider_id: spawn_config.model_provider_id.clone(),
|
||||||
model_provider: spawn_config.model_provider.clone(),
|
model_provider: spawn_config.model_provider.clone(),
|
||||||
@@ -210,6 +219,29 @@ impl GuardianReviewSessionReuseKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encrypted_parent_compaction(items: &[ResponseItem]) -> Option<ResponseItem> {
|
||||||
|
let item = items.iter().rev().find(|item| {
|
||||||
|
matches!(
|
||||||
|
item,
|
||||||
|
ResponseItem::Compaction { .. } | ResponseItem::ContextCompaction { .. }
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match item {
|
||||||
|
ResponseItem::Compaction {
|
||||||
|
id: Some(_),
|
||||||
|
encrypted_content,
|
||||||
|
..
|
||||||
|
} if !encrypted_content.is_empty() => Some(item.clone()),
|
||||||
|
ResponseItem::ContextCompaction {
|
||||||
|
id: Some(_),
|
||||||
|
encrypted_content: Some(encrypted_content),
|
||||||
|
..
|
||||||
|
} if !encrypted_content.is_empty() => Some(item.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn prompt_cache_key_override_for_review_session(
|
pub(crate) fn prompt_cache_key_override_for_review_session(
|
||||||
session_source: &SessionSource,
|
session_source: &SessionSource,
|
||||||
parent_thread_id: Option<ThreadId>,
|
parent_thread_id: Option<ThreadId>,
|
||||||
@@ -311,9 +343,16 @@ impl GuardianReviewSessionManager {
|
|||||||
let spawn_config = guardian_review_session_config(&parent_session, &parent_turn)
|
let spawn_config = guardian_review_session_config(&parent_session, &parent_turn)
|
||||||
.await?
|
.await?
|
||||||
.spawn_config;
|
.spawn_config;
|
||||||
|
let parent_history = parent_session.clone_history().await;
|
||||||
|
let parent_compaction = spawn_config
|
||||||
|
.features
|
||||||
|
.enabled(Feature::GuardianReuseParentCompaction)
|
||||||
|
.then(|| encrypted_parent_compaction(parent_history.raw_items()))
|
||||||
|
.flatten();
|
||||||
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&spawn_config,
|
&spawn_config,
|
||||||
parent_session.user_instructions().await,
|
parent_session.user_instructions().await,
|
||||||
|
parent_history.history_version(),
|
||||||
);
|
);
|
||||||
let spawn_cancel_token = self.cancellation_token.child_token();
|
let spawn_cancel_token = self.cancellation_token.child_token();
|
||||||
let spawn_cancel_guard = spawn_cancel_token.clone().drop_guard();
|
let spawn_cancel_guard = spawn_cancel_token.clone().drop_guard();
|
||||||
@@ -323,6 +362,7 @@ impl GuardianReviewSessionManager {
|
|||||||
spawn_config,
|
spawn_config,
|
||||||
reuse_key,
|
reuse_key,
|
||||||
spawn_cancel_token.clone(),
|
spawn_cancel_token.clone(),
|
||||||
|
parent_compaction,
|
||||||
/*fork_snapshot*/ None,
|
/*fork_snapshot*/ None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -375,9 +415,17 @@ impl GuardianReviewSessionManager {
|
|||||||
params: GuardianReviewSessionParams,
|
params: GuardianReviewSessionParams,
|
||||||
) -> (GuardianReviewSessionOutcome, GuardianReviewAnalyticsResult) {
|
) -> (GuardianReviewSessionOutcome, GuardianReviewAnalyticsResult) {
|
||||||
let deadline = params.deadline;
|
let deadline = params.deadline;
|
||||||
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let parent_history = params.parent_session.clone_history().await;
|
||||||
|
let parent_compaction = params
|
||||||
|
.spawn_config
|
||||||
|
.features
|
||||||
|
.enabled(Feature::GuardianReuseParentCompaction)
|
||||||
|
.then(|| encrypted_parent_compaction(parent_history.raw_items()))
|
||||||
|
.flatten();
|
||||||
|
let mut next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
¶ms.spawn_config,
|
¶ms.spawn_config,
|
||||||
params.parent_session.user_instructions().await,
|
params.parent_session.user_instructions().await,
|
||||||
|
parent_history.history_version(),
|
||||||
);
|
);
|
||||||
let mut stale_trunk_to_shutdown = None;
|
let mut stale_trunk_to_shutdown = None;
|
||||||
let mut spawned_trunk = false;
|
let mut spawned_trunk = false;
|
||||||
@@ -389,6 +437,13 @@ impl GuardianReviewSessionManager {
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut state) => {
|
Ok(mut state) => {
|
||||||
|
if parent_compaction.is_none()
|
||||||
|
&& let Some(trunk) = state.trunk.as_ref()
|
||||||
|
{
|
||||||
|
// Without a decryptable summary, the existing reviewer may
|
||||||
|
// hold the only remaining authorization or restriction.
|
||||||
|
next_reuse_key.parent_history_version = trunk.reuse_key.parent_history_version;
|
||||||
|
}
|
||||||
if let Some(trunk) = state.trunk.as_ref()
|
if let Some(trunk) = state.trunk.as_ref()
|
||||||
&& trunk.reuse_key != next_reuse_key
|
&& trunk.reuse_key != next_reuse_key
|
||||||
&& trunk.review_lock.try_acquire().is_ok()
|
&& trunk.review_lock.try_acquire().is_ok()
|
||||||
@@ -408,6 +463,7 @@ impl GuardianReviewSessionManager {
|
|||||||
params.spawn_config.clone(),
|
params.spawn_config.clone(),
|
||||||
next_reuse_key.clone(),
|
next_reuse_key.clone(),
|
||||||
spawn_cancel_token.clone(),
|
spawn_cancel_token.clone(),
|
||||||
|
parent_compaction.clone(),
|
||||||
/*fork_snapshot*/ None,
|
/*fork_snapshot*/ None,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
@@ -453,6 +509,7 @@ impl GuardianReviewSessionManager {
|
|||||||
params,
|
params,
|
||||||
next_reuse_key,
|
next_reuse_key,
|
||||||
deadline,
|
deadline,
|
||||||
|
parent_compaction,
|
||||||
/*fork_snapshot*/ None,
|
/*fork_snapshot*/ None,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
@@ -465,6 +522,7 @@ impl GuardianReviewSessionManager {
|
|||||||
params,
|
params,
|
||||||
next_reuse_key,
|
next_reuse_key,
|
||||||
deadline,
|
deadline,
|
||||||
|
parent_compaction,
|
||||||
trunk.fork_snapshot().await,
|
trunk.fork_snapshot().await,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
@@ -503,6 +561,7 @@ impl GuardianReviewSessionManager {
|
|||||||
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
session.get_config().await.as_ref(),
|
session.get_config().await.as_ref(),
|
||||||
session.user_instructions().await,
|
session.user_instructions().await,
|
||||||
|
session.clone_history().await.history_version(),
|
||||||
);
|
);
|
||||||
self.state.lock().await.trunk = Some(Arc::new(GuardianReviewSession {
|
self.state.lock().await.trunk = Some(Arc::new(GuardianReviewSession {
|
||||||
reuse_key,
|
reuse_key,
|
||||||
@@ -523,6 +582,7 @@ impl GuardianReviewSessionManager {
|
|||||||
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
session.get_config().await.as_ref(),
|
session.get_config().await.as_ref(),
|
||||||
session.user_instructions().await,
|
session.user_instructions().await,
|
||||||
|
session.clone_history().await.history_version(),
|
||||||
);
|
);
|
||||||
self.state
|
self.state
|
||||||
.lock()
|
.lock()
|
||||||
@@ -606,6 +666,7 @@ impl GuardianReviewSessionManager {
|
|||||||
params: GuardianReviewSessionParams,
|
params: GuardianReviewSessionParams,
|
||||||
reuse_key: GuardianReviewSessionReuseKey,
|
reuse_key: GuardianReviewSessionReuseKey,
|
||||||
deadline: tokio::time::Instant,
|
deadline: tokio::time::Instant,
|
||||||
|
parent_compaction: Option<ResponseItem>,
|
||||||
fork_snapshot: Option<GuardianReviewForkSnapshot>,
|
fork_snapshot: Option<GuardianReviewForkSnapshot>,
|
||||||
) -> (GuardianReviewSessionOutcome, GuardianReviewAnalyticsResult) {
|
) -> (GuardianReviewSessionOutcome, GuardianReviewAnalyticsResult) {
|
||||||
let spawn_cancel_token = self.cancellation_token.child_token();
|
let spawn_cancel_token = self.cancellation_token.child_token();
|
||||||
@@ -621,6 +682,7 @@ impl GuardianReviewSessionManager {
|
|||||||
fork_config,
|
fork_config,
|
||||||
reuse_key,
|
reuse_key,
|
||||||
spawn_cancel_token.clone(),
|
spawn_cancel_token.clone(),
|
||||||
|
parent_compaction,
|
||||||
fork_snapshot,
|
fork_snapshot,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
@@ -663,6 +725,7 @@ async fn spawn_guardian_review_session(
|
|||||||
spawn_config: Config,
|
spawn_config: Config,
|
||||||
reuse_key: GuardianReviewSessionReuseKey,
|
reuse_key: GuardianReviewSessionReuseKey,
|
||||||
cancel_token: CancellationToken,
|
cancel_token: CancellationToken,
|
||||||
|
parent_compaction: Option<ResponseItem>,
|
||||||
fork_snapshot: Option<GuardianReviewForkSnapshot>,
|
fork_snapshot: Option<GuardianReviewForkSnapshot>,
|
||||||
) -> anyhow::Result<GuardianReviewSession> {
|
) -> anyhow::Result<GuardianReviewSession> {
|
||||||
let (initial_history, prior_review_count, initial_transcript_cursor) = match fork_snapshot {
|
let (initial_history, prior_review_count, initial_transcript_cursor) = match fork_snapshot {
|
||||||
@@ -671,7 +734,12 @@ async fn spawn_guardian_review_session(
|
|||||||
fork_snapshot.prior_review_count,
|
fork_snapshot.prior_review_count,
|
||||||
fork_snapshot.last_reviewed_transcript_cursor,
|
fork_snapshot.last_reviewed_transcript_cursor,
|
||||||
),
|
),
|
||||||
None => (None, 0, None),
|
None => (
|
||||||
|
parent_compaction
|
||||||
|
.map(|item| InitialHistory::Forked(vec![RolloutItem::ResponseItem(item)])),
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let (session, io) = Box::pin(run_codex_thread_interactive(
|
let (session, io) = Box::pin(run_codex_thread_interactive(
|
||||||
spawn_config,
|
spawn_config,
|
||||||
@@ -1163,6 +1231,7 @@ mod tests {
|
|||||||
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
session.get_config().await.as_ref(),
|
session.get_config().await.as_ref(),
|
||||||
session.user_instructions().await,
|
session.user_instructions().await,
|
||||||
|
session.clone_history().await.history_version(),
|
||||||
);
|
);
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -1303,6 +1372,7 @@ mod tests {
|
|||||||
let cached_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let cached_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&cached_spawn_config,
|
&cached_spawn_config,
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut changed_parent_config = parent_config;
|
let mut changed_parent_config = parent_config;
|
||||||
@@ -1319,6 +1389,7 @@ mod tests {
|
|||||||
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&next_spawn_config,
|
&next_spawn_config,
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1331,8 +1402,60 @@ mod tests {
|
|||||||
GuardianReviewSessionReuseKey::from_spawn_config(
|
GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&cached_spawn_config,
|
&cached_spawn_config,
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cached_reuse_key,
|
||||||
|
GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
|
&cached_spawn_config,
|
||||||
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut compaction_enabled_config = cached_spawn_config;
|
||||||
|
compaction_enabled_config
|
||||||
|
.features
|
||||||
|
.enable(Feature::GuardianReuseParentCompaction)
|
||||||
|
.expect("Guardian parent-compaction reuse should be configurable");
|
||||||
|
assert_ne!(
|
||||||
|
GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
|
&compaction_enabled_config,
|
||||||
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
|
),
|
||||||
|
GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
|
&compaction_enabled_config,
|
||||||
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypted_parent_compaction_requires_original_item_id() {
|
||||||
|
let item = ResponseItem::Compaction {
|
||||||
|
id: Some(codex_protocol::ResponseItemId::from_server(
|
||||||
|
"cmp_guardian_parent_summary".to_string(),
|
||||||
|
)),
|
||||||
|
encrypted_content: "encrypted guardian parent summary".to_string(),
|
||||||
|
internal_chat_message_metadata_passthrough: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
encrypted_parent_compaction(std::slice::from_ref(&item)),
|
||||||
|
Some(item)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
encrypted_parent_compaction(&[ResponseItem::Compaction {
|
||||||
|
id: None,
|
||||||
|
encrypted_content: "encrypted guardian parent summary".to_string(),
|
||||||
|
internal_chat_message_metadata_passthrough: None,
|
||||||
|
}]),
|
||||||
|
None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -1389,6 +1512,7 @@ mod tests {
|
|||||||
let cached_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let cached_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&cached_spawn_config,
|
&cached_spawn_config,
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut changed_parent_config = parent_config;
|
let mut changed_parent_config = parent_config;
|
||||||
@@ -1405,6 +1529,7 @@ mod tests {
|
|||||||
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
&next_spawn_config,
|
&next_spawn_config,
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
|
/*parent_history_version*/ 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_ne!(cached_reuse_key, next_reuse_key);
|
assert_ne!(cached_reuse_key, next_reuse_key);
|
||||||
@@ -1767,6 +1892,11 @@ mod tests {
|
|||||||
review_session.reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
review_session.reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||||
¶ms.spawn_config,
|
¶ms.spawn_config,
|
||||||
params.parent_session.user_instructions().await,
|
params.parent_session.user_instructions().await,
|
||||||
|
params
|
||||||
|
.parent_session
|
||||||
|
.clone_history()
|
||||||
|
.await
|
||||||
|
.history_version(),
|
||||||
);
|
);
|
||||||
let manager = GuardianReviewSessionManager {
|
let manager = GuardianReviewSessionManager {
|
||||||
state: Arc::new(Mutex::new(GuardianReviewSessionState {
|
state: Arc::new(Mutex::new(GuardianReviewSessionState {
|
||||||
|
|||||||
@@ -2053,15 +2053,27 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
),
|
),
|
||||||
ev_completed("resp-guardian-3"),
|
ev_completed("resp-guardian-3"),
|
||||||
]),
|
]),
|
||||||
|
sse(vec![
|
||||||
|
ev_response_created("resp-guardian-4"),
|
||||||
|
ev_assistant_message(
|
||||||
|
"msg-guardian-4",
|
||||||
|
"{\"risk_level\":\"low\",\"user_authorization\":\"high\",\"outcome\":\"allow\",\"rationale\":\"fourth guardian rationale\"}",
|
||||||
|
),
|
||||||
|
ev_completed("resp-guardian-4"),
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (session, mut turn) = guardian_test_session_and_turn(&server).await;
|
let (session, mut turn) = guardian_test_session_and_turn(&server).await;
|
||||||
Arc::get_mut(&mut turn)
|
let mut config = (*turn.config).clone();
|
||||||
.expect("turn should be unique")
|
config
|
||||||
.model_info
|
.features
|
||||||
.auto_review_model_override = Some("codex-auto-review".to_string());
|
.enable(Feature::GuardianReuseParentCompaction)
|
||||||
|
.expect("Guardian parent-compaction reuse should be configurable");
|
||||||
|
let turn_mut = Arc::get_mut(&mut turn).expect("turn should be unique");
|
||||||
|
turn_mut.model_info.auto_review_model_override = Some("codex-auto-review".to_string());
|
||||||
|
turn_mut.config = Arc::new(config);
|
||||||
seed_guardian_parent_history(&session, &turn).await;
|
seed_guardian_parent_history(&session, &turn).await;
|
||||||
|
|
||||||
let first_request = GuardianApprovalRequest::Shell {
|
let first_request = GuardianApprovalRequest::Shell {
|
||||||
@@ -2135,10 +2147,32 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
/*max_attempts*/ 1,
|
/*max_attempts*/ 1,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
let committed_rollout_items = session
|
||||||
|
.guardian_review_session
|
||||||
|
.committed_fork_rollout_items_for_test()
|
||||||
|
.await
|
||||||
|
.expect("committed guardian fork snapshot");
|
||||||
|
assert_eq!(
|
||||||
|
committed_rollout_items
|
||||||
|
.iter()
|
||||||
|
.filter(|item| rollout_item_contains_message_text(
|
||||||
|
item,
|
||||||
|
"Use prior reviews as context, not binding precedent."
|
||||||
|
))
|
||||||
|
.count(),
|
||||||
|
1,
|
||||||
|
"follow-up reminder should be persisted for guardian forks"
|
||||||
|
);
|
||||||
session
|
session
|
||||||
.record_conversation_items(
|
.replace_history(
|
||||||
turn.as_ref(),
|
vec![
|
||||||
&[
|
ResponseItem::Compaction {
|
||||||
|
id: Some(codex_protocol::ResponseItemId::from_server(
|
||||||
|
"cmp_guardian_parent_summary".to_string(),
|
||||||
|
)),
|
||||||
|
encrypted_content: "encrypted guardian parent summary".to_string(),
|
||||||
|
internal_chat_message_metadata_passthrough: None,
|
||||||
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
@@ -2158,6 +2192,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
/*reference_context_item*/ None,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let third_request = GuardianApprovalRequest::Shell {
|
let third_request = GuardianApprovalRequest::Shell {
|
||||||
@@ -2181,6 +2216,30 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
/*max_attempts*/ 1,
|
/*max_attempts*/ 1,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
session
|
||||||
|
.replace_history(
|
||||||
|
vec![ResponseItem::Message {
|
||||||
|
id: None,
|
||||||
|
role: "user".to_string(),
|
||||||
|
content: vec![ContentItem::InputText {
|
||||||
|
text: "Please review after a summary-free context reset.".to_string(),
|
||||||
|
}],
|
||||||
|
phase: None,
|
||||||
|
internal_chat_message_metadata_passthrough: None,
|
||||||
|
}],
|
||||||
|
/*reference_context_item*/ None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let fourth_outcome = run_guardian_review_session_for_test(
|
||||||
|
Arc::clone(&session),
|
||||||
|
Arc::clone(&turn),
|
||||||
|
guardian_shell_request("shell-4"),
|
||||||
|
ApprovalRequestReasons::default(),
|
||||||
|
guardian_output_schema(),
|
||||||
|
/*external_cancel*/ None,
|
||||||
|
/*max_attempts*/ 1,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let (GuardianReviewOutcome::Completed(first_assessment), first_metadata) = first_outcome else {
|
let (GuardianReviewOutcome::Completed(first_assessment), first_metadata) = first_outcome else {
|
||||||
panic!("expected first guardian assessment");
|
panic!("expected first guardian assessment");
|
||||||
@@ -2192,9 +2251,14 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
let (GuardianReviewOutcome::Completed(third_assessment), third_metadata) = third_outcome else {
|
let (GuardianReviewOutcome::Completed(third_assessment), third_metadata) = third_outcome else {
|
||||||
panic!("expected third guardian assessment");
|
panic!("expected third guardian assessment");
|
||||||
};
|
};
|
||||||
|
let (GuardianReviewOutcome::Completed(fourth_assessment), fourth_metadata) = fourth_outcome
|
||||||
|
else {
|
||||||
|
panic!("expected fourth guardian assessment");
|
||||||
|
};
|
||||||
assert_eq!(first_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
assert_eq!(first_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
||||||
assert_eq!(second_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
assert_eq!(second_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
||||||
assert_eq!(third_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
assert_eq!(third_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
||||||
|
assert_eq!(fourth_assessment.outcome, GuardianAssessmentOutcome::Allow);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
first_metadata.guardian_session_kind,
|
first_metadata.guardian_session_kind,
|
||||||
Some(codex_analytics::GuardianReviewSessionKind::TrunkNew)
|
Some(codex_analytics::GuardianReviewSessionKind::TrunkNew)
|
||||||
@@ -2205,6 +2269,10 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
third_metadata.guardian_session_kind,
|
third_metadata.guardian_session_kind,
|
||||||
|
Some(codex_analytics::GuardianReviewSessionKind::TrunkNew)
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
fourth_metadata.guardian_session_kind,
|
||||||
Some(codex_analytics::GuardianReviewSessionKind::TrunkReused)
|
Some(codex_analytics::GuardianReviewSessionKind::TrunkReused)
|
||||||
));
|
));
|
||||||
ThreadId::from_string(
|
ThreadId::from_string(
|
||||||
@@ -2230,26 +2298,49 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
.expect("third guardian thread id should be a valid UUID");
|
.expect("third guardian thread id should be a valid UUID");
|
||||||
assert_eq!(first_metadata.had_prior_review_context, Some(false));
|
assert_eq!(first_metadata.had_prior_review_context, Some(false));
|
||||||
assert_eq!(second_metadata.had_prior_review_context, Some(true));
|
assert_eq!(second_metadata.had_prior_review_context, Some(true));
|
||||||
assert_eq!(third_metadata.had_prior_review_context, Some(true));
|
assert_eq!(third_metadata.had_prior_review_context, Some(false));
|
||||||
|
assert_eq!(fourth_metadata.had_prior_review_context, Some(true));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
first_metadata.guardian_thread_id,
|
first_metadata.guardian_thread_id,
|
||||||
second_metadata.guardian_thread_id
|
second_metadata.guardian_thread_id
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_ne!(
|
||||||
second_metadata.guardian_thread_id,
|
second_metadata.guardian_thread_id,
|
||||||
third_metadata.guardian_thread_id
|
third_metadata.guardian_thread_id
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
third_metadata.guardian_thread_id,
|
||||||
|
fourth_metadata.guardian_thread_id
|
||||||
|
);
|
||||||
|
|
||||||
let requests = request_log.requests();
|
let requests = request_log.requests();
|
||||||
assert_eq!(requests.len(), 3);
|
assert_eq!(requests.len(), 4);
|
||||||
|
|
||||||
let first_body = requests[0].body_json();
|
let first_body = requests[0].body_json();
|
||||||
let second_body = requests[1].body_json();
|
let second_body = requests[1].body_json();
|
||||||
let third_body = requests[2].body_json();
|
let third_body = requests[2].body_json();
|
||||||
|
let fourth_body = requests[3].body_json();
|
||||||
|
let third_input = third_body["input"]
|
||||||
|
.as_array()
|
||||||
|
.expect("guardian review should include input items");
|
||||||
|
assert!(third_input.iter().any(|item| {
|
||||||
|
item["type"] == "compaction"
|
||||||
|
&& item["id"] == "cmp_guardian_parent_summary"
|
||||||
|
&& item["encrypted_content"] == "encrypted guardian parent summary"
|
||||||
|
}));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
first_body["prompt_cache_key"],
|
first_body["prompt_cache_key"],
|
||||||
second_body["prompt_cache_key"]
|
second_body["prompt_cache_key"]
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
second_body["prompt_cache_key"],
|
||||||
|
third_body["prompt_cache_key"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
third_body["prompt_cache_key"],
|
||||||
|
fourth_body["prompt_cache_key"]
|
||||||
|
);
|
||||||
|
assert!(fourth_body.to_string().contains("third guardian rationale"));
|
||||||
assert!(
|
assert!(
|
||||||
second_body.to_string().contains(concat!(
|
second_body.to_string().contains(concat!(
|
||||||
"Use prior reviews as context, not binding precedent. ",
|
"Use prior reviews as context, not binding precedent. ",
|
||||||
@@ -2269,25 +2360,17 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
.to_string()
|
.to_string()
|
||||||
.matches("Use prior reviews as context, not binding precedent.")
|
.matches("Use prior reviews as context, not binding precedent.")
|
||||||
.count(),
|
.count(),
|
||||||
1,
|
0,
|
||||||
"later follow-up guardian requests should not append the reminder again"
|
"a fresh guardian session should not inherit the follow-up reminder"
|
||||||
);
|
|
||||||
let committed_rollout_items = session
|
|
||||||
.guardian_review_session
|
|
||||||
.committed_fork_rollout_items_for_test()
|
|
||||||
.await
|
|
||||||
.expect("committed guardian fork snapshot");
|
|
||||||
assert_eq!(
|
|
||||||
committed_rollout_items
|
|
||||||
.iter()
|
|
||||||
.filter(|item| rollout_item_contains_message_text(
|
|
||||||
item,
|
|
||||||
"Use prior reviews as context, not binding precedent."
|
|
||||||
))
|
|
||||||
.count(),
|
|
||||||
1,
|
|
||||||
"follow-up reminder should be persisted for guardian forks"
|
|
||||||
);
|
);
|
||||||
|
let third_user_message = requests[2]
|
||||||
|
.message_input_text_groups("user")
|
||||||
|
.last()
|
||||||
|
.expect("fresh guardian user message")
|
||||||
|
.join("");
|
||||||
|
assert!(third_user_message.contains(">>> TRANSCRIPT START\n"));
|
||||||
|
assert!(third_user_message.contains("Please push the third docs fix too."));
|
||||||
|
assert!(!third_body.to_string().contains(first_rationale));
|
||||||
let second_user_message = requests[1]
|
let second_user_message = requests[1]
|
||||||
.message_input_text_groups("user")
|
.message_input_text_groups("user")
|
||||||
.last()
|
.last()
|
||||||
|
|||||||
@@ -247,6 +247,8 @@ pub enum Feature {
|
|||||||
DefaultModeRequestUserInput,
|
DefaultModeRequestUserInput,
|
||||||
/// Enable automatic review for approval prompts.
|
/// Enable automatic review for approval prompts.
|
||||||
GuardianApproval,
|
GuardianApproval,
|
||||||
|
/// Reuse encrypted parent compaction when restarting Guardian review sessions.
|
||||||
|
GuardianReuseParentCompaction,
|
||||||
/// Enable Guardian V2 automatic approval reviews.
|
/// Enable Guardian V2 automatic approval reviews.
|
||||||
GuardianV2,
|
GuardianV2,
|
||||||
/// Enable persisted thread goals and automatic goal continuation.
|
/// Enable persisted thread goals and automatic goal continuation.
|
||||||
@@ -1365,6 +1367,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
|||||||
stage: Stage::Stable,
|
stage: Stage::Stable,
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
},
|
},
|
||||||
|
FeatureSpec {
|
||||||
|
id: Feature::GuardianReuseParentCompaction,
|
||||||
|
key: "guardian_reuse_parent_compaction",
|
||||||
|
stage: Stage::UnderDevelopment,
|
||||||
|
default_enabled: false,
|
||||||
|
},
|
||||||
FeatureSpec {
|
FeatureSpec {
|
||||||
id: Feature::GuardianV2,
|
id: Feature::GuardianV2,
|
||||||
key: "guardianv2",
|
key: "guardianv2",
|
||||||
|
|||||||
Reference in new issue
Block a user