[codex] Omit fork turns from thread started notifications (#19093)

## Why

`thread/fork` responses intentionally include copied history so the
caller can render the fork immediately, but `thread/started` is a
lifecycle notification. The v2 `Thread` contract says notifications
should return `turns: []`, and the fork path was reusing the response
thread directly, causing copied turns to be emitted through
`thread/started` as well.

## What Changed

- Route app-server `thread/started` notification construction through a
helper that clears `thread.turns` before sending.
- Keep `thread/fork` responses unchanged so callers still receive copied
history.
- Add persistent and ephemeral fork coverage that asserts
`thread/started` emits an empty `turns` array while the response retains
fork history.

## Testing

- `just fmt`
- `cargo test -p codex-app-server`
This commit is contained in:
Ruslan Nigmatullin
2026-04-24 12:31:13 -07:00
committed by GitHub
parent 0db6811b7c
commit a3cccbd8ed
2 changed files with 24 additions and 5 deletions

View File

@@ -2859,7 +2859,7 @@ impl CodexMessageProcessor {
))
.await;
let notif = ThreadStartedNotification { thread };
let notif = thread_started_notification(thread);
listener_task_context
.outgoing
.send_server_notification(ServerNotification::ThreadStarted(notif))
@@ -5365,7 +5365,7 @@ impl CodexMessageProcessor {
.await;
}
let notif = ThreadStartedNotification { thread };
let notif = thread_started_notification(thread);
self.outgoing
.send_server_notification(ServerNotification::ThreadStarted(notif))
.await;
@@ -7744,7 +7744,7 @@ impl CodexMessageProcessor {
.await,
/*has_in_progress_turn*/ false,
);
let notif = ThreadStartedNotification { thread };
let notif = thread_started_notification(thread);
self.outgoing
.send_server_notification(ServerNotification::ThreadStarted(notif))
.await;
@@ -10094,6 +10094,11 @@ fn build_thread_from_snapshot(
}
}
fn thread_started_notification(mut thread: Thread) -> ThreadStartedNotification {
thread.turns.clear();
ThreadStartedNotification { thread }
}
pub(crate) fn summary_to_thread(
summary: ConversationSummary,
fallback_cwd: &AbsolutePathBuf,