[671a76a2] Commit-under-lock for release reject() plus structured refusals - #1125
Open
roboco-app[bot] wants to merge 2 commits into
Open
roboco-app[bot] wants to merge 2 commits into
roboco-app[bot] wants to merge 2 commits into
Conversation
added 2 commits
September 28, 2026 22:04
…mutex; structured refusal reasons Mirror approve()'s documented commit-under-lock in reject() so the CANCELLED write is durable before the mutex drops — a racing approve could otherwise re-read a non-cancelled row in _approve_precheck and launch the fail-closed executor on a CEO-rejected proposal. Redis-down and lock-held refusals now raise ReleaseRejectRefused (redis_unavailable / already_in_progress, approve()'s vocabulary) and the reject route surfaces the actual reason instead of one ambiguous 409; the telegram reject caller maps the refusal to a readable message. Two-session interleaving regression test fails pre-fix (verified) and passes post-fix.
|
Thanks for opening your first pull request on RoboCo! Quick checklist before review (most of these are enforced by CI, but worth a glance):
See CONTRIBUTING.md for the full workflow and the Code of Conduct for the community standards we follow. Welcome aboard — a maintainer will review shortly. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Close the reject()/approve() race in roboco/services/release_proposal.py so a CEO-rejected proposal can never be executed. The hole: approve() commits TaskStatus.COMPLETED while holding the release Redis mutex (its comment cites the READ-COMMITTED invisibility of flushed-but-uncommitted writes — that pattern exists precisely for this reason); reject() has the mirror-image bug — it sets CANCELLED and only flush()es (
:528-530 at intake time), then releases the mutex in its finally block (:533-534) before the request-level commit lands. A racing approve (e.g. the background executor task) then acquires the mutex, re-reads the row in _approve_precheck before reject's commit, passes the CANCELLED guard, and launches the ~40-minute fail-closed executor on a proposal the CEO explicitly rejected. Build, surgically: (1) commit-under-lock in reject() — exact mirror of approve()'s documented pattern; (2) structured refusals: Redis-unreachable, lock-held, and already-published each return a structured result or typed exception matching approve()'s structured redis_unavailable / already_in_progress style, with the reject route surfacing the actual reason (approve() currently returns bare-None equivalents here — fix that asymmetry); (3) a two-session interleaving regression test — reject flushes and releases the mutex, the second session's approve() re-reads before reject's session commits — that FAILS on current code and passes only with the fix. Constraints: intake line numbers are approximate (verified at slave@18d2d5e0, 197 commits behind origin) — re-anchor on the symbols approve, reject, _approve_precheck at branch HEAD before fixing. This module is a ledger-flagged rework hotspot (bounced 8x): surgical only, no drive-by refactors. Keep DB writes under the lock minimal (status + required_changes marker) to keep the lock-hold window short. Do NOT edit panel/ — if the panel's existing structured-result rendering does not pick up the new refusal reasons, flag back to me and I escalate to main-pm. This is the third item in your queue, after the stuck-payload fields (11a83407) and the Telegram brief count (55fdd4fc).