diff --git a/codex-rs/core/src/rollout/metadata.rs b/codex-rs/core/src/rollout/metadata.rs index 3a603f0da7..4e2658c01f 100644 --- a/codex-rs/core/src/rollout/metadata.rs +++ b/codex-rs/core/src/rollout/metadata.rs @@ -65,31 +65,63 @@ pub(crate) fn builder_from_items<'a>( items: impl IntoIterator, rollout_path: &Path, ) -> Option { - if let Some(session_meta) = items.into_iter().find_map(|item| match item { - RolloutItem::SessionMeta(meta_line) => Some(meta_line), - RolloutItem::ResponseItem(_) - | RolloutItem::Compacted(_) - | RolloutItem::TurnContext(_) - | RolloutItem::EventMsg(_) => None, - }) && let Some(builder) = builder_from_session_meta(session_meta, rollout_path) - { - return Some(builder); + scan_builder_from_items(items, rollout_path).builder +} + +struct BuilderScan { + saw_any_items: bool, + builder: Option, +} + +fn scan_builder_from_items<'a>( + items: impl IntoIterator, + rollout_path: &Path, +) -> BuilderScan { + let mut saw_any_items = false; + let mut session_meta = None; + for item in items { + saw_any_items = true; + if let RolloutItem::SessionMeta(meta_line) = item { + session_meta = Some(meta_line); + break; + } } - let file_name = rollout_path.file_name()?.to_str()?; - if !file_name.starts_with(ROLLOUT_PREFIX) || !file_name.ends_with(ROLLOUT_SUFFIX) { - return None; + if let Some(session_meta) = session_meta + && let Some(builder) = builder_from_session_meta(session_meta, rollout_path) + { + return BuilderScan { + saw_any_items, + builder: Some(builder), + }; + } + + let builder = { + let file_name = rollout_path.file_name().and_then(|name| name.to_str()); + if let Some(file_name) = file_name + && file_name.starts_with(ROLLOUT_PREFIX) + && file_name.ends_with(ROLLOUT_SUFFIX) + && let Some((created_ts, uuid)) = parse_timestamp_uuid_from_filename(file_name) + && let Some(created_at) = + DateTime::::from_timestamp(created_ts.unix_timestamp(), 0) + .and_then(|timestamp| timestamp.with_nanosecond(0)) + && let Ok(id) = ThreadId::from_string(&uuid.to_string()) + { + Some(ThreadMetadataBuilder::new( + id, + rollout_path.to_path_buf(), + created_at, + SessionSource::default(), + )) + } else { + None + } + }; + + BuilderScan { + saw_any_items, + builder, } - let (created_ts, uuid) = parse_timestamp_uuid_from_filename(file_name)?; - let created_at = - DateTime::::from_timestamp(created_ts.unix_timestamp(), 0)?.with_nanosecond(0)?; - let id = ThreadId::from_string(&uuid.to_string()).ok()?; - Some(ThreadMetadataBuilder::new( - id, - rollout_path.to_path_buf(), - created_at, - SessionSource::default(), - )) } pub(crate) async fn extract_metadata_from_rollout( @@ -100,18 +132,19 @@ pub(crate) async fn extract_metadata_from_rollout( let parse_errors = loaded_rollout.parse_errors; let source = loaded_rollout.source; let rollout_start = source.inclusive_start_of_rollout_index(); - let rollout_items = source - .iter_forward_from(rollout_start) - .map(|(_, item)| item) - .collect::>(); - let has_any_parsed_items = !rollout_items.is_empty(); - if parse_errors > 0 && !has_any_parsed_items { + let builder_scan = scan_builder_from_items( + source + .iter_forward_from(rollout_start) + .map(|(_, item)| item), + rollout_path, + ); + if parse_errors > 0 && !builder_scan.saw_any_items { anyhow::bail!( "rollout contains parse errors and no readable items: {}", rollout_path.display() ); } - let builder = builder_from_items(rollout_items, rollout_path).ok_or_else(|| { + let builder = builder_scan.builder.ok_or_else(|| { anyhow::anyhow!( "rollout missing metadata builder: {}", rollout_path.display()