mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
ensure thread.history_mode is immutable (#30261)
## Description This PR makes `thread.history_mode` immutable after the thread's canonical first `SessionMeta` has been written. Later same-thread `SessionMeta` lines are compatibility metadata writes, not a new thread definition. Without this, an older binary could append a `SessionMeta` that omits `history_mode`; when a newer binary replays it, serde defaults that missing field to `legacy` and SQLite could downgrade a paginated thread. ## Why `history_mode` is the persisted thread storage contract. Paginated-thread fail-closed behavior and SQLite memory filtering depend on it staying aligned with canonical rollout metadata, especially when multiple Codex binary versions can touch the same local rollout. ## What changed - Stop generic rollout metadata replay from overwriting `history_mode` from later `SessionMeta` items. - Remove `history_mode` from `ThreadMetadataPatch`, so mutable metadata sync and app-server metadata updates cannot rewrite it. - When local metadata sync has to recreate a missing SQLite row, recover `history_mode` from the rollout's canonical first `SessionMeta` instead of from a mutable patch. - Keep the in-memory thread store using the created thread's canonical `history_mode` instead of metadata patches. - Fill the one remaining core test `CreateThreadParams` initializer with the new `history_mode` field; Bazel CI caught this after the parent history-mode PR landed. ## Validation - `just fmt` - `just test -p codex-thread-store` - `just test -p codex-state session_meta_does_not_set_model_or_reasoning_effort`
This commit is contained in:
1 parent
cf36c688b3
commit
812cd2bb57
7 files changed
+226
-66
No files matched your search
@@ -26,9 +26,12 @@ pub(super) async fn create_thread(
|
||||
params: CreateThreadParams,
|
||||
) -> ThreadStoreResult<()> {
|
||||
let thread_id = params.thread_id;
|
||||
let history_mode = params.history_mode;
|
||||
store.ensure_live_recorder_absent(thread_id).await?;
|
||||
let recorder = create_thread::create_thread(store, params).await?;
|
||||
store.insert_live_recorder(thread_id, recorder).await
|
||||
store
|
||||
.insert_live_recorder(thread_id, recorder, history_mode)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn resume_thread(
|
||||
@@ -98,7 +101,9 @@ pub(super) async fn resume_thread(
|
||||
.map_err(|err| ThreadStoreError::Internal {
|
||||
message: format!("failed to resume local thread recorder: {err}"),
|
||||
})?;
|
||||
store.insert_live_recorder(params.thread_id, recorder).await
|
||||
store
|
||||
.insert_live_recorder(params.thread_id, recorder, history_mode)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
@@ -191,6 +196,7 @@ pub(super) async fn rollout_path(
|
||||
.await
|
||||
.get(&thread_id)
|
||||
.ok_or(ThreadStoreError::ThreadNotFound { thread_id })?
|
||||
.recorder
|
||||
.rollout_path()
|
||||
.to_path_buf())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user