mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[1/3] core: make world state snapshots serializable (#29833)
## Why `WorldState` currently keeps its diff baseline as live Rust objects keyed by process-local `TypeId`. That baseline cannot be written to a rollout or restored after resume, so Codex reconstructs an approximation from `TurnContextItem`. This is the first change in the WorldState persistence stack. It gives every section a stable persisted identity and a compact serializable comparison snapshot without changing rollout behavior yet. ## What changed - Require each `WorldStateSection` to define a stable ID and serializable snapshot type. - Reject duplicate section IDs when constructing `WorldState`. - Persist a dedicated environment comparison snapshot using model-visible strings instead of runtime path types. - Store only `WorldStateSnapshot` in `ContextManager`, removing the parallel live-object baseline. - Render diffs by restoring each section's typed snapshot; invalid snapshots fall back to a full section render. - Omit null object fields for future RFC 7386 patches while preserving null values inside arrays. Follow-up PRs will record full snapshots and merge patches, then restore the baseline during resume, fork, and rollback. ## Test plan - WorldState snapshot tests cover stable IDs, duplicate rejection, null omission, and array preservation. - Environment tests cover persistence-safe snapshot values and existing diff rendering. - ContextManager baseline deduplication and session context-update persistence tests. Related: #29249
This commit is contained in:
@@ -1402,14 +1402,13 @@ impl Session {
|
||||
prepare_response_items(&mut history);
|
||||
let world_state_baseline = reference_context_item
|
||||
.as_ref()
|
||||
.map(build_world_state_from_turn_context_item);
|
||||
.map(build_world_state_from_turn_context_item)
|
||||
.map(|world_state| world_state.snapshot());
|
||||
{
|
||||
let mut state = self.state.lock().await;
|
||||
state.replace_history(history, reference_context_item);
|
||||
if let Some(world_state) = world_state_baseline {
|
||||
state
|
||||
.history
|
||||
.set_world_state_baseline(Arc::new(world_state));
|
||||
state.history.set_world_state_baseline(world_state);
|
||||
}
|
||||
let fallback_ids = state.auto_compact_window_ids();
|
||||
let window_id = window_id.unwrap_or(fallback_ids.window_id);
|
||||
@@ -2796,7 +2795,7 @@ impl Session {
|
||||
.await,
|
||||
);
|
||||
let items = crate::context_manager::updates::merge_contextual_fragments(
|
||||
world_state.render_diff(previous_world_state.as_ref()),
|
||||
world_state.render_diff(&previous_world_state.snapshot()),
|
||||
);
|
||||
if !items.is_empty() {
|
||||
self.record_conversation_items(turn_context, &items).await;
|
||||
@@ -2807,7 +2806,7 @@ impl Session {
|
||||
.lock()
|
||||
.await
|
||||
.history
|
||||
.set_world_state_baseline(Arc::clone(&world_state));
|
||||
.set_world_state_baseline(world_state.snapshot());
|
||||
world_state
|
||||
}
|
||||
|
||||
@@ -2949,7 +2948,9 @@ impl Session {
|
||||
let mut state = self.state.lock().await;
|
||||
state.replace_history(items, reference_context_item.clone());
|
||||
if let Some(world_state) = world_state_baseline {
|
||||
state.history.set_world_state_baseline(world_state);
|
||||
state
|
||||
.history
|
||||
.set_world_state_baseline(world_state.snapshot());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3532,7 +3533,7 @@ impl Session {
|
||||
.lock()
|
||||
.await
|
||||
.history
|
||||
.set_world_state_baseline(Arc::clone(&world_state));
|
||||
.set_world_state_baseline(world_state.snapshot());
|
||||
context_items
|
||||
} else {
|
||||
// Steady-state path: append only built-in context diffs here; turn-scoped extension
|
||||
@@ -3543,7 +3544,7 @@ impl Session {
|
||||
let world_state_items = {
|
||||
let mut state = self.state.lock().await;
|
||||
crate::context_manager::updates::merge_contextual_fragments(
|
||||
state.history.update_world_state(Arc::clone(&world_state)),
|
||||
state.history.update_world_state(world_state.as_ref()),
|
||||
)
|
||||
};
|
||||
context_items.extend(world_state_items);
|
||||
|
||||
@@ -8099,11 +8099,13 @@ async fn record_context_updates_includes_turn_context_fragments_on_steady_state_
|
||||
});
|
||||
let mut previous_context_item = turn_context.to_turn_context_item();
|
||||
previous_context_item.turn_id = Some("previous-turn-id".to_string());
|
||||
let world_state = Arc::new(build_world_state_from_turn_context(&session, &turn_context).await);
|
||||
let world_state = build_world_state_from_turn_context(&session, &turn_context).await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(Some(previous_context_item));
|
||||
state.history.set_world_state_baseline(world_state);
|
||||
state
|
||||
.history
|
||||
.set_world_state_baseline(world_state.snapshot());
|
||||
}
|
||||
|
||||
session
|
||||
@@ -8773,12 +8775,13 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
.with_model(next_model.to_string(), &session.services.models_manager)
|
||||
.await;
|
||||
let previous_context_item = previous_context.to_turn_context_item();
|
||||
let world_state =
|
||||
Arc::new(build_world_state_from_turn_context(&session, &previous_context).await);
|
||||
let world_state = build_world_state_from_turn_context(&session, &previous_context).await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(Some(previous_context_item.clone()));
|
||||
state.history.set_world_state_baseline(world_state);
|
||||
state
|
||||
.history
|
||||
.set_world_state_baseline(world_state.snapshot());
|
||||
}
|
||||
let rollout_path = attach_thread_persistence(&mut session).await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user