fix(gateway): B6 give_me_work returns pre-assigned pending tasks first

Smoke run 3 showed Main PM's first give_me_work() returning
{status: idle, next: 'no Main PM work'} even though c7935d2c was
pending and assigned to Main PM. The filter only walked
list_assigned_for_agent (ordered by priority/updated_at — pending
could rank behind in_progress rows) and the PM path fell through
to idle because the pre-assigned pending case was not checked first.

Pre-pended a list_pending_for_agent check in both give_me_work and
pm_give_me_work: tasks where assigned_to=agent_id AND status=pending
take priority over all other lookups. Added TaskService.list_pending_for_agent
for the query (ordered by sequence, priority, created_at).

Updated existing tests in test_choreographer_dev, test_choreographer_pm_extras,
and test_heartbeat_wired to set list_pending_for_agent.return_value=[]
where they were not testing the pre-assigned path.

Spec ref: docs/superpowers/specs/2026-05-12-post-smoke-3-fixes-design.md
section B6.
This commit is contained in:
Renn F
2026-05-12 04:44:03 +02:00
parent 85e20e6a2a
commit d73e86044b
6 changed files with 311 additions and 0 deletions
@@ -909,6 +909,7 @@ async def test_pm_give_me_work_returns_first_assigned() -> None:
pm_id = uuid4()
t = MagicMock(id=uuid4(), status="pending", title="x", team="backend")
task_svc = AsyncMock()
task_svc.list_pending_for_agent.return_value = []
task_svc.list_assigned_for_agent.return_value = [t]
deps = _make_deps(task=task_svc)
c = Choreographer(deps)
@@ -923,6 +924,7 @@ async def test_pm_give_me_work_returns_first_assigned() -> None:
async def test_pm_give_me_work_returns_idle_when_empty() -> None:
pm_id = uuid4()
task_svc = AsyncMock()
task_svc.list_pending_for_agent.return_value = []
task_svc.list_assigned_for_agent.return_value = []
deps = _make_deps(task=task_svc)
c = Choreographer(deps)
@@ -936,6 +938,7 @@ async def test_pm_give_me_work_paused_hint_mentions_subtasks() -> None:
pm_id = uuid4()
t = MagicMock(id=uuid4(), status="paused", title="x", team="backend")
task_svc = AsyncMock()
task_svc.list_pending_for_agent.return_value = []
task_svc.list_assigned_for_agent.return_value = [t]
deps = _make_deps(task=task_svc)
c = Choreographer(deps)