Fix forked thread name inheritance (#26075)

Fixes #25950.

## Why
Forking a renamed thread could fall back to the source thread's
first-prompt title because the fork path did not preserve the source's
explicit name. That meant fork-of-renamed-fork flows could show stale
sidebar labels even though the user had renamed the parent.

## What changed
`thread/fork` now reads the source thread's distinct `name`, normalizes
it, persists it onto materialized forks, and applies it to the returned
API thread. Because the source `name` already excludes first-prompt
pseudo-titles, forks inherit only an explicit user rename instead of
stale generated metadata.
This commit is contained in:
Eric Traut
2026-06-03 12:56:54 -07:00
committed by GitHub
Unverified
parent a2ebe07b39
commit d8121f93c8
2 changed files with 92 additions and 21 deletions
@@ -3173,6 +3173,10 @@ impl ThreadRequestProcessor {
.read_stored_thread_for_resume(&thread_id, path.as_ref(), /*include_history*/ true)
.await?;
let source_thread_id = source_thread.thread_id;
let source_thread_name = source_thread
.name
.as_deref()
.and_then(codex_core::util::normalize_thread_name);
let history_items = source_thread
.history
.as_ref()
@@ -3264,6 +3268,21 @@ impl ThreadRequestProcessor {
app_server_client_version,
)
.await?;
if session_configured.rollout_path.is_some()
&& let Some(name) = source_thread_name.clone()
{
self.thread_manager
.update_thread_metadata(
thread_id,
StoreThreadMetadataPatch {
name: Some(Some(name)),
..Default::default()
},
/*include_archived*/ true,
)
.await
.map_err(|err| core_thread_write_error("inherit source thread name", err))?;
}
// Auto-attach a conversation listener when forking a thread.
log_listener_attach_result(
@@ -3291,7 +3310,6 @@ impl ThreadRequestProcessor {
)
} else {
let config_snapshot = forked_thread.config_snapshot().await;
// forked thread names do not inherit the source thread name
let mut thread = build_thread_from_snapshot(
thread_id,
session_configured.session_id.to_string(),
@@ -3309,6 +3327,9 @@ impl ThreadRequestProcessor {
}
thread
};
if let Some(name) = source_thread_name {
set_thread_name_from_title(&mut thread, name);
}
thread.session_id = session_configured.session_id.to_string();
thread.thread_source = forked_thread
.config_snapshot()