Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hermes_cli/kanban_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2678,7 +2678,8 @@ def unblock_task(conn: sqlite3.Connection, task_id: str) -> bool:
).fetchone()
new_status = "todo" if undone_parents else "ready"
cur = conn.execute(
"UPDATE tasks SET status = ?, current_run_id = NULL "
"UPDATE tasks SET status = ?, current_run_id = NULL, "
"consecutive_failures = 0, last_failure_error = NULL "
"WHERE id = ? AND status = 'blocked'",
(new_status, task_id),
)
Expand Down
20 changes: 20 additions & 0 deletions tests/hermes_cli/test_kanban_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,26 @@ def test_block_then_unblock(kanban_home):
assert kb.get_task(conn, t).status == "ready"


def test_unblock_resets_failure_counters(kanban_home):
"""unblock_task must reset consecutive_failures and last_failure_error."""
with kb.connect() as conn:
t = kb.create_task(conn, title="x", assignee="a")
kb.claim_task(conn, t)
assert kb.block_task(conn, t, reason="need input")
# Simulate accumulated failures from the circuit breaker
conn.execute(
"UPDATE tasks SET consecutive_failures = 5, "
"last_failure_error = 'test error' WHERE id = ?",
(t,),
)
conn.commit()
assert kb.unblock_task(conn, t)
task = kb.get_task(conn, t)
assert task.status == "ready"
assert task.consecutive_failures == 0
assert task.last_failure_error is None


# ---------------------------------------------------------------------------
# Parent-completion invariant at the claim gate (RCA t_a6acd07d)
# ---------------------------------------------------------------------------
Expand Down