fix(codex): replay legacy endpoint-stamped reasoning without a model stamp
Native compaction checkpoints and reasoning items persisted before model stamping existed carry only `_issuer_kind`. Treating a missing `_issuer_model` as foreign dropped every such item once the current model was known, which wiped existing sessions' native-compaction context on upgrade (four consumer tests in test_native_compaction / test_native_preflight_estimate / test_413_compression went red on the stack). Trust the endpoint stamp when no model stamp is present, as main does today. Items minted after this change carry the model stamp and still drop on a same-endpoint model switch; a wrong guess on a legacy item is caught by the invalid_encrypted_content 400 classifier and the replay kill switch.
This commit is contained in:
@@ -350,9 +350,8 @@ def _replay_reasoning_items(
|
||||
"""Replay persisted encrypted reasoning/compaction items for one assistant turn. Skips duplicate
|
||||
ids, ``compaction`` checkpoints unless THIS request carries ``context_management`` (else a persisted
|
||||
checkpoint erases pre-checkpoint history on a model that cannot decrypt it), and items stamped by
|
||||
another issuer or model (HTTP 400). Endpoint-stamped legacy items without model provenance drop
|
||||
(fail closed) when the current model is known; fully unstamped items pass. ``id`` (store=False
|
||||
lookups 404) and the Hermes provenance fields are stripped."""
|
||||
another issuer or model (HTTP 400). Items without a model stamp (legacy or unstamped) replay on a
|
||||
matching issuer. ``id`` (store=False lookups 404) and the Hermes provenance fields are stripped."""
|
||||
global _CROSS_ISSUER_WARN_EMITTED
|
||||
replayed: List[Dict[str, Any]] = []
|
||||
for ri in _as_list(msg.get("codex_reasoning_items")):
|
||||
@@ -364,9 +363,11 @@ def _replay_reasoning_items(
|
||||
item_issuer = ri.get("_issuer_kind")
|
||||
item_model = ri.get("_issuer_model")
|
||||
foreign_issuer = current_issuer_kind is not None and item_issuer is not None and item_issuer != current_issuer_kind
|
||||
foreign_model = current_issuer_model is not None and (
|
||||
(item_model is not None and item_model != current_issuer_model)
|
||||
or (item_model is None and item_issuer is not None)
|
||||
# No model stamp → trust the endpoint stamp. Native compaction checkpoints and reasoning persisted
|
||||
# before model stamping exist carry none; dropping them would erase every existing session's
|
||||
# context once. A wrong guess is caught by the invalid_encrypted_content 400 classifier.
|
||||
foreign_model = (
|
||||
current_issuer_model is not None and item_model is not None and item_model != current_issuer_model
|
||||
)
|
||||
if foreign_issuer or foreign_model:
|
||||
if not _CROSS_ISSUER_WARN_EMITTED:
|
||||
|
||||
@@ -593,16 +593,21 @@ def test_reasoning_replay_requires_matching_issuer_model_on_same_endpoint():
|
||||
assert not any(i.get("type") == "reasoning" for i in other)
|
||||
|
||||
|
||||
def test_reasoning_replay_drops_endpoint_stamped_legacy_item_without_model():
|
||||
# Fail closed: an endpoint-only stamp cannot prove the minting model once the request model is known.
|
||||
def test_legacy_endpoint_stamped_item_without_model_replays_on_same_issuer():
|
||||
# WHY: native compaction checkpoints and reasoning persisted before model stamping carry only the
|
||||
# endpoint stamp; dropping them would erase every existing session's context once after upgrade.
|
||||
issuer = "other:https://responses.example.com/v1"
|
||||
legacy = {"type": "reasoning", "encrypted_content": "legacy-blob", "_issuer_kind": issuer}
|
||||
items = _chat_messages_to_responses_input(
|
||||
_reasoning_history(legacy), current_issuer_kind=issuer, current_issuer_model="gpt-5.6-sol"
|
||||
)
|
||||
assert not any(i.get("type") == "reasoning" for i in items)
|
||||
# Ordinary assistant text stays replayable; only the sealed blob is withheld.
|
||||
assert any(i.get("role") == "assistant" for i in items)
|
||||
replayed = [i for i in items if i.get("type") == "reasoning"]
|
||||
assert [i["encrypted_content"] for i in replayed] == ["legacy-blob"]
|
||||
# A different endpoint stamp still drops.
|
||||
foreign = _chat_messages_to_responses_input(
|
||||
_reasoning_history(legacy), current_issuer_kind="codex_backend", current_issuer_model="gpt-5.6-sol"
|
||||
)
|
||||
assert not any(i.get("type") == "reasoning" for i in foreign)
|
||||
|
||||
|
||||
def test_preflight_codex_api_kwargs_drops_oversized_message_id_end_to_end():
|
||||
|
||||
Reference in New Issue
Block a user