[codex] assign IDs to normalized prompt outputs#30311
Conversation
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4f3fd29028
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| items: Vec<ResponseItem>, | ||
| ) -> Vec<ResponseItem> { | ||
| if turn_context.config.features.enabled(Feature::ItemIds) { | ||
| Self::assign_missing_response_item_ids(Cow::Owned(items)).into_owned() |
There was a problem hiding this comment.
Keep prompt-only item IDs stable across retries
When item_ids is enabled and normalization synthesizes a missing call output, this assigns a fresh UUID only in the prompt clone without recording it back to history. Any later prompt rebuild from the same history (for example a sampling retry, another turn after resuming the rollout, or a compaction retry) renders the same synthetic output with a different id, changing model-visible context and defeating prompt-cache reuse; it also means consumers still cannot rely on persistent IDs for normalized outputs. Consider deriving the synthetic ID from stable fields such as call_id, or installing the synthesized output/ID into history before prompt serialization.
AGENTS.md reference: AGENTS.md:L91-L97
Useful? React with 👍 / 👎.
## Why Response item IDs represent stable conversation identity. `ContextManager::for_prompt` repairs an unmatched call by synthesizing an `"aborted"` output in the disposable prompt projection, but that output previously had no ID. Assigning a fresh ID on every prompt build would make retries and resumes change otherwise identical model context and reduce prompt-cache reuse. The concrete bug is that these normalization-created outputs bypass the regular item-ID allocation path. Even with item IDs enabled, a prompt could therefore contain an identified call paired with a synthetic output whose `id` was missing. This change closes that gap by deriving the output ID from the source call's item ID. For legacy calls that have no item ID, the output remains ID-less because there is no stable source identity to derive from. The originating call already has a stable item ID under the item-ID model introduced in #28814. A prompt-only output can therefore derive stable identity from that call without mutating canonical history or persisted rollouts. This addresses the failure exposed by #30311 while keeping normalization read-only outside its detached prompt snapshot. UUIDv5 is intentional here because it is the standard namespaced, deterministic UUID construction. Using the output kind and source call ID as the name produces the same UUID on every projection while keeping output kinds in separate name domains. UUIDv7 would introduce randomness and time, so keeping it stable would require persisting the synthetic repair. UUIDv5 uses SHA-1 internally, but this is only an identity mapping—not an authenticity or security boundary. ## What changed - Derive a deterministic UUIDv5 ID for each synthesized call output from the source call item ID. - Use the Responses API prefix appropriate for function, custom-tool, tool-search, and local-shell outputs. - Preserve the existing insertion position immediately after the unmatched call. - Keep synthesized outputs prompt-only; no rollout, task-lifecycle, compaction, or raw-response behavior changes. ## Testing - `just test -p codex-core for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history` - `just test -p codex-core synthetic_call_output_id_is_stable_across_resumes` - `just test -p codex-core normalize_adds_missing_output` - `just test -p codex-core response_item_ids`
Summary
ContextManager::for_promptnormalization.Root cause
item_idsassigns IDs at the durable history-recording boundary. Later,for_promptcan synthesize an"aborted"call output for an unmatched call. That prompt-only output bypassed the allocation boundary and serialized without an ID, which breaks consumers that require IDs for every rendered response item.This keeps the existing feature gate and type-specific UUIDv7 allocator instead of weakening the downstream invariant. Related: #28814.
Validation
just test -p codex-core --test all normalized_call_outputs_receive_item_idscargo fmt --checkjust test -p codex-core; currently blocked onmainby the unrelatedCreateThreadParamsinitializer missinghistory_modeincore/src/session/tests.rs:7006.