fix(kanban): claim-less complete no longer closes a live worker's run

complete_task authorised a terminal transition by task status alone; the
`current_run_id = ?` fence only applied when the caller volunteered
expected_run_id (derived from HERMES_KANBAN_* env). A human at the CLI, an
orchestrator session or any env-less caller therefore marked a `running`
card done and _end_run closed the dispatcher worker's run row while that
worker kept executing (#111764).

Mirror the fence request_review already carries: a `running` task under a
live claim needs expected_run_id (worker ownership) or force=True (explicit
operator override), otherwise LiveClaimError. `hermes kanban complete
--force` and the dashboard's "mark done" (a human action) carry the override;
the kanban_complete tool reports a structured refusal. Completing `ready`,
`blocked` or `review` cards without a claim is unchanged, so the manual /
orchestrator flows PR #73188 pinned keep working.

Fixes #111764
This commit is contained in:
teknium1
2026-09-15 12:07:49 -07:00
committed by Teknium
parent c7f4bc5bd7
commit 0959224313
7 changed files with 118 additions and 8 deletions
+6
View File
@@ -598,6 +598,12 @@ def _handle_complete(args: dict, **kw) -> str:
f"Your task is still in-flight and its scratch workspace was kept. Fix the "
f"artifact path or storage error, then retry kanban_complete with the same "
f"handoff.")
except kb.LiveClaimError as claim_err:
# Env-less caller (orchestrator, another session) on a card a dispatcher
# worker is executing: refusing here is what keeps that worker's run open.
return tool_error(
f"kanban_complete refused: {claim_err}. Nothing changed. Wait for the worker "
f"to finish, or an operator can run `hermes kanban complete --force {tid}`.")
except kb.HallucinatedCardsError as hall_err:
# The gate runs before the write txn, so the task was NOT mutated;
# say so explicitly or the model treats the error as terminal and