mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(gateway): hold pre-assigned dev at give_me_work's list_assigned fallback when deps are unmet
This commit is contained in:
@@ -613,6 +613,29 @@ class Choreographer:
|
|||||||
return f"call i_will_plan(task_id='{tid}', plan='<plan>') to start"
|
return f"call i_will_plan(task_id='{tid}', plan='<plan>') to start"
|
||||||
return f"call i_will_work_on(task_id='{tid}', plan='<plan>') to start"
|
return f"call i_will_work_on(task_id='{tid}', plan='<plan>') to start"
|
||||||
|
|
||||||
|
async def _drop_dependency_held(self, tasks: list[Any]) -> list[Any]:
|
||||||
|
"""Drop pre-assigned PENDING tasks whose non-terminal dependencies are
|
||||||
|
still unresolved.
|
||||||
|
|
||||||
|
``give_me_work``'s ``list_assigned_for_agent`` fallback includes PENDING
|
||||||
|
rows with no dependency filter, so without this a held pre-assigned
|
||||||
|
subtask (e.g. a frontend dev's task waiting on the UX/UI design) would
|
||||||
|
still be offered and the agent only bounced at claim time. Mirrors the
|
||||||
|
gate in ``TaskService.list_pending_for_agent`` and ``_run_claim_guards``.
|
||||||
|
Only PENDING rows are gated — an already-claimed task is past the gate.
|
||||||
|
"""
|
||||||
|
offerable: list[Any] = []
|
||||||
|
for task in tasks:
|
||||||
|
dep_ids = list(getattr(task, "dependency_ids", []) or [])
|
||||||
|
if (
|
||||||
|
str(task.status) == "pending"
|
||||||
|
and dep_ids
|
||||||
|
and await self._deps.task.unmet_dependency_ids(dep_ids)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
offerable.append(task)
|
||||||
|
return offerable
|
||||||
|
|
||||||
async def give_me_work(self, agent_id: UUID) -> Envelope:
|
async def give_me_work(self, agent_id: UUID) -> Envelope:
|
||||||
"""Return the agent's most-actionable task or signal idle."""
|
"""Return the agent's most-actionable task or signal idle."""
|
||||||
agent = await self._deps.task.agent_for(agent_id)
|
agent = await self._deps.task.agent_for(agent_id)
|
||||||
@@ -632,7 +655,9 @@ class Choreographer:
|
|||||||
next=self._claim_verb_hint(role, t),
|
next=self._claim_verb_hint(role, t),
|
||||||
context_briefing=await self._briefing_for(agent_id, t.id),
|
context_briefing=await self._briefing_for(agent_id, t.id),
|
||||||
).with_introspection(task=t, role=role)
|
).with_introspection(task=t, role=role)
|
||||||
assigned = await self._deps.task.list_assigned_for_agent(agent_id)
|
assigned = await self._drop_dependency_held(
|
||||||
|
await self._deps.task.list_assigned_for_agent(agent_id)
|
||||||
|
)
|
||||||
if assigned:
|
if assigned:
|
||||||
t = assigned[0]
|
t = assigned[0]
|
||||||
return Envelope.ok(
|
return Envelope.ok(
|
||||||
|
|||||||
@@ -274,10 +274,20 @@ async def test_all_three_dev_paths_gate_then_release(dep_gate_setup: dict) -> No
|
|||||||
assert issue is not None, "spawn validation must hold the dev while UX is unmet"
|
assert issue is not None, "spawn validation must hold the dev while UX is unmet"
|
||||||
assert "dependency" in issue
|
assert "dependency" in issue
|
||||||
|
|
||||||
# --- (b) give_me_work: list_pending_for_agent ---
|
# --- (b) give_me_work has TWO offer paths and both must exclude the held
|
||||||
|
# subtask: list_pending_for_agent (primary) and the
|
||||||
|
# list_assigned_for_agent fallback (PENDING rows, no built-in dep filter).
|
||||||
offered = await svc.list_pending_for_agent(fe_dev_db_id)
|
offered = await svc.list_pending_for_agent(fe_dev_db_id)
|
||||||
assert dev_subtask.id not in {t.id for t in offered}, (
|
assert dev_subtask.id not in {t.id for t in offered}, (
|
||||||
"give_me_work must not offer the dev subtask while UX is unmet"
|
"list_pending_for_agent must not offer the held dev subtask"
|
||||||
|
)
|
||||||
|
assigned = await svc.list_assigned_for_agent(fe_dev_db_id)
|
||||||
|
assert dev_subtask.id in {t.id for t in assigned}, (
|
||||||
|
"sanity: the held subtask is assigned+pending, so the fallback path is real"
|
||||||
|
)
|
||||||
|
offerable = await choreo._drop_dependency_held(assigned)
|
||||||
|
assert dev_subtask.id not in {t.id for t in offerable}, (
|
||||||
|
"give_me_work's list_assigned_for_agent fallback must not offer it"
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- (c) claim: _run_claim_guards (the i_will_work_on guard set) ---
|
# --- (c) claim: _run_claim_guards (the i_will_work_on guard set) ---
|
||||||
@@ -300,7 +310,13 @@ async def test_all_three_dev_paths_gate_then_release(dep_gate_setup: dict) -> No
|
|||||||
)
|
)
|
||||||
offered_after = await svc.list_pending_for_agent(fe_dev_db_id)
|
offered_after = await svc.list_pending_for_agent(fe_dev_db_id)
|
||||||
assert dev_subtask.id in {t.id for t in offered_after}, (
|
assert dev_subtask.id in {t.id for t in offered_after}, (
|
||||||
"give_me_work must offer the dev subtask once UX is terminal"
|
"list_pending_for_agent must offer the dev subtask once UX is terminal"
|
||||||
|
)
|
||||||
|
offerable_after = await choreo._drop_dependency_held(
|
||||||
|
await svc.list_assigned_for_agent(fe_dev_db_id)
|
||||||
|
)
|
||||||
|
assert dev_subtask.id in {t.id for t in offerable_after}, (
|
||||||
|
"the assigned-fallback gate must release the subtask once UX is terminal"
|
||||||
)
|
)
|
||||||
released = await svc.get(dev_subtask.id)
|
released = await svc.get(dev_subtask.id)
|
||||||
assert (
|
assert (
|
||||||
|
|||||||
Reference in New Issue
Block a user