diff --git a/agent/turn_finalizer.py b/agent/turn_finalizer.py index 4e2d318b2e2c2..9006963e097c3 100644 --- a/agent/turn_finalizer.py +++ b/agent/turn_finalizer.py @@ -151,6 +151,10 @@ def finalize_turn( # We route through ``_record_task_failure(outcome="timed_out")`` # rather than ``kanban_block`` so this counts toward the dispatcher's # consecutive-failure circuit breaker (#29747 gap 2). + # Hold the claim (release_claim=False) so the dispatcher cannot + # spawn a second worker into the same worktree while this process + # is still alive — the goal loop will continue with a fresh + # iteration budget until goal_max_turns is exhausted (#71175). _kanban_task = os.environ.get("HERMES_KANBAN_TASK") if _kanban_task: try: @@ -167,7 +171,7 @@ def finalize_turn( "iterations" ), outcome="timed_out", - release_claim=True, + release_claim=False, end_run=True, event_payload_extra={ "budget_used": api_call_count, diff --git a/tests/agent/test_turn_finalizer_iteration_limit_exit.py b/tests/agent/test_turn_finalizer_iteration_limit_exit.py index f1920634b779b..554e5bc89e197 100644 --- a/tests/agent/test_turn_finalizer_iteration_limit_exit.py +++ b/tests/agent/test_turn_finalizer_iteration_limit_exit.py @@ -292,7 +292,7 @@ def test_pending_response_records_kanban_timeout(monkeypatch): "within the allowed iterations" ), outcome="timed_out", - release_claim=True, + release_claim=False, end_run=True, event_payload_extra={"budget_used": 60, "budget_max": 60}, ) diff --git a/tests/run_agent/test_run_agent.py b/tests/run_agent/test_run_agent.py index 3dfa2aff0d4d4..507b693dc3b55 100644 --- a/tests/run_agent/test_run_agent.py +++ b/tests/run_agent/test_run_agent.py @@ -5994,7 +5994,7 @@ def test_kanban_block_called_on_iteration_exhaustion(self, agent, monkeypatch): # Positional: (conn, task_id, ...) assert call.args[1] == "t_test_task_123" assert call.kwargs.get("outcome") == "timed_out" - assert call.kwargs.get("release_claim") is True + assert call.kwargs.get("release_claim") is False assert call.kwargs.get("end_run") is True assert "Iteration budget exhausted" in call.kwargs.get("error", "")