Skip to content

[671a76a2] Commit-under-lock for release reject() plus structured refusals - #1125

Open
roboco-app[bot] wants to merge 2 commits into
feature/backend/b738f2fc--192bc020from
feature/backend/b738f2fc--192bc020--671a76a2
Open

roboco-app[bot] wants to merge 2 commits into
feature/backend/b738f2fc--192bc020from
feature/backend/b738f2fc--192bc020--671a76a2

Conversation

@roboco-app

@roboco-app roboco-app Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

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).

Backend Developer 2 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.
@github-actions github-actions Bot added documentation Docs, README, CHANGELOG, governance files area: panel Touches panel/ (Next.js control panel) area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: services Touches roboco/services/ (business logic, side effects) tests Test suite changes area: agents Touches agents/ (prompts, role config) labels Sep 28, 2026
@github-actions

Copy link
Copy Markdown

Thanks for opening your first pull request on RoboCo!

Quick checklist before review (most of these are enforced by CI, but worth a glance):

  • make quality — ruff format check, ruff check, mypy, pytest (≥80% coverage), and the rest of the gate
  • Panel changes pass pnpm lint and pnpm exec tsc --noEmit (run from panel/)
  • No # noqa / # type: ignore shortcuts; pre-existing violations in touched files are fixed
  • Added an entry under ## [Unreleased] in CHANGELOG.md
  • Signed the CLA (the bot will prompt you on this PR)
  • Signed your commits — master requires verified signatures (SSH signing setup)
  • Updated any affected docs under docs/

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

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: agents Touches agents/ (prompts, role config) area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: panel Touches panel/ (Next.js control panel) area: services Touches roboco/services/ (business logic, side effects) cell/backend Task owned by Backend Team documentation Docs, README, CHANGELOG, governance files tests Test suite changes to feature/backend/b738f2fc--192bc020

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

0 participants