Files
roboco/roboco/api/schemas/v2/do.py
T
Renn F bcc748c8a3 fix: restore pre-gateway structured verb surfaces (5 fixes)
Smoke run 2026-05-11 showed five regressions stemming from the gateway
consolidating multiple typed endpoints into thin verbs with collapsed
signatures. The choreography is fine; the verb signatures lost the
structured shape that pre-gateway forced agents to fill. Each fix
restores a structured surface so the LLM's tool schema again carries
the constraints that prevent the observed bugs.

A) do_server: list valid channel slugs in say()/dm() docstrings.
   Stops invented channels (`backend`, `backend-dev`) — the LLM now
   sees the closed set in the tool schema.

B) choreographer: add _delegate_sibling_dedup_guard.
   Rejects a delegate that would create a non-terminal sibling with
   the same assigned_to + task_type under the same parent — the dupe
   shape observed on smoke (Main PM creating two planning tasks for
   be-pm; Cell PM creating two code tasks for be-dev-1).

C) choreographer: extend _validate_assignee_task_type to all roles.
   Devs may only get code|documentation|research (not planning/design/
   administrative). QA gets code only. Documenters get documentation
   only. Catches the misroute observed on smoke (Cell PM gave
   be-dev-2 a 'research' coordination task that should have stayed
   with the PM).

D) i_will_plan: thread approach / technical_considerations / risks /
   open_questions from MCP through to TaskService.set_plan as a
   TaskPlan-shaped dict. Empty default keeps back-compat. Panel's
   Plan tab now renders Approach / Sub-Tasks / Technical
   Considerations / Risks / Open Questions instead of an empty pane.

E) note(): scope-specific structured fields restored.
   For 'decision' scope: context, options[], chosen, rationale,
   consequences. For 'reflect' scope: what_done, what_learned,
   what_struggled, next_steps. Rendered as markdown sections into
   the journal entry content so the Decisions and Reflections views
   show named blocks instead of a one-line phrase. Pre-gateway parity.

Files changed:
- roboco/mcp/do_server.py (A, E)
- roboco/mcp/flow_server.py (D)
- roboco/services/gateway/choreographer/_impl.py (B, C, D)
- roboco/services/gateway/content_actions.py (E)
- roboco/api/schemas/v2/flow.py (D)
- roboco/api/schemas/v2/do.py (E)
- roboco/api/routes/v2/flow_main_pm.py (D)
- roboco/api/routes/v2/flow_cell_pm.py (D)
- roboco/api/routes/v2/do.py (E)

Quality: ruff + mypy clean. 89 unit tests pass on the touched surfaces.
2026-05-11 03:45:09 +02:00

68 lines
1.8 KiB
Python

"""Request schemas for /api/v2/do/* content tools."""
from uuid import UUID
from pydantic import BaseModel, Field
class CommitRequest(BaseModel):
message: str = Field(..., min_length=1)
files: list[str] | None = None
class NoteRequest(BaseModel):
"""Journal entry. ``text`` is always the short summary line.
Scope-specific fields are optional but pre-gateway parity expected
them filled for `decision` and `reflect`:
- decision: ``context``, ``options``, ``chosen``, ``rationale``,
``consequences``
- reflect: ``what_done``, ``what_learned``, ``what_struggled``,
``next_steps``
When provided, these are formatted into the journal entry's content
as structured markdown — the panel UI's decision/reflect views show
them as named sections instead of a one-line phrase.
"""
text: str = Field(..., min_length=1)
scope: str = "note"
task_id: UUID | None = None
title: str | None = None
# decision scope
context: str | None = None
options: list[str] | None = None
chosen: str | None = None
rationale: str | None = None
consequences: str | None = None
# reflect scope
what_done: str | None = None
what_learned: str | None = None
what_struggled: str | None = None
next_steps: str | None = None
class SayRequest(BaseModel):
channel: str
text: str = Field(..., min_length=1)
task_id: UUID | None = None
class DmRequest(BaseModel):
recipient: str # agent slug
text: str = Field(..., min_length=1)
task_id: UUID | None = None
skill: str | None = None
class NotifyRequest(BaseModel):
target: str # agent slug
text: str = Field(..., min_length=1)
priority: str = "normal" # normal | high | urgent
task_id: UUID | None = None
class EvidenceRequest(BaseModel):
task_id: UUID