diff --git a/hermes_cli/kanban_db.py b/hermes_cli/kanban_db.py index 4a80385b6f..04e747c57e 100644 --- a/hermes_cli/kanban_db.py +++ b/hermes_cli/kanban_db.py @@ -3495,6 +3495,18 @@ def specify_triage_task( def archive_task(conn: sqlite3.Connection, task_id: str) -> bool: + # Snapshot pid+claim before the DB update clears them, so we can + # terminate the worker process even after those columns are NULLed. + row = conn.execute( + "SELECT worker_pid, claim_lock FROM tasks WHERE id = ?", + (task_id,), + ).fetchone() + prev_pid = row["worker_pid"] if row else None + prev_lock = row["claim_lock"] if row else None + # Terminate the worker process first (same order as reclaim_task). + # If the task wasn't running on this host, this is a safe no-op. + termination = _terminate_reclaimed_worker(prev_pid, prev_lock) + with write_txn(conn): cur = conn.execute( "UPDATE tasks SET status = 'archived', " @@ -3508,7 +3520,9 @@ def archive_task(conn: sqlite3.Connection, task_id: str) -> bool: conn, task_id, outcome="reclaimed", status="reclaimed", summary="task archived with run still active", ) - _append_event(conn, task_id, "archived", None, run_id=run_id) + payload = {} + payload.update(termination) + _append_event(conn, task_id, "archived", payload, run_id=run_id) # ``archived`` parents no longer block children; promote them now. recompute_ready(conn) # Reap the workspace on archive too (never-completed tasks kept it forever).