mirror of
https://github.com/openai/codex.git
synced 2026-11-03 16:23:51 +00:00
Preserve Guardian history across thread reconstruction (#42065)
## Why Guardian review evidence must remain available after a compacted thread is resumed or forked, while rolled-back or parent-local evidence must not grant authorization in a different context. ## What changed - Store the bounded, model-invisible Guardian transcript alongside compacted rollout history and restore it from the newest surviving checkpoint during replay. - Trim Guardian history at rollback boundaries, clearing it when the boundary has already been evicted. - Preserve the checkpoint for user-initiated forks, but remove it when spawning a subagent so parent review evidence is not inherited. - Keep the new rollout field optional for compatibility with existing compacted records and legacy readers. ## Testing - Cover Guardian history across compaction, restart, paginated and pathless stores, user forks, rollback, bounded replay, serialization, and subagent forks. GitOrigin-RevId: 6ab076d5e0c3aac4d6a41ebd50ba2b200c90213e
This commit is contained in:
1 parent
3a04482645
commit
28097e98eb
23 files changed
+369
-27
No files matched your search
@@ -1,7 +1,7 @@
|
||||
//! Bounded, chronological review evidence independent of the parent's compaction.
|
||||
//!
|
||||
//! User messages and other entries have separate limits and keep their original order.
|
||||
//! Hosts append original items and reset this history on rollback or reconstruction.
|
||||
//! Hosts append original items, restore bounded checkpoints, and trim rolled-back turns.
|
||||
//! Clones share immutable payloads; eviction changes the generation so readers cannot
|
||||
//! reuse an offset into a different retained prefix. Prompt selection remains caller-owned.
|
||||
|
||||
@@ -142,6 +142,19 @@ impl TranscriptHistory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a rolled-back boundary and everything after it. If retention already
|
||||
/// evicted the boundary, discard the remaining evidence rather than keep later grants.
|
||||
pub fn truncate_before(&mut self, boundary: &ResponseItem) {
|
||||
let index = self.items.iter().position(|(item, _)| {
|
||||
item.id()
|
||||
.zip(boundary.id())
|
||||
.is_some_and(|(item_id, boundary_id)| item_id == boundary_id)
|
||||
|| item.as_ref() == boundary
|
||||
});
|
||||
self.items.truncate(index.unwrap_or(0));
|
||||
self.generation = self.generation.saturating_add(1);
|
||||
}
|
||||
|
||||
/// Generation of the retained prefix, independent of parent compaction.
|
||||
pub fn generation(&self) -> u64 {
|
||||
self.generation
|
||||
|
||||
@@ -18,6 +18,19 @@ fn tool(text: &str) -> ResponseItem {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rollback_keeps_the_earlier_prefix_or_clears_an_evicted_boundary() {
|
||||
let items = [message("keep"), message("roll back"), tool("later")];
|
||||
let mut history = TranscriptHistory::default();
|
||||
history.reset(items.iter());
|
||||
let generation = history.generation();
|
||||
history.truncate_before(&items[1]);
|
||||
assert_eq!(history.items().collect::<Vec<_>>(), vec![&items[0]]);
|
||||
assert!(history.generation() > generation);
|
||||
history.truncate_before(&items[1]);
|
||||
assert_eq!(history.items().count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_kind_evicts_its_own_oldest_entries_without_reordering() {
|
||||
let users = [message("first"), message("second"), message("third")];
|
||||
|
||||
Reference in new issue
Block a user