mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(tasks): CEO-reject of a coordination root routes to pending, not needs_revision
A CEO reject sent the root to NEEDS_REVISION, but that status is developer-claim-only — a coordination/integration root (project_id NULL + product_id set) has no developer, so the Main PM that owns it could not re-claim, escalated to the Board, and the task deadlocked in blocked with no forward state. ceo_reject now detects the coordination root and routes it to PENDING (the Main PM's claim source) with the claim cleared, via the audited admin_set_status override (awaiting_ceo_approval->pending has no in-band transition). Non-coordination tasks keep the normal needs_revision->developer path. Test pending (test DB was down mid-incident).
This commit is contained in:
+23
-7
@@ -3970,8 +3970,14 @@ class TaskService(BaseService):
|
||||
else rejection_entry
|
||||
)
|
||||
|
||||
# Validate transition with CEO role requirement
|
||||
self._validate_and_set_status(task, TaskStatus.NEEDS_REVISION, "ceo")
|
||||
# A coordination/integration root (no repo of its own, carries a
|
||||
# product) has no developer to revise it — NEEDS_REVISION is
|
||||
# developer-claim-only, so it would deadlock the Main PM that owns the
|
||||
# root. Such a root is routed to PENDING below instead; every other task
|
||||
# takes the normal NEEDS_REVISION path back toward its developer.
|
||||
is_coordination_root = task.project_id is None and task.product_id is not None
|
||||
if not is_coordination_root:
|
||||
self._validate_and_set_status(task, TaskStatus.NEEDS_REVISION, "ceo")
|
||||
|
||||
# Surface the CEO's required changes through the task journal — the one
|
||||
# channel a downstream worker actually reads (evidence.journal_highlights
|
||||
@@ -3986,16 +3992,26 @@ class TaskService(BaseService):
|
||||
|
||||
# Route the rejected task to whoever should drive the rework.
|
||||
reassigned_to: str | None
|
||||
if task.project_id is None and task.product_id is not None:
|
||||
# Coordination/integration root: the Main PM delegates the rework — a
|
||||
# board/dev role cannot drive a coordination task.
|
||||
if is_coordination_root:
|
||||
# Coordination/integration root: the Main PM re-plans and
|
||||
# re-delegates the rework — a board/dev role cannot drive it. Land it
|
||||
# in PENDING (the Main PM's claim source), claim cleared, so it
|
||||
# re-enters plan→delegate. awaiting_ceo_approval→pending has no
|
||||
# in-band transition, so use the audited privileged override.
|
||||
main_pm_id = UUID(AGENT_UUIDS["main-pm"])
|
||||
task.team = Team.MAIN_PM
|
||||
task.assigned_to = cast("Any", main_pm_id)
|
||||
task.claimed_by = cast("Any", main_pm_id)
|
||||
task.claimed_by = None
|
||||
reassigned_to = str(main_pm_id)
|
||||
await self.session.flush()
|
||||
await self.admin_set_status(
|
||||
task_id,
|
||||
TaskStatus.PENDING,
|
||||
actor_role="ceo",
|
||||
actor_id=AGENT_UUIDS["ceo"],
|
||||
)
|
||||
self.log.info(
|
||||
"Coordination task rejected by CEO - routed to Main PM",
|
||||
"Coordination task rejected by CEO - routed to Main PM (pending)",
|
||||
task_id=str(task_id),
|
||||
)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user