mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
approve() closed the proposal only on status=='published'. A retry that finds the tag already shipped returns 'already_published' (is_already_published), so if a prior publish's route commit failed / HTTP 504'd, the proposal stayed non-terminal forever — every retry returned already_published and never closed it; only a manual cancel unstuck it. Close on both published and already_published: the release shipped either way. _heartbeat_loop returned silently when the lock was no longer owned (a >TTL Redis outage let the mutex expire mid-execute), leaving executor.execute running UNGUARDED — a concurrent approve (once Redis returns) could then acquire the lock and _prepare_release_clone rm -rf the in-flight shared release clone while the first execute was still mid-run_gate, re-opening the very rm -rf-clone race the mutex+heartbeat exist to prevent. Run execute as a task; on lock-loss the heartbeat sets a flag and cancels it, and approve() turns the CancelledError into a structured 'lock_lost' result (an external cancellation of approve itself still propagates — distinguished by the flag). TDD: 2 red→green (already_published → COMPLETED not wedged; heartbeat lock-loss → lock_lost + execute cancelled, proposal not completed). 8 concurrency tests green; ruff/mypy clean.