From 8a062af60fa79999c39f36d34eedc5bd6064e54d Mon Sep 17 00:00:00 2001 From: Philip Date: Tue, 4 Aug 2026 16:49:47 -0500 Subject: [PATCH] Dispatch fixes: objectives no longer eat slots; escalation gets fresh kanban task - in-flight count excluded objectives: two open objectives + one task hit MAX_IN_FLIGHT=3 and starved dispatch to 1 concurrent for 75 minutes - idempotency key now includes owner_id: re-dispatch after consultant escalation was deduping onto the old owner's blocked kanban task, so the consultant never actually ran Co-Authored-By: Claude Fable 5 --- engine/dispatcher.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/dispatcher.py b/engine/dispatcher.py index 7662f7f..f3a0be4 100644 --- a/engine/dispatcher.py +++ b/engine/dispatcher.py @@ -226,7 +226,8 @@ class Dispatcher: async def _dispatch_ready(self) -> None: in_flight = (await db.fetch_one( - "SELECT count(*) AS n FROM work_item WHERE status = 'in_progress'"))["n"] + "SELECT count(*) AS n FROM work_item WHERE status = 'in_progress' " + "AND type IN ('task','subtask','defect','research','review')"))["n"] slots = MAX_IN_FLIGHT - in_flight if slots <= 0: return @@ -279,7 +280,9 @@ class Dispatcher: body=_task_body(item) + await self._sibling_context(item), assignee=item["hermes_profile"], tenant=item["company_slug"], - idempotency_key=str(item["id"]), + # owner in the key: re-dispatch after escalation must create a + # fresh kanban task, not dedupe onto the old owner's blocked one + idempotency_key=f"{item['id']}:{item['owner_id']}", max_runtime_seconds=int(budget.get("max_wall_seconds", 1800)), goal_max_turns=int(budget.get("max_iterations", 15)), workspace_path=(item.get("company_config") or {}).get("workspace_path"),