fix(gateway): unblock PM planning + drop magic delegate task_type

Two coupled fixes from the 2026-05-08 smoke-test trace:

1. pm_cannot_execute_code is now scoped to i_will_work_on (the
   EXECUTION verb) only. Pre-fix it also fired on i_will_plan, which
   deadlocked any code-typed parent: cell_pm couldn't plan, so couldn't
   transition parent to in_progress, so couldn't delegate. PMs PLAN
   code-typed parents and DELEGATE the work — that's exactly the verb
   we were blocking.

2. delegate.task_type is now REQUIRED at both the HTTP boundary
   (DelegateRequest) and the choreographer dataclass (DelegateInputs).
   The pre-fix default of 'code' silently changed semantics whenever a
   caller forgot the field — main-pm's call in the smoke trace omitted
   it, schema defaulted to 'code', and the cell PM downstream was
   wedged. Also drops the choreographer's task.task_type fallback
   (the DB column is NOT NULL anyway).

Plus middleware coverage tests for the parallel ServiceError →
4xx handler hierarchy added in the prior session, restoring 100%
coverage across the touched files.

Tests: 3101 passing, 100% coverage, ruff clean.
This commit is contained in:
Renn F
2026-05-08 07:45:18 +02:00
parent f0eec854d1
commit 01ff44b83f
11 changed files with 350 additions and 17 deletions
@@ -258,12 +258,15 @@ async def test_delegate_dispatches_inputs_bundle() -> None:
"description": "Add the foo endpoint with tests.",
"assigned_to": "be-dev-1",
"team": "backend",
"task_type": "code",
},
headers=_HEADERS,
)
assert resp.status_code == _HTTP_200
mock_chore.delegate.assert_awaited_once()
inputs = mock_chore.delegate.await_args.args[2]
assert inputs.task_type == "code"
@pytest.mark.asyncio
@@ -229,11 +229,15 @@ async def test_delegate_to_cell_pm_dispatches_inputs_bundle() -> None:
"description": "Plan + drive backend work for feature X.",
"assigned_to": "be-pm",
"team": "backend",
"task_type": "planning",
},
headers=_HEADERS,
)
assert resp.status_code == _HTTP_200
mock_chore.delegate.assert_awaited_once()
inputs = mock_chore.delegate.await_args.args[2]
assert inputs.task_type == "planning"
@pytest.mark.asyncio