fix(gateway): preserve abort after dispatch rejection#91000
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 15, 2026, 10:05 PM ET / July 16, 2026, 02:05 UTC. Summary PR surface: Source +92, Tests +1402. Total +1494 across 5 files. Reproducibility: yes. —the source-level reproduction deterministically starts dispatch, aborts the same run, and then rejects dispatch; the supplied after-fix real Gateway/WebSocket proof exercises the corresponding observable terminal results. The current exact merge result still needs rerunning because the branch is behind main. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Use one explicitly approved terminal-owner invariant—preferably shared with the canonical agent terminal-outcome normalization where practical—then rebase and prove abort-first, error-first, lifecycle-terminal, queued-followup, and restart-safe paths on the exact merge result. Do we have a high-confidence way to reproduce the issue? Yes—the source-level reproduction deterministically starts dispatch, aborts the same run, and then rejects dispatch; the supplied after-fix real Gateway/WebSocket proof exercises the corresponding observable terminal results. The current exact merge result still needs rerunning because the branch is behind main. Is this the best way to solve the issue? Unclear—the bounded patch and proof are convincing, but repository policy favors canonical terminal-outcome normalization, so the Gateway owner should confirm that this local ownership layer is the narrowest maintainable boundary. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 154d53c4f6fc. Label changesLabel justifications:
Evidence reviewedPR surface: Source +92, Tests +1402. Total +1494 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (9 earlier review cycles; latest 8 shown)
|
What Problem This Solves
Fixes #90999.
chat.sendcan receive a dispatch rejection after an explicitchat.aborthas already made the run terminal. In that ordering, the later rejection can replace the cached aborted result withstatus: "error"and send a contradictory error terminal to clients.Why This Change Was Made
The dispatch-error path now chooses terminal ownership before asynchronous fallback work. An explicit abort marker retains its aborted result; a dispatch error retains its error result when no abort marker owns the run; and a real agent lifecycle terminal remains the sole live terminal projection. The change also keeps queued-followup and restart-safe session-admission cleanup on their existing ownership paths.
The diff is limited to five Gateway dispatch/admission files and their regression coverage. It does not change provider routing, authentication, protocol schemas, or client APIs.
User Impact
Clients observing an explicitly aborted
chat.sendno longer receive a later contradictory error terminal when the original dispatch rejects. The error-first path remains an error, and a signal-only lifecycle interruption still exposes its lifecycle terminal without a duplicate client-visible error.Related: #84176 and #84306.
Evidence
Full E2E: partial. The proof starts a real local Gateway server and connects a real WebSocket RPC client. It exercises production
chat.send/chat.aborthandling, runtime lifecycle event subscription, WebSocket transport, and client-visiblechatbroadcasts. Dispatch rejection is the controlled boundary; no live provider/model, browser UI, or native client was used.Refresh head:
adc576fd19e1c40f08cbb2549a906209e63c0238.Current
mainat preparation:f6653d1127820c202a8f9cb0b0b8685c20fe84b2. The merge tree for current main plus this head is clean (f9af0f067d6b70ef71511183d4ca73cd45c20c60), and the PR-style diff contains only:Commands run against the refresh head:
Observed results:
The exact-head WebSocket proof uses a real Gateway server and WebSocket RPC client. It verifies that an abort-first rejection has no error terminal, an error-first late abort remains error, and a signal-only lifecycle terminal remains the only client-visible terminal after the controlled dispatch rejection.
What was not tested: live provider/model execution, browser UI, and native clients.