mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
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:
parent
aea570db4e
commit
77db9d6bf3
2 changed files with 48 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue