core: collapse reference turn context state

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Charles Cunningham
2026-03-16 20:45:29 -07:00
co-authored by Codex
parent 66e13b89ba
commit 79cf16c434
5 changed files with 55 additions and 78 deletions
+9 -6
View File
@@ -187,12 +187,13 @@ pub enum SteerInputError {
EmptyInput,
}
/// Notes from the previous real user turn.
/// Notes from the last stored turn-context snapshot that future prompt construction
/// should diff against.
///
/// Conceptually this is the same role that `previous_model` used to fill, but
/// it can carry other prior-turn settings that matter when constructing
/// sensible state-change diffs or full-context reinjection, such as model
/// switches or detecting a prior `realtime_active -> false` transition.
/// Conceptually this is the same role that `previous_model` used to fill, but it
/// can carry other prior-turn settings that matter when constructing sensible
/// state-change diffs or full-context reinjection, such as model switches or
/// detecting a prior `realtime_active -> false` transition.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct PreviousTurnSettings {
pub(crate) model: String,
@@ -2147,6 +2148,8 @@ impl Session {
.await;
{
let mut state = self.state.lock().await;
// The injected initial context becomes the new baseline for future diffs,
// so advance both the stored settings snapshot and the reference baseline.
state.set_reference_context_item(Some(turn_context.to_turn_context_item()));
}
@@ -2174,7 +2177,7 @@ impl Session {
.previous_turn_settings();
let surviving_turn_context_item = reconstructed_rollout
.reference_turn_context_state
.latest_turn_context_item();
.turn_context_item();
let mut state = self.state.lock().await;
state.replace_history_with_reference_turn_context_state(
reconstructed_rollout.history,
@@ -64,39 +64,26 @@ fn merge_surviving_segment_turn_context_state(
segment_turn_context_state: ReferenceTurnContextState,
counts_as_user_turn: bool,
) {
// Only real user turns should backfill "previous turn settings". Standalone task turns may
// carry lifecycle events, but they must not become the latest real turn context.
let segment_is_newest_stored_turn_context =
reference_turn_context_state.turn_context_item().is_none();
// Only real user turns may establish the surviving stored turn-context snapshot.
// Standalone task turns can carry lifecycle events, but they must not become the source of
// future settings diffs or rollback metadata.
if counts_as_user_turn
&& reference_turn_context_state
.latest_turn_context_item()
.is_none()
&& let Some(turn_context_item) = segment_turn_context_state.latest_turn_context_item()
&& segment_is_newest_stored_turn_context
&& let Some(turn_context_item) = segment_turn_context_state.turn_context_item()
{
reference_turn_context_state.set_latest_turn_context_item(Some(turn_context_item));
reference_turn_context_state.set_turn_context_item_during_reverse_replay(turn_context_item);
}
// A compaction seen in this segment hides older reference baselines, but it must not erase a
// newer stored reference baseline we already captured from a later surviving user turn.
if segment_turn_context_state.compacted_since_model_saw_reference_turn_context()
&& reference_turn_context_state
.stored_reference_turn_context_item()
.is_none()
// A compaction seen in this segment shadows any older baseline, but it must not hide a newer
// stored turn context we already captured from a later surviving user turn.
if segment_is_newest_stored_turn_context
&& segment_turn_context_state.compacted_since_model_saw_reference_turn_context()
{
reference_turn_context_state.note_compaction();
}
// The model-visible reference baseline comes from the newest surviving user turn that both
// carries a stored baseline and has not been hidden by a later surviving compaction.
if counts_as_user_turn
&& !reference_turn_context_state.compacted_since_model_saw_reference_turn_context()
&& reference_turn_context_state
.stored_reference_turn_context_item()
.is_none()
&& let Some(turn_context_item) =
segment_turn_context_state.stored_reference_turn_context_item()
{
reference_turn_context_state.set_reference_context_item(Some(turn_context_item));
}
}
impl Session {
@@ -212,9 +199,7 @@ impl Session {
if base_replacement_history.is_some()
&& pending_rollback_turns == 0
&& reference_turn_context_state
.latest_turn_context_item()
.is_some()
&& reference_turn_context_state.turn_context_item().is_some()
{
// At this point the replay-derived metadata and replacement-history base for the
// surviving tail are both fixed, so older rollout items cannot affect this result.
@@ -257,7 +257,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
serde_json::to_value(
reconstructed
.reference_turn_context_state
.latest_turn_context_item(),
.turn_context_item(),
)
.expect("serialize surviving turn context item"),
serde_json::to_value(Some(first_context_item))
@@ -491,7 +491,7 @@ async fn reconstruct_history_rollback_backfills_surviving_turn_context_from_olde
serde_json::to_value(
reconstructed
.reference_turn_context_state
.latest_turn_context_item(),
.turn_context_item(),
)
.expect("serialize surviving turn context item"),
serde_json::to_value(Some(first_context_item))
+5 -5
View File
@@ -1033,7 +1033,7 @@ async fn record_initial_history_reconstructs_forked_transcript() {
}
#[tokio::test]
async fn record_initial_history_forked_hydrates_previous_turn_settings() {
async fn record_initial_history_forked_advances_previous_turn_settings_to_injected_baseline() {
let (session, turn_context) = make_session_and_context().await;
let previous_model = "forked-rollout-model";
let previous_context_item = TurnContextItem {
@@ -1094,7 +1094,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
assert_eq!(
session.previous_turn_settings().await,
Some(PreviousTurnSettings {
model: previous_model.to_string(),
model: turn_context.model_info.slug.clone(),
realtime_active: Some(turn_context.realtime_active),
})
);
@@ -3914,7 +3914,7 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
}
#[tokio::test]
async fn record_context_updates_and_set_reference_context_item_persists_baseline_without_emitting_diffs()
async fn record_context_updates_and_set_reference_context_item_emits_model_diff_from_stored_baseline()
{
let (session, previous_context) = make_session_and_context().await;
let next_model = if previous_context.model_info.slug == "gpt-5.1" {
@@ -3955,7 +3955,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
let update_items = session
.build_settings_update_items(Some(&previous_context_item), &turn_context)
.await;
assert_eq!(update_items, Vec::new());
assert!(!update_items.is_empty());
session
.record_context_updates_and_set_reference_context_item(&turn_context)
@@ -3963,7 +3963,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
assert_eq!(
session.clone_history().await.raw_items().to_vec(),
Vec::new()
update_items
);
assert_eq!(
serde_json::to_value(session.reference_context_item().await)
+25 -36
View File
@@ -36,30 +36,24 @@ pub(crate) struct ContextManager {
reference_turn_context_state: ReferenceTurnContextState,
}
/// Session-owned bookkeeping for turn-context state that survives history replay,
/// rollback, and compaction.
/// Session-owned bookkeeping for the last stored turn-context snapshot that survives
/// history replay, rollback, and compaction.
///
/// This intentionally tracks both the latest real turn context we know about and the
/// model-visible reference baseline, because those diverge when compaction hides the
/// baseline without erasing the last real turn's settings.
/// The stored `TurnContextItem` drives future settings diffs and rollback metadata.
/// Compaction may shadow it for reference-baseline purposes without erasing the
/// stored snapshot itself.
#[derive(Debug, Clone, Default)]
pub(crate) struct ReferenceTurnContextState {
/// The most recent real turn context we reconstructed or recorded, even if a later
/// compaction means the model can no longer rely on it as the active baseline.
/// The last stored turn-context snapshot we reconstructed or recorded.
///
/// This drives `previous_turn_settings()` and other rollback bookkeeping, which
/// intentionally survive compaction until a newer real turn replaces them.
latest_turn_context_item: Option<TurnContextItem>,
/// The last turn context item that established the model's reference baseline.
///
/// Unlike `latest_turn_context_item`, this is only model-visible when
/// `compacted_since_model_saw_reference_turn_context` is false.
reference_turn_context_item: Option<TurnContextItem>,
/// This drives `previous_turn_settings()` and rollback bookkeeping even when
/// compaction has shadowed it for reference-baseline purposes.
turn_context_item: Option<TurnContextItem>,
/// Whether compaction has crossed the current reference baseline without a later
/// reinjection or real turn context re-establishing it.
/// reinjection or newer stored turn context re-establishing it.
///
/// When this is true, `reference_context_item()` must return `None` even if
/// `reference_turn_context_item` still retains the last stored baseline for replay or
/// `turn_context_item` still retains the last stored snapshot for replay or
/// rollback bookkeeping.
compacted_since_model_saw_reference_turn_context: bool,
}
@@ -74,7 +68,7 @@ impl ReferenceTurnContextState {
}
pub(crate) fn note_compaction_during_reverse_replay(&mut self) {
if self.reference_turn_context_item.is_none() {
if self.turn_context_item.is_none() {
self.compacted_since_model_saw_reference_turn_context = true;
}
}
@@ -83,39 +77,34 @@ impl ReferenceTurnContextState {
&mut self,
turn_context_item: &TurnContextItem,
) {
if self.latest_turn_context_item.is_none() {
self.latest_turn_context_item = Some(turn_context_item.clone());
}
if self.reference_turn_context_item.is_none() {
self.reference_turn_context_item = Some(turn_context_item.clone());
if self.turn_context_item.is_none() {
self.turn_context_item = Some(turn_context_item.clone());
}
}
pub(crate) fn set_latest_turn_context_item(&mut self, item: Option<TurnContextItem>) {
self.latest_turn_context_item = item;
pub(crate) fn set_turn_context_item_during_reverse_replay(
&mut self,
turn_context_item: TurnContextItem,
) {
self.turn_context_item = Some(turn_context_item);
}
pub(crate) fn record_regular_turn_context(&mut self, turn_context_item: TurnContextItem) {
self.latest_turn_context_item = Some(turn_context_item.clone());
self.reference_turn_context_item = Some(turn_context_item);
self.turn_context_item = Some(turn_context_item);
self.compacted_since_model_saw_reference_turn_context = false;
}
pub(crate) fn set_reference_context_item(&mut self, item: Option<TurnContextItem>) {
if let Some(item) = item {
self.reference_turn_context_item = Some(item);
self.turn_context_item = Some(item);
self.compacted_since_model_saw_reference_turn_context = false;
} else {
self.note_compaction();
}
}
pub(crate) fn latest_turn_context_item(&self) -> Option<TurnContextItem> {
self.latest_turn_context_item.clone()
}
pub(crate) fn stored_reference_turn_context_item(&self) -> Option<TurnContextItem> {
self.reference_turn_context_item.clone()
pub(crate) fn turn_context_item(&self) -> Option<TurnContextItem> {
self.turn_context_item.clone()
}
pub(crate) fn compacted_since_model_saw_reference_turn_context(&self) -> bool {
@@ -123,7 +112,7 @@ impl ReferenceTurnContextState {
}
pub(crate) fn previous_turn_settings(&self) -> Option<PreviousTurnSettings> {
self.latest_turn_context_item
self.turn_context_item
.as_ref()
.map(|turn_context_item| PreviousTurnSettings {
model: turn_context_item.model.clone(),
@@ -135,7 +124,7 @@ impl ReferenceTurnContextState {
if self.compacted_since_model_saw_reference_turn_context {
None
} else {
self.reference_turn_context_item.clone()
self.turn_context_item.clone()
}
}
}