fix: follow-ups for salvaged PR #72425

- codex app-server sibling path: surface a WARNING (was silent debug) when
  the projected-message flush fails — same bug class as the main fix, but
  codex output has already streamed so fail-closed and agent_persisted=False
  are both wrong here (#860/#42039 duplicate-write hazard); loud durability
  gap logging instead.
- map session_persistence_failed in _format_turn_completion_explanation so
  the user sees an actionable reason instead of 'The request failed:
  unknown error' + explainer test.
- contributors/emails mapping for elco@thedaoist.gg (attribution CI).
This commit is contained in:
kshitijk4poor 2026-07-27 20:50:26 +05:00 committed by kshitij
parent 858bedea02
commit 8e934e84ac
4 changed files with 36 additions and 2 deletions

View file

@ -778,12 +778,27 @@ def run_codex_app_server_turn(
# the already-flushed user turn). See gateway/run.py agent_persisted.
if getattr(agent, "_session_db", None) is not None:
try:
agent._flush_messages_to_session_db(messages)
_codex_flush_ok = agent._flush_messages_to_session_db(messages)
except Exception:
logger.debug(
_codex_flush_ok = False
logger.warning(
"codex app-server projected-message flush failed",
exc_info=True,
)
if _codex_flush_ok is False:
# Unlike the chat-completions loop (which fails closed BEFORE
# projection — see conversation_loop session_persistence_failed),
# codex output has already streamed to the user by the time this
# flush runs, so there is nothing left to withhold. We cannot
# flip agent_persisted=False either: the gateway fallback write
# would re-INSERT the already-flushed user turn (#860/#42039).
# Surface the durability gap loudly instead of a silent debug.
logger.warning(
"codex app-server turn was delivered but could NOT be "
"persisted to the session DB (session=%s) — this turn "
"will be missing after restart/resume",
getattr(agent, "session_id", None),
)
# Counter ticks for the agent-improvement loop.

View file

@ -0,0 +1 @@
elcocoel

View file

@ -3445,6 +3445,14 @@ class AIAgent:
"the model produced no follow-up text. Send `continue` to "
"let it summarize."
)
if reason == "session_persistence_failed":
return (
prefix
+ "the turn was stopped because session storage could not be "
"written (the transcript would have been lost on restart). "
"Check disk space / permissions for the state DB, then send "
"your message again."
)
# Unknown/diagnostic-only reasons (e.g. "unknown", guardrail_halt
# which already surfaces its own message) — don't second-guess.
return ""

View file

@ -106,6 +106,16 @@ def test_explanation_for_all_retries_exhausted():
assert "retries" in out.lower()
def test_explanation_for_session_persistence_failed():
"""Fail-closed persistence exits (#72425) must explain themselves."""
out = AIAgent._format_turn_completion_explanation(
"session_persistence_failed"
)
assert out # non-empty
assert "session storage" in out.lower()
assert "disk space" in out.lower()
# --------------------------------------------------------------------------
# 2. Enable/disable seam
# --------------------------------------------------------------------------