From 2b22690fa453e6425b1655c12d32d30dc74ca3aa Mon Sep 17 00:00:00 2001 From: t Date: Fri, 10 Jul 2026 20:44:10 +0000 Subject: [PATCH] fix(#4812): capture resume cursor + journal baseline before SSE headers commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex round-3 finding #1 [SILENT]: baselining after end_sse_headers() left a window where a run completing between header commit and baseline was absorbed into the baseline + silently lost. Move resume-id parse + fingerprint baseline before send_response/headers (both side-effect-free: header read + stat-only fingerprint). Codex round-3 also flagged finding #2 [CORE] (reconciliation terminal-event-at-cutoff dedup keeps the handler attached to a dead run queue) — NOT addressed here; escalating to Nathan per Codex-depth diminishing-returns (3rd round into the same SSE seam class). Co-authored-by: rodboev --- api/routes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/api/routes.py b/api/routes.py index ed63fd5b..770cfd82 100644 --- a/api/routes.py +++ b/api/routes.py @@ -16868,6 +16868,14 @@ def _handle_session_run_journal_stream_for_session(handler, parsed, session_id): except KeyError: return j(handler, {"error": "Session not found"}, status=404) + # Parse the resume cursor and baseline the journal BEFORE committing SSE headers + # (and thus before any run could complete mid-handler). Capturing after + # end_sse_headers() leaves a window where a run finishing between header commit + # and baseline is absorbed into the baseline and silently lost. Both operations + # are side-effect-free (header read + stat-only fingerprint), safe pre-response. + resume_event_id = _session_events_resume_event_id(handler, parsed) + _idle_journal_fp = session_journal_fingerprint(session_id) + handler.send_response(200) handler.send_header("Content-Type", "text/event-stream; charset=utf-8") handler.send_header("Cache-Control", "no-cache") @@ -16877,12 +16885,6 @@ def _handle_session_run_journal_stream_for_session(handler, parsed, session_id): end_sse_headers(handler) _sse_set_write_deadline(handler) - resume_event_id = _session_events_resume_event_id(handler, parsed) - # Baseline the journal at the EARLIEST point — before the preliminary active-run - # lookup AND the initial cursor replay below — so a run completing during either - # operation is detected by the idle wait loop rather than absorbed into a - # too-late baseline and silently lost (no stream left to attach afterward). - _idle_journal_fp = session_journal_fingerprint(session_id) active_stream_id = _active_run_stream_for_session(session_id) subscriber = None subscriber_stream = None