Include shared histories in rollout compression (#42039)

## What changed

- Make `local_thread_store_compression` compress cold rollout files across shared and forked histories without a separate compression mode.
- Retire `local_thread_store_shared_compression` while continuing to accept it in strict configuration without changing compression behavior.
- Read rollout files through the compressed-rollout reader when `codex exec resume` determines the latest turn's working directory.

## Testing

- Cover compression and restoration of an archived fork chain.
- Cover cwd-based resume selection when only the compressed rollout remains.
- Cover the removed compatibility key in strict configuration.

GitOrigin-RevId: 8edebf61eccc1f79cabb8e641f8982c76f337bf1
This commit is contained in:
jif
2026-09-01 12:23:20 +00:00
committed by copyberry
parent 2e5ee418ad
commit 9d0eae74cd
13 changed files with 137 additions and 305 deletions
+2
View File
@@ -37,6 +37,7 @@ codex-login = { workspace = true }
codex-model-provider-info = { workspace = true }
codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-rollout = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-cli = { workspace = true }
codex-utils-oss = { workspace = true }
@@ -77,3 +78,4 @@ tracing-opentelemetry = { workspace = true }
uuid = { workspace = true }
walkdir = { workspace = true }
wiremock = { workspace = true }
zstd = { workspace = true }
+17 -13
View File
@@ -1563,20 +1563,24 @@ async fn latest_thread_cwd(thread: &AppServerThread) -> PathBuf {
}
async fn parse_latest_turn_context_cwd(path: &Path) -> Option<PathBuf> {
let text = tokio::fs::read_to_string(path).await.ok()?;
for line in text.lines().rev() {
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
let path = path.to_path_buf();
tokio::task::spawn_blocking(move || {
let reader = codex_rollout::open_rollout_seekable_reader(&path).ok()?;
let mut scanner = codex_rollout::ReverseJsonlScanner::new(reader).ok()?;
while let Some(outcome) = scanner.scan_next::<RolloutLine>().ok()? {
if let codex_rollout::ScanOutcome::Parsed(RolloutLine {
item: RolloutItem::TurnContext(item),
..
}) = outcome
{
return Some(item.cwd.into_path_buf());
}
}
let Ok(rollout_line) = serde_json::from_str::<RolloutLine>(trimmed) else {
continue;
};
if let RolloutItem::TurnContext(item) = rollout_line.item {
return Some(item.cwd.into_path_buf());
}
}
None
None
})
.await
.ok()
.flatten()
}
fn cwds_match(current_cwd: &Path, session_cwd: &Path) -> bool {
+8
View File
@@ -564,6 +564,14 @@ async fn exec_resume_last_respects_cwd_filter_and_all_flag() -> anyhow::Result<(
"resume --last --all should pick newest session"
);
// Selection must still use the latest turn's cwd when only the compressed rollout exists.
zstd::stream::copy_encode(
std::fs::File::open(&path_b)?,
std::fs::File::create(path_b.with_extension("jsonl.zst"))?,
/*level*/ 3,
)?;
std::fs::remove_file(&path_b)?;
let marker_a2 = format!("resume-cwd-a-2-{}", Uuid::new_v4());
let prompt_a2 = format!("echo {marker_a2}");
test.cmd_with_server(&server)