mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(gateway): thread agent_team through all 27 lifecycle Context sites
Arms the team-match spec gate that sat in its permissive fallback since shipping: cell-scoped roles are now rejected on cross-team verbs at the gateway (a misrouted frontend PM blocked, escalated, and held a backend task live 2026-07-02). Org-wide roles remain exempt via the policy-side _ORG_WIDE_ROLES. Test mocks aligned to carry consistent teams.
This commit is contained in:
@@ -1437,6 +1437,7 @@ class Choreographer:
|
|||||||
plan=effective_plan,
|
plan=effective_plan,
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
ctx = _ClaimPlanStartContext(
|
ctx = _ClaimPlanStartContext(
|
||||||
@@ -1680,6 +1681,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
if rejection := self._open_pr_preflight_rejection(
|
if rejection := self._open_pr_preflight_rejection(
|
||||||
@@ -1878,6 +1880,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
)
|
)
|
||||||
@@ -3295,6 +3298,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=reason,
|
notes=reason,
|
||||||
)
|
)
|
||||||
@@ -3388,6 +3392,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "unclaim", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "unclaim", t, spec_ctx)
|
||||||
@@ -3543,6 +3548,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "reassign", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "reassign", t, spec_ctx)
|
||||||
@@ -3630,6 +3636,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "resume", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "resume", t, spec_ctx)
|
||||||
@@ -3799,6 +3806,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "sync_branch", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "sync_branch", t, spec_ctx)
|
||||||
if not decision.allowed:
|
if not decision.allowed:
|
||||||
@@ -4293,6 +4301,7 @@ class Choreographer:
|
|||||||
plan=effective_plan,
|
plan=effective_plan,
|
||||||
actor_id=pm_agent_id,
|
actor_id=pm_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
ctx = _ClaimPlanStartContext(
|
ctx = _ClaimPlanStartContext(
|
||||||
@@ -4393,6 +4402,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=pm_agent_id,
|
actor_id=pm_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(parent),
|
original_developer_slug=_extract_original_developer(parent),
|
||||||
)
|
)
|
||||||
soup = self._free_text_soup(
|
soup = self._free_text_soup(
|
||||||
@@ -5587,6 +5597,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=pm_agent_id,
|
actor_id=pm_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
)
|
)
|
||||||
@@ -6522,6 +6533,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=main_pm_agent_id,
|
actor_id=main_pm_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
notes=notes,
|
notes=notes,
|
||||||
)
|
)
|
||||||
soup = self._free_text_soup(checks=(("notes", notes, 10),))
|
soup = self._free_text_soup(checks=(("notes", notes, 10),))
|
||||||
@@ -6873,6 +6885,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
if soup := await self._guard_free_text(
|
if soup := await self._guard_free_text(
|
||||||
@@ -7011,6 +7024,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=pm_agent_id,
|
actor_id=pm_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
issues=tuple(issues),
|
issues=tuple(issues),
|
||||||
@@ -7081,6 +7095,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=pm_agent_id,
|
actor_id=pm_agent_id,
|
||||||
actor_slug=getattr(me, "slug", None) if me is not None else None,
|
actor_slug=getattr(me, "slug", None) if me is not None else None,
|
||||||
|
agent_team=str(me.team) if me is not None and me.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=reason,
|
notes=reason,
|
||||||
)
|
)
|
||||||
@@ -7251,6 +7266,7 @@ class Choreographer:
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=agent_id,
|
actor_id=agent_id,
|
||||||
actor_slug=getattr(me, "slug", None) if me is not None else None,
|
actor_slug=getattr(me, "slug", None) if me is not None else None,
|
||||||
|
agent_team=str(me.team) if me is not None and me.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=reason,
|
notes=reason,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class DocMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=doc_agent_id,
|
actor_id=doc_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "claim_doc_task", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "claim_doc_task", t, spec_ctx)
|
||||||
@@ -374,10 +375,21 @@ class DocMixin(_Base):
|
|||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
verb="i_documented",
|
verb="i_documented",
|
||||||
)
|
)
|
||||||
return rejection, agent, role_str, spec_module.Context(), briefing
|
return (
|
||||||
|
rejection,
|
||||||
|
agent,
|
||||||
|
role_str,
|
||||||
|
spec_module.Context(
|
||||||
|
agent_team=str(agent.team)
|
||||||
|
if agent is not None and agent.team
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
briefing,
|
||||||
|
)
|
||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=doc_agent_id,
|
actor_id=doc_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(owned_task),
|
original_developer_slug=_extract_original_developer(owned_task),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
files=tuple(files),
|
files=tuple(files),
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class PRGateMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=reviewer_agent_id,
|
actor_id=reviewer_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "claim_gate_review", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "claim_gate_review", t, spec_ctx)
|
||||||
if not decision.allowed:
|
if not decision.allowed:
|
||||||
@@ -207,6 +208,7 @@ class PRGateMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=reviewer_agent_id,
|
actor_id=reviewer_agent_id,
|
||||||
actor_slug=str(reviewer_agent_id),
|
actor_slug=str(reviewer_agent_id),
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=markers.get_original_developer(t),
|
original_developer_slug=markers.get_original_developer(t),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
issues=issues,
|
issues=issues,
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class PRReviewerMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=reviewer_agent_id,
|
actor_id=reviewer_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "claim_pr_review", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "claim_pr_review", t, spec_ctx)
|
||||||
if not decision.allowed:
|
if not decision.allowed:
|
||||||
@@ -328,6 +329,7 @@ class PRReviewerMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=reviewer_agent_id,
|
actor_id=reviewer_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
notes=body,
|
notes=body,
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "post_pr_review", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "post_pr_review", t, spec_ctx)
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class QAMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=qa_agent_id,
|
actor_id=qa_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
)
|
)
|
||||||
decision = spec_module.can_invoke_intent(role, "claim_review", t, spec_ctx)
|
decision = spec_module.can_invoke_intent(role, "claim_review", t, spec_ctx)
|
||||||
@@ -328,6 +329,7 @@ class QAMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=qa_agent_id,
|
actor_id=qa_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=inputs.notes,
|
notes=inputs.notes,
|
||||||
issues=inputs.issues,
|
issues=inputs.issues,
|
||||||
@@ -554,6 +556,7 @@ class QAMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=qa_agent_id,
|
actor_id=qa_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=self._merge_ac_verdicts_into_notes(notes, ac_verdicts),
|
notes=self._merge_ac_verdicts_into_notes(notes, ac_verdicts),
|
||||||
)
|
)
|
||||||
@@ -648,6 +651,7 @@ class QAMixin(_Base):
|
|||||||
spec_ctx = spec_module.Context(
|
spec_ctx = spec_module.Context(
|
||||||
actor_id=qa_agent_id,
|
actor_id=qa_agent_id,
|
||||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||||
|
agent_team=str(agent.team) if agent is not None and agent.team else None,
|
||||||
original_developer_slug=_extract_original_developer(t),
|
original_developer_slug=_extract_original_developer(t),
|
||||||
notes=notes,
|
notes=notes,
|
||||||
issues=tuple(issues),
|
issues=tuple(issues),
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ def _dev_task_svc(agent_id: object, task_id: object) -> AsyncMock:
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
task_type="code",
|
task_type="code",
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ async def test_submit_up_blocks_when_subtask_pending() -> None:
|
|||||||
sub = MagicMock(id=sub_id, status="paused", title="Paused subtask")
|
sub = MagicMock(id=sub_id, status="paused", title="Paused subtask")
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(role="cell_pm")
|
task_svc.agent_for.return_value = MagicMock(role="cell_pm", team="backend")
|
||||||
task_svc.all_subtasks_terminal.return_value = False
|
task_svc.all_subtasks_terminal.return_value = False
|
||||||
task_svc.get_subtasks.return_value = [sub]
|
task_svc.get_subtasks.return_value = [sub]
|
||||||
journal_svc = AsyncMock()
|
journal_svc = AsyncMock()
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ async def test_delegate_blocks_when_parent_assigned_to_other_agent() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=other_pm_id,
|
assigned_to=other_pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
@@ -134,6 +135,7 @@ async def test_delegate_allows_when_parent_in_progress_and_owned() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
new_task = MagicMock(id=uuid4())
|
new_task = MagicMock(id=uuid4())
|
||||||
@@ -162,6 +164,7 @@ async def test_delegate_blocks_when_subtask_cap_exceeded() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
too_many = [MagicMock(id=uuid4()) for _ in range(13)]
|
too_many = [MagicMock(id=uuid4()) for _ in range(13)]
|
||||||
@@ -189,6 +192,7 @@ async def test_delegate_allows_when_subtask_cap_within_soft_zone() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
many = [MagicMock(id=uuid4()) for _ in range(10)]
|
many = [MagicMock(id=uuid4()) for _ in range(10)]
|
||||||
@@ -217,6 +221,7 @@ async def test_delegate_allows_at_zero_subtasks() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
new_task = MagicMock(id=uuid4())
|
new_task = MagicMock(id=uuid4())
|
||||||
@@ -243,6 +248,7 @@ async def test_delegate_blocks_at_exact_cap_plus_one() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
# Already 12 children — adding the 13th must be blocked.
|
# Already 12 children — adding the 13th must be blocked.
|
||||||
@@ -272,6 +278,7 @@ async def test_delegate_blocks_when_parent_quick_context_empty() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="",
|
quick_context="",
|
||||||
)
|
)
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
@@ -304,6 +311,7 @@ async def test_delegate_past_max_depth_returns_invalid_state_not_500() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
depth_msg = (
|
depth_msg = (
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ async def test_i_will_work_on_pending_with_plan() -> None:
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
task_type="code",
|
task_type="code",
|
||||||
@@ -338,6 +339,7 @@ async def test_i_will_work_on_blocks_when_journal_note_at_claim_missing() -> Non
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
task_type="code",
|
task_type="code",
|
||||||
|
|||||||
@@ -632,6 +632,7 @@ async def test_delegate_parent_no_project_rejected() -> None:
|
|||||||
parent = MagicMock(
|
parent = MagicMock(
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
project_id=None,
|
project_id=None,
|
||||||
product_id=None,
|
product_id=None,
|
||||||
title="p",
|
title="p",
|
||||||
|
|||||||
@@ -757,7 +757,7 @@ async def test_complete_dispatches_cell_pm() -> None:
|
|||||||
after = MagicMock(**{**t.__dict__, "status": "completed"})
|
after = MagicMock(**{**t.__dict__, "status": "completed"})
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(role="cell_pm")
|
task_svc.agent_for.return_value = MagicMock(role="cell_pm", team="backend")
|
||||||
task_svc.all_subtasks_terminal.return_value = True
|
task_svc.all_subtasks_terminal.return_value = True
|
||||||
task_svc.cell_pm_complete.return_value = after
|
task_svc.cell_pm_complete.return_value = after
|
||||||
git_svc = AsyncMock()
|
git_svc = AsyncMock()
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ async def test_i_will_plan_claims_starts_and_sets_plan() -> None:
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
task_type="planning",
|
task_type="planning",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
@@ -164,6 +165,7 @@ async def test_i_will_plan_blocks_when_journal_decision_at_claim_missing() -> No
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
task_type="planning",
|
task_type="planning",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
@@ -715,6 +717,7 @@ async def test_delegate_cell_pm_to_team_dev_creates_subtask() -> None:
|
|||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=cell_pm_id,
|
assigned_to=cell_pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
new_task = MagicMock(id=uuid4())
|
new_task = MagicMock(id=uuid4())
|
||||||
@@ -862,6 +865,7 @@ async def test_delegate_invalid_team_enum_rejected() -> None:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
@@ -1013,7 +1017,7 @@ async def test_submit_up_blocks_without_journal_decision() -> None:
|
|||||||
async def test_submit_up_short_notes_rejected() -> None:
|
async def test_submit_up_short_notes_rejected() -> None:
|
||||||
pm_id = uuid4()
|
pm_id = uuid4()
|
||||||
task_id = uuid4()
|
task_id = uuid4()
|
||||||
t = MagicMock(id=task_id, status="in_progress", assigned_to=pm_id)
|
t = MagicMock(id=task_id, status="in_progress", assigned_to=pm_id, team="backend")
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(role="cell_pm", team="backend")
|
task_svc.agent_for.return_value = MagicMock(role="cell_pm", team="backend")
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ async def test_i_will_plan_calls_claim_and_start_with_task_id_first() -> None:
|
|||||||
status="pending",
|
status="pending",
|
||||||
plan=None,
|
plan=None,
|
||||||
assigned_to=None,
|
assigned_to=None,
|
||||||
|
team="backend",
|
||||||
task_type="planning",
|
task_type="planning",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ def _parent_in_progress(pm_id: Any) -> MagicMock:
|
|||||||
project_id=uuid4(),
|
project_id=uuid4(),
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
priority=2,
|
priority=2,
|
||||||
# delegate obligates the PM's quick_context resumption section.
|
# delegate obligates the PM's quick_context resumption section.
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ def _parent(pm_id: object) -> MagicMock:
|
|||||||
product_id=None,
|
product_id=None,
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
# delegate obligates the PM's quick_context resumption section.
|
# delegate obligates the PM's quick_context resumption section.
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ def _parent(pm_id: Any, product_id: Any = None, project_id: Any = None) -> Magic
|
|||||||
product_id=product_id,
|
product_id=product_id,
|
||||||
status="in_progress",
|
status="in_progress",
|
||||||
assigned_to=pm_id,
|
assigned_to=pm_id,
|
||||||
|
team="backend",
|
||||||
# delegate obligates the PM's quick_context resumption section.
|
# delegate obligates the PM's quick_context resumption section.
|
||||||
quick_context="Decomposition planned; cells implement their slice next.",
|
quick_context="Decomposition planned; cells implement their slice next.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def _make_deps(**overrides: Any) -> ChoreographerDeps:
|
|||||||
async def test_resume_transitions_paused_to_in_progress() -> None:
|
async def test_resume_transitions_paused_to_in_progress() -> None:
|
||||||
aid = uuid4()
|
aid = uuid4()
|
||||||
tid = uuid4()
|
tid = uuid4()
|
||||||
t = MagicMock(id=tid, status="paused", assigned_to=aid)
|
t = MagicMock(id=tid, status="paused", assigned_to=aid, team="backend")
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(
|
task_svc.agent_for.return_value = MagicMock(
|
||||||
@@ -108,7 +108,7 @@ async def test_resume_rejects_when_not_claimant() -> None:
|
|||||||
aid = uuid4()
|
aid = uuid4()
|
||||||
other = uuid4()
|
other = uuid4()
|
||||||
tid = uuid4()
|
tid = uuid4()
|
||||||
t = MagicMock(id=tid, status="paused", assigned_to=other)
|
t = MagicMock(id=tid, status="paused", assigned_to=other, team="backend")
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(
|
task_svc.agent_for.return_value = MagicMock(
|
||||||
@@ -177,7 +177,7 @@ async def test_resume_success_writes_heartbeat() -> None:
|
|||||||
"""Heartbeat fires on success — agent is back to active work."""
|
"""Heartbeat fires on success — agent is back to active work."""
|
||||||
aid = uuid4()
|
aid = uuid4()
|
||||||
tid = uuid4()
|
tid = uuid4()
|
||||||
t = MagicMock(id=tid, status="paused", assigned_to=aid)
|
t = MagicMock(id=tid, status="paused", assigned_to=aid, team="backend")
|
||||||
task_svc = AsyncMock()
|
task_svc = AsyncMock()
|
||||||
task_svc.get.return_value = t
|
task_svc.get.return_value = t
|
||||||
task_svc.agent_for.return_value = MagicMock(
|
task_svc.agent_for.return_value = MagicMock(
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ async def test_i_will_plan_calls_ensure_work_session() -> None:
|
|||||||
commits=[],
|
commits=[],
|
||||||
pr_number=None,
|
pr_number=None,
|
||||||
quick_context=None,
|
quick_context=None,
|
||||||
team="main_pm",
|
team="backend",
|
||||||
task_type="planning",
|
task_type="planning",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
@@ -199,7 +199,7 @@ async def test_i_will_plan_calls_ensure_work_session() -> None:
|
|||||||
commits=[],
|
commits=[],
|
||||||
pr_number=None,
|
pr_number=None,
|
||||||
quick_context=None,
|
quick_context=None,
|
||||||
team="main_pm",
|
team="backend",
|
||||||
task_type="planning",
|
task_type="planning",
|
||||||
parent_task_id=None,
|
parent_task_id=None,
|
||||||
sequence=0,
|
sequence=0,
|
||||||
|
|||||||
Reference in New Issue
Block a user