fix(kanban): explicit re-queue bypasses the recent-success respawn guard

Dragging a task done→ready did nothing: the respawn guard saw a run that
completed within the success window and deferred forever, unable to tell a
deliberate operator re-run from a status flap. Now a re-queue event
(status change, promote, unblock, reclaim) AFTER the completion bypasses
the recent_success guard, so an explicit done→ready runs again.
This commit is contained in:
Brooklyn Nicholson 2026-07-09 16:05:21 -05:00
parent aea570db4e
commit 77db9d6bf3
2 changed files with 48 additions and 6 deletions

View file

@ -6789,7 +6789,9 @@ def check_respawn_guard(conn: sqlite3.Connection, task_id: str) -> Optional[str]
``"recent_success"``
A completed run exists within ``_RESPAWN_GUARD_SUCCESS_WINDOW``
seconds. Useful work already succeeded for this task; wait for
human review rather than immediately re-spawning.
human review rather than immediately re-spawning. Bypassed when an
explicit re-queue event (status change, promote, unblock, reclaim)
arrives AFTER that completion that's a deliberate re-run request.
``"active_pr"``
A GitHub PR URL appears in a recent task comment (within
@ -6852,13 +6854,29 @@ def check_respawn_guard(conn: sqlite3.Connection, task_id: str) -> Optional[str]
return "blocker_auth"
# 3. Completed run within guard window — proof of recent success.
# Exception: an explicit re-queue AFTER that success (an operator
# dragging done→ready, a dependency re-promotion, an unblock, a
# reclaim) is a deliberate "run it again" — honor it instead of
# deferring. Without this, a manual done→ready just sits there,
# silently held by the guard, until the window elapses.
cutoff = now - _RESPAWN_GUARD_SUCCESS_WINDOW
if conn.execute(
"SELECT id FROM task_runs "
"WHERE task_id = ? AND outcome = 'completed' AND ended_at >= ?",
recent_completed = conn.execute(
"SELECT ended_at FROM task_runs "
"WHERE task_id = ? AND outcome = 'completed' AND ended_at >= ? "
"ORDER BY ended_at DESC LIMIT 1",
(task_id, cutoff),
).fetchone():
return "recent_success"
).fetchone()
if recent_completed:
completed_at = int(recent_completed["ended_at"] or 0)
requeued_after = conn.execute(
"SELECT 1 FROM task_events "
"WHERE task_id = ? AND created_at >= ? "
"AND kind IN ('status', 'promoted', 'unblocked', 'reclaimed') "
"LIMIT 1",
(task_id, completed_at),
).fetchone()
if not requeued_after:
return "recent_success"
# 4. GitHub PR URL in a recent comment — prior worker already opened a PR.
pr_cutoff = now - _RESPAWN_GUARD_PR_WINDOW

View file

@ -1879,6 +1879,30 @@ def test_respawn_guard_recent_success(kanban_home):
assert reason == "recent_success"
def test_respawn_guard_recent_success_bypassed_by_requeue(kanban_home):
"""An explicit re-queue after a recent success (operator done->ready,
promote, unblock, reclaim) is a deliberate re-run and must bypass the
recent_success guard otherwise a manual done->ready just sits there
until the window elapses."""
with kb.connect() as conn:
t = kb.create_task(conn, title="rerun-me", assignee="alice")
now = int(time.time())
conn.execute(
"INSERT INTO task_runs (task_id, status, outcome, started_at, ended_at) "
"VALUES (?, 'done', 'completed', ?, ?)",
(t, now - 120, now - 60),
)
# Baseline: a recent completion defers the respawn.
assert kb.check_respawn_guard(conn, t) == "recent_success"
# Operator drags done -> ready: a 'status' event after completion.
conn.execute(
"INSERT INTO task_events (task_id, kind, created_at) "
"VALUES (?, 'status', ?)",
(t, now - 10),
)
assert kb.check_respawn_guard(conn, t) is None
def test_respawn_guard_stale_success_not_guarded(kanban_home):
"""A completed run outside the guard window does not block re-spawn."""
with kb.connect() as conn: