Files
roboco/roboco/api/schemas/v2/flow.py
T
Renn F 01ff44b83f 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.
2026-05-08 07:45:18 +02:00

114 lines
2.4 KiB
Python

"""Request schemas for /api/v2/flow/* intent verbs."""
from uuid import UUID
from pydantic import BaseModel, Field
class GiveMeWorkRequest(BaseModel):
"""Empty request body — agent_id comes from header."""
class IWillWorkOnRequest(BaseModel):
task_id: UUID
plan: str | None = None
class SubmitForQaRequest(BaseModel):
task_id: UUID
class IAmDoneRequest(BaseModel):
task_id: UUID
notes: str = ""
class IAmBlockedRequest(BaseModel):
task_id: UUID
reason: str = Field(..., min_length=1)
class UnclaimRequest(BaseModel):
task_id: UUID
class ResumeRequest(BaseModel):
task_id: UUID
class IAmIdleRequest(BaseModel):
"""Empty request body."""
class ClaimReviewRequest(BaseModel):
task_id: UUID
class PassReviewRequest(BaseModel):
task_id: UUID
notes: str = Field(..., min_length=1)
class FailReviewRequest(BaseModel):
task_id: UUID
issues: list[str] = Field(..., min_length=1)
class ClaimDocTaskRequest(BaseModel):
task_id: UUID
class IDocumentedRequest(BaseModel):
task_id: UUID
notes: str = Field(..., min_length=1)
files: list[str] = Field(..., min_length=1)
class TriageRequest(BaseModel):
"""Empty request body."""
class UnblockRequest(BaseModel):
task_id: UUID
restore: bool = True
class CompleteRequest(BaseModel):
task_id: UUID
notes: str = Field(..., min_length=1)
class EscalateUpRequest(BaseModel):
task_id: UUID
reason: str = Field(..., min_length=1)
class EscalateToCeoRequest(BaseModel):
task_id: UUID
reason: str = Field(..., min_length=1)
class IWillPlanRequest(BaseModel):
task_id: UUID
plan: str = Field(..., min_length=1)
class DelegateRequest(BaseModel):
parent_task_id: UUID
title: str = Field(..., min_length=1)
description: str = Field(..., min_length=1)
assigned_to: str = Field(..., min_length=1)
team: str = Field(..., min_length=1)
# task_type is REQUIRED. The 2026-05-08 trace showed agents omitting
# it and the old default of 'code' deadlocking the lifecycle. Force
# callers to declare intent: code | documentation | research |
# planning | design | administrative.
task_type: str = Field(..., min_length=1)
acceptance_criteria: list[str] | None = None
estimated_complexity: str = "medium"
class SubmitUpRequest(BaseModel):
task_id: UUID
notes: str = Field(..., min_length=1)