mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
Retain inherited Guardian instructions in standalone forks (#43478)
## Why A standalone root forked from a worker needs its inherited user instructions for Guardian review, including after compaction. Parent input counters belong to a different thread, and omitted parent answers may have restricted an inherited authorization. ## What changed - Adopt inherited user instructions into thread-owned retained context for standalone roots, recovering surviving checkpoint messages when needed. - Keep inherited instructions before local evidence without sharing local acceptance counters, and preserve that distinction during reconciliation and rollback. - Mark parent verified answers as incomplete and treat recovered checkpoint instructions as incomplete excerpts. ## Testing Add coverage for standalone forks from copied history and truncated checkpoints, retention through compaction and resume, inherited/local ordering collisions, and rollback scope. GitOrigin-RevId: bf716e224a1d29d6c528ef0dd7cdb118a413a08c
This commit is contained in:
1 parent
aa12ab45df
commit
16ff14c266
19 files changed
+646
-131
No files matched your search
@@ -17,6 +17,7 @@ use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::UserMessageEvent;
|
||||
use codex_rollout::CompactedItem;
|
||||
use codex_rollout::RetainedContextEntry;
|
||||
use codex_rollout::RetainedInputSource;
|
||||
use codex_rollout::RolloutItem;
|
||||
use codex_rollout::RolloutLine;
|
||||
|
||||
@@ -42,7 +43,7 @@ struct PendingUserResponse {
|
||||
struct InstructionBoundary {
|
||||
record_index: usize,
|
||||
message_id: Option<ResponseItemId>,
|
||||
acceptance_order: Option<u64>,
|
||||
input_source: RetainedInputSource,
|
||||
alive: bool,
|
||||
}
|
||||
|
||||
@@ -161,10 +162,7 @@ impl RollbackPlanner {
|
||||
} else if rollback::counts_as_boundary(&response.item) {
|
||||
let boundary = self.start_boundary(index);
|
||||
self.boundaries[boundary].message_id = response.id().cloned();
|
||||
self.boundaries[boundary].acceptance_order = response
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.user_input_order);
|
||||
self.boundaries[boundary].input_source = response.metadata.as_ref().into();
|
||||
if let ResponseItem::Message { role, content, .. } = &response.item
|
||||
&& role == "user"
|
||||
{
|
||||
@@ -323,7 +321,7 @@ impl RollbackPlanner {
|
||||
self.boundaries.push(InstructionBoundary {
|
||||
record_index: index,
|
||||
message_id: None,
|
||||
acceptance_order: None,
|
||||
input_source: RetainedInputSource::Local(None),
|
||||
alive: true,
|
||||
});
|
||||
let had_prior_boundary = !self.boundary_stack.is_empty();
|
||||
@@ -380,7 +378,8 @@ impl RollbackPlanner {
|
||||
}
|
||||
if let Some(boundary) = first_removed_boundary {
|
||||
let source = &self.boundaries[boundary];
|
||||
if let Some(order) = source.acceptance_order {
|
||||
let acceptance_order = source.input_source.acceptance_order();
|
||||
if let Some(order) = acceptance_order {
|
||||
// An answer may have been persisted before the queued instruction
|
||||
// accepted ahead of it. Both are removed at that acceptance boundary.
|
||||
for fact in &self.retained_fact_sources {
|
||||
@@ -411,10 +410,10 @@ impl RollbackPlanner {
|
||||
// including in checkpoints. Legacy evidence uses the recorded boundary.
|
||||
// Later checkpoints have not been observed yet and already reflect this rollback.
|
||||
for frame in self.compactions.iter_mut().rev().take_while(|frame| {
|
||||
source.acceptance_order.is_some() || frame.record_index >= source.record_index
|
||||
acceptance_order.is_some() || frame.record_index >= source.record_index
|
||||
}) {
|
||||
if let Some(context) = &mut frame.item.retained_context {
|
||||
if source.acceptance_order.is_some()
|
||||
if acceptance_order.is_some()
|
||||
|| context
|
||||
.ordered_entries()
|
||||
.any(|(_, entry)| matches!(entry, RetainedContextEntry::UserMessage(_)))
|
||||
@@ -422,7 +421,7 @@ impl RollbackPlanner {
|
||||
context.rollback(
|
||||
&removed_turns,
|
||||
source.message_id.as_ref().map(ResponseItemId::as_str),
|
||||
source.acceptance_order,
|
||||
source.input_source,
|
||||
);
|
||||
} else {
|
||||
// Checkpoints written without instruction retention keep the legacy
|
||||
|
||||
Reference in new issue
Block a user