mirror of
https://github.com/openai/codex.git
synced 2026-11-06 00:30:29 +00:00
Preserve thread settings for goal-first and forked threads (#34469)
## Why Goal-first and forked threads could create rollouts without recording their effective thread settings. Resuming those threads after a restart could then use current configuration instead of the settings selected when the thread was created. ## What changed - Persist a `thread_settings_applied` item when a goal first materializes a rollout, including a follow-up snapshot if settings changed concurrently. - Append each fork's effective settings after its copied history so they take precedence over inherited settings during resume. - Centralize conversion from `ThreadConfigSnapshot` to `ThreadSettingsSnapshot`. ## Testing Added coverage that restarts the app server and verifies goal-first and forked threads retain their respective `approvals_reviewer` settings. GitOrigin-RevId: 6cb9aaae73bbe9ca432831cff7e826f95ffd6d01
This commit is contained in:
1 parent
9970cd706f
commit
1836ae0612
7 files changed
+230
-30
No files matched your search
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use codex_core::ForkSnapshot;
|
||||
use codex_core::NewThread;
|
||||
use codex_core::ThreadConfigSnapshot;
|
||||
use codex_core::parse_turn_item;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
@@ -10,6 +11,7 @@ use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::ResumedHistory;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::RolloutLine;
|
||||
use codex_protocol::protocol::ThreadSettingsAppliedEvent;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
@@ -91,7 +93,7 @@ async fn fork_thread_twice_drops_to_first_message() {
|
||||
|
||||
// After cutting at nth user input (n=1 → second user message), cut strictly before that input.
|
||||
let cut1 = user_inputs.get(1).copied().unwrap_or(0);
|
||||
let expected_after_first: Vec<RolloutItem> = base_items[..cut1].to_vec();
|
||||
let mut expected_after_first: Vec<RolloutItem> = base_items[..cut1].to_vec();
|
||||
|
||||
// After dropping again (n=1 on fork1), compute expected relative to fork1's rollout.
|
||||
|
||||
@@ -111,6 +113,9 @@ async fn fork_thread_twice_drops_to_first_message() {
|
||||
.expect("fork 1");
|
||||
|
||||
let fork1_path = codex_fork1.rollout_path().expect("rollout path");
|
||||
expected_after_first.push(thread_settings_applied_item(
|
||||
codex_fork1.config_snapshot().await,
|
||||
));
|
||||
|
||||
// GetHistory on fork1 flushed; the file is ready.
|
||||
let fork1_items = read_rollout_items(&fork1_path);
|
||||
@@ -142,7 +147,10 @@ async fn fork_thread_twice_drops_to_first_message() {
|
||||
.get(fork1_user_inputs.len().saturating_sub(1))
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
let expected_after_second: Vec<RolloutItem> = fork1_items[..cut_last_on_fork1].to_vec();
|
||||
let mut expected_after_second: Vec<RolloutItem> = fork1_items[..cut_last_on_fork1].to_vec();
|
||||
expected_after_second.push(thread_settings_applied_item(
|
||||
codex_fork2.config_snapshot().await,
|
||||
));
|
||||
let fork2_items = read_rollout_items(&fork2_path);
|
||||
pretty_assertions::assert_eq!(
|
||||
serde_json::to_value(&fork2_items).unwrap(),
|
||||
@@ -150,6 +158,14 @@ async fn fork_thread_twice_drops_to_first_message() {
|
||||
);
|
||||
}
|
||||
|
||||
fn thread_settings_applied_item(snapshot: ThreadConfigSnapshot) -> RolloutItem {
|
||||
RolloutItem::EventMsg(EventMsg::ThreadSettingsApplied(
|
||||
ThreadSettingsAppliedEvent {
|
||||
thread_settings: snapshot.into_thread_settings_snapshot(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn fork_thread_from_history_does_not_require_source_rollout_path() {
|
||||
skip_if_no_network!();
|
||||
|
||||
Reference in new issue
Block a user