mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Addressing submit_pm_review, get_or_create_channel_by_slug, and other discrepancies/painpoints of the agents
This commit is contained in:
@@ -195,11 +195,19 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Submission Tool
|
## Your Submission Tools
|
||||||
|
|
||||||
|
**For documentation tasks (awaiting_documentation status):**
|
||||||
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
||||||
|
|
||||||
After calling this, your job is DONE. PM will complete the task.
|
**For directly-assigned tasks (not from QA workflow):**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
### When to use which:
|
||||||
|
- **`docs_complete`** - Tasks in `awaiting_documentation` status (from dev→QA→docs workflow)
|
||||||
|
- **`submit_pm_review`** - Tasks assigned directly to you (documentation projects, style guides, etc.)
|
||||||
|
|
||||||
|
After calling either, your job is DONE. PM will complete the task.
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
@@ -213,7 +221,8 @@ capabilities:
|
|||||||
tools:
|
tools:
|
||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_docs_complete # NOT roboco_task_complete (that's PM only)
|
- roboco_task_docs_complete # For awaiting_documentation tasks
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
- roboco_journal_entry, roboco_journal_reflect
|
- roboco_journal_entry, roboco_journal_reflect
|
||||||
- roboco_journal_decision, roboco_journal_learning
|
- roboco_journal_decision, roboco_journal_learning
|
||||||
|
|||||||
@@ -307,13 +307,33 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Verdict Tools
|
## Your Verdict Tools (for DEV work you're reviewing)
|
||||||
|
|
||||||
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
||||||
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
||||||
|
|
||||||
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
||||||
|
|
||||||
|
## Directly-Assigned Tasks (not dev review)
|
||||||
|
|
||||||
|
Sometimes you're assigned tasks directly (audit tasks, test suite creation, etc.) that don't follow the dev→QA workflow:
|
||||||
|
|
||||||
|
**Your workflow for directly-assigned tasks:**
|
||||||
|
```
|
||||||
|
SCAN → CLAIM → PLAN → START → EXECUTE → SUBMIT_PM_REVIEW
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tools for directly-assigned work:**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
**When to use this:**
|
||||||
|
- Tasks assigned directly to you (not `awaiting_qa` from a developer)
|
||||||
|
- Audit tasks, investigation tasks, test infrastructure work
|
||||||
|
- Any task where YOU are the implementer, not the reviewer
|
||||||
|
|
||||||
|
**When NOT to use:**
|
||||||
|
- Tasks in `awaiting_qa` status from developer work → use `qa_pass`/`qa_fail` instead
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -329,6 +349,7 @@ tools:
|
|||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_qa_pass, roboco_task_qa_fail
|
- roboco_task_qa_pass, roboco_task_qa_fail
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
|
|
||||||
# Journal (Your Own)
|
# Journal (Your Own)
|
||||||
|
|||||||
@@ -193,11 +193,19 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Submission Tool
|
## Your Submission Tools
|
||||||
|
|
||||||
|
**For documentation tasks (awaiting_documentation status):**
|
||||||
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
||||||
|
|
||||||
After calling this, your job is DONE. PM will complete the task.
|
**For directly-assigned tasks (not from QA workflow):**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
### When to use which:
|
||||||
|
- **`docs_complete`** - Tasks in `awaiting_documentation` status (from dev→QA→docs workflow)
|
||||||
|
- **`submit_pm_review`** - Tasks assigned directly to you (documentation projects, style guides, etc.)
|
||||||
|
|
||||||
|
After calling either, your job is DONE. PM will complete the task.
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
@@ -211,7 +219,8 @@ capabilities:
|
|||||||
tools:
|
tools:
|
||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_docs_complete # NOT roboco_task_complete (that's PM only)
|
- roboco_task_docs_complete # For awaiting_documentation tasks
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
- roboco_journal_entry, roboco_journal_reflect
|
- roboco_journal_entry, roboco_journal_reflect
|
||||||
- roboco_journal_decision, roboco_journal_learning
|
- roboco_journal_decision, roboco_journal_learning
|
||||||
|
|||||||
@@ -197,13 +197,33 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Verdict Tools
|
## Your Verdict Tools (for DEV work you're reviewing)
|
||||||
|
|
||||||
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
||||||
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
||||||
|
|
||||||
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
||||||
|
|
||||||
|
## Directly-Assigned Tasks (not dev review)
|
||||||
|
|
||||||
|
Sometimes you're assigned tasks directly (audit tasks, test suite creation, etc.) that don't follow the dev→QA workflow:
|
||||||
|
|
||||||
|
**Your workflow for directly-assigned tasks:**
|
||||||
|
```
|
||||||
|
SCAN → CLAIM → PLAN → START → EXECUTE → SUBMIT_PM_REVIEW
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tools for directly-assigned work:**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
**When to use this:**
|
||||||
|
- Tasks assigned directly to you (not `awaiting_qa` from a developer)
|
||||||
|
- Audit tasks, investigation tasks, test infrastructure work
|
||||||
|
- Any task where YOU are the implementer, not the reviewer
|
||||||
|
|
||||||
|
**When NOT to use:**
|
||||||
|
- Tasks in `awaiting_qa` status from developer work → use `qa_pass`/`qa_fail` instead
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -219,6 +239,7 @@ tools:
|
|||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_qa_pass, roboco_task_qa_fail
|
- roboco_task_qa_pass, roboco_task_qa_fail
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
# Journal (Your Own)
|
# Journal (Your Own)
|
||||||
- roboco_journal_entry, roboco_journal_reflect
|
- roboco_journal_entry, roboco_journal_reflect
|
||||||
|
|||||||
@@ -193,11 +193,19 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Submission Tool
|
## Your Submission Tools
|
||||||
|
|
||||||
|
**For documentation tasks (awaiting_documentation status):**
|
||||||
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
- `roboco_task_docs_complete(task_id, doc_notes?)` - Docs done, goes to PM for final review
|
||||||
|
|
||||||
After calling this, your job is DONE. PM will complete the task.
|
**For directly-assigned tasks (not from QA workflow):**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
### When to use which:
|
||||||
|
- **`docs_complete`** - Tasks in `awaiting_documentation` status (from dev→QA→docs workflow)
|
||||||
|
- **`submit_pm_review`** - Tasks assigned directly to you (documentation projects, design system docs, etc.)
|
||||||
|
|
||||||
|
After calling either, your job is DONE. PM will complete the task.
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
@@ -211,7 +219,8 @@ capabilities:
|
|||||||
tools:
|
tools:
|
||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_docs_complete # NOT roboco_task_complete (that's PM only)
|
- roboco_task_docs_complete # For awaiting_documentation tasks
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
- roboco_journal_entry, roboco_journal_reflect
|
- roboco_journal_entry, roboco_journal_reflect
|
||||||
- roboco_journal_decision, roboco_journal_learning
|
- roboco_journal_decision, roboco_journal_learning
|
||||||
|
|||||||
@@ -196,13 +196,33 @@ These are for OTHER roles:
|
|||||||
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
- `roboco_session_create_for_tasks()` - PM-only (you don't create sessions)
|
||||||
- `roboco_group_create()` - PM-only (you don't create groups)
|
- `roboco_group_create()` - PM-only (you don't create groups)
|
||||||
|
|
||||||
## Your Verdict Tools
|
## Your Verdict Tools (for DEV work you're reviewing)
|
||||||
|
|
||||||
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
- `roboco_task_qa_pass(task_id, qa_notes)` - Work passes, goes to Documenter
|
||||||
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
- `roboco_task_qa_fail(task_id, qa_notes, issues_list)` - Work fails, returns to Developer
|
||||||
|
|
||||||
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
Pick ONE. After your verdict, scan for next `awaiting_qa` task.
|
||||||
|
|
||||||
|
## Directly-Assigned Tasks (not dev review)
|
||||||
|
|
||||||
|
Sometimes you're assigned tasks directly (audit tasks, design system review, etc.) that don't follow the dev→QA workflow:
|
||||||
|
|
||||||
|
**Your workflow for directly-assigned tasks:**
|
||||||
|
```
|
||||||
|
SCAN → CLAIM → PLAN → START → EXECUTE → SUBMIT_PM_REVIEW
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tools for directly-assigned work:**
|
||||||
|
- `roboco_task_submit_pm_review(task_id, notes?)` - Submit your own work for PM review
|
||||||
|
|
||||||
|
**When to use this:**
|
||||||
|
- Tasks assigned directly to you (not `awaiting_qa` from a developer)
|
||||||
|
- Audit tasks, investigation tasks, accessibility audits
|
||||||
|
- Any task where YOU are the implementer, not the reviewer
|
||||||
|
|
||||||
|
**When NOT to use:**
|
||||||
|
- Tasks in `awaiting_qa` status from developer work → use `qa_pass`/`qa_fail` instead
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -217,6 +237,7 @@ tools:
|
|||||||
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
- roboco_task_scan, roboco_task_get, roboco_task_claim
|
||||||
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
- roboco_task_plan, roboco_task_start, roboco_task_progress
|
||||||
- roboco_task_qa_pass, roboco_task_qa_fail
|
- roboco_task_qa_pass, roboco_task_qa_fail
|
||||||
|
- roboco_task_submit_pm_review # For directly-assigned tasks
|
||||||
- roboco_task_escalate, roboco_agent_idle
|
- roboco_task_escalate, roboco_agent_idle
|
||||||
# Journal (Your Own)
|
# Journal (Your Own)
|
||||||
- roboco_journal_entry, roboco_journal_reflect
|
- roboco_journal_entry, roboco_journal_reflect
|
||||||
|
|||||||
@@ -61,12 +61,13 @@ async def create_group(
|
|||||||
|
|
||||||
service = get_messaging_service(db)
|
service = get_messaging_service(db)
|
||||||
|
|
||||||
# Get channel by slug
|
# Get channel by slug (auto-creates from config if needed)
|
||||||
channel = await service.get_channel_by_slug(data.channel_slug)
|
channel = await service.get_or_create_channel_by_slug(data.channel_slug)
|
||||||
if not channel:
|
if not channel:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Channel not found: {data.channel_slug}",
|
detail=f"Channel not found: {data.channel_slug}. "
|
||||||
|
"This channel is not defined in system configuration.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create group via service
|
# Create group via service
|
||||||
|
|||||||
@@ -920,6 +920,45 @@ async def docs_complete(
|
|||||||
return task_to_response(task)
|
return task_to_response(task)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{task_id}/submit-pm-review", response_model=TaskResponse)
|
||||||
|
async def submit_for_pm_review(
|
||||||
|
task_id: UUID,
|
||||||
|
db: DbSession,
|
||||||
|
agent: CurrentAgentContext,
|
||||||
|
data: QANotes | None = None,
|
||||||
|
) -> TaskResponse:
|
||||||
|
"""Submit a task directly for PM review.
|
||||||
|
|
||||||
|
Use this for tasks that don't follow the standard dev→QA→docs workflow,
|
||||||
|
such as PM validation tasks, QA audit tasks, or other directly-assigned work.
|
||||||
|
|
||||||
|
Only the assigned agent can submit their task for PM review.
|
||||||
|
"""
|
||||||
|
service = get_task_service(db)
|
||||||
|
task = await service.get(task_id)
|
||||||
|
if not task:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND, detail="Task not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only assigned agent can submit for PM review
|
||||||
|
if task.assigned_to != agent.agent_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Only the assigned agent can submit for PM review",
|
||||||
|
)
|
||||||
|
|
||||||
|
notes = data.notes if data else None
|
||||||
|
task = await service.submit_for_pm_review(task_id, notes)
|
||||||
|
if not task:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Cannot submit for PM review - task not in progress",
|
||||||
|
)
|
||||||
|
await db.commit()
|
||||||
|
return task_to_response(task)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{task_id}/complete", response_model=TaskResponse)
|
@router.post("/{task_id}/complete", response_model=TaskResponse)
|
||||||
async def complete_task(
|
async def complete_task(
|
||||||
task_id: UUID,
|
task_id: UUID,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ from roboco.mcp.tasks.handlers import (
|
|||||||
handle_session_get_for_task,
|
handle_session_get_for_task,
|
||||||
handle_session_link_task,
|
handle_session_link_task,
|
||||||
handle_session_unlink_task,
|
handle_session_unlink_task,
|
||||||
|
handle_submit_pm_review,
|
||||||
handle_task_activate,
|
handle_task_activate,
|
||||||
handle_task_assign,
|
handle_task_assign,
|
||||||
handle_task_block,
|
handle_task_block,
|
||||||
@@ -250,6 +251,35 @@ def _register_core_tools(mcp: FastMCP, client: ApiClient, agent_id: str) -> None
|
|||||||
)
|
)
|
||||||
return await handle_task_escalate(client, input_data, agent_id)
|
return await handle_task_escalate(client, input_data, agent_id)
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
async def roboco_task_submit_pm_review(
|
||||||
|
task_id: str, notes: str | None = None
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Submit task directly for PM review.
|
||||||
|
|
||||||
|
Use this for tasks that don't follow the standard dev→QA→docs workflow:
|
||||||
|
- PM validation tasks assigned directly to you
|
||||||
|
- QA audit tasks (not reviewing dev work)
|
||||||
|
- Any directly-assigned non-dev work
|
||||||
|
|
||||||
|
The task will transition to 'awaiting_pm_review' and the PM
|
||||||
|
will verify and complete it.
|
||||||
|
|
||||||
|
WHEN TO USE:
|
||||||
|
- You completed a task assigned directly to you (not dev work)
|
||||||
|
- The task doesn't need QA/docs review
|
||||||
|
- You want the PM to verify and close it
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_id: The task UUID to submit
|
||||||
|
notes: Optional completion notes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Task awaiting PM review
|
||||||
|
"""
|
||||||
|
return await handle_submit_pm_review(client, task_id, agent_id, notes)
|
||||||
|
|
||||||
|
|
||||||
def _register_blocking_tools(mcp: FastMCP, client: ApiClient, agent_id: str) -> None:
|
def _register_blocking_tools(mcp: FastMCP, client: ApiClient, agent_id: str) -> None:
|
||||||
"""Register blocking/unblocking/pause tools."""
|
"""Register blocking/unblocking/pause tools."""
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from roboco.mcp.tasks.handlers.claim import handle_task_claim
|
|||||||
from roboco.mcp.tasks.handlers.lifecycle import (
|
from roboco.mcp.tasks.handlers.lifecycle import (
|
||||||
handle_agent_idle,
|
handle_agent_idle,
|
||||||
handle_docs_complete,
|
handle_docs_complete,
|
||||||
|
handle_submit_pm_review,
|
||||||
handle_task_cancel,
|
handle_task_cancel,
|
||||||
handle_task_complete,
|
handle_task_complete,
|
||||||
)
|
)
|
||||||
@@ -50,6 +51,7 @@ __all__ = [
|
|||||||
"handle_session_get_for_task",
|
"handle_session_get_for_task",
|
||||||
"handle_session_link_task",
|
"handle_session_link_task",
|
||||||
"handle_session_unlink_task",
|
"handle_session_unlink_task",
|
||||||
|
"handle_submit_pm_review",
|
||||||
"handle_task_activate",
|
"handle_task_activate",
|
||||||
"handle_task_assign",
|
"handle_task_assign",
|
||||||
"handle_task_block",
|
"handle_task_block",
|
||||||
|
|||||||
@@ -129,6 +129,49 @@ async def handle_task_complete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_submit_pm_review(
|
||||||
|
client: ApiClient, task_id: str, agent_id: str, notes: str | None = None
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Handle direct submission for PM review.
|
||||||
|
|
||||||
|
For tasks that don't follow the standard dev→QA→docs workflow,
|
||||||
|
such as PM validation tasks, QA audit tasks, or directly-assigned work.
|
||||||
|
"""
|
||||||
|
task, error = await fetch_task_or_error(client, task_id)
|
||||||
|
if error:
|
||||||
|
return error
|
||||||
|
assert task is not None
|
||||||
|
|
||||||
|
# Must be in_progress to submit for PM review
|
||||||
|
current_status = task.get("status")
|
||||||
|
if current_status != "in_progress":
|
||||||
|
return format_error_response(
|
||||||
|
"INVALID_STATE",
|
||||||
|
f"Cannot submit for PM review - task is '{current_status}', "
|
||||||
|
"expected 'in_progress'.",
|
||||||
|
{"current_status": current_status},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Submit to API
|
||||||
|
payload = {}
|
||||||
|
if notes:
|
||||||
|
payload["notes"] = notes
|
||||||
|
|
||||||
|
resp = await client.post(f"/tasks/{task_id}/submit-pm-review", json=payload)
|
||||||
|
if not resp.ok:
|
||||||
|
return format_error_response(
|
||||||
|
"SUBMIT_FAILED",
|
||||||
|
"Failed to submit for PM review",
|
||||||
|
{"status_code": resp.status_code, "api_error": resp.text},
|
||||||
|
)
|
||||||
|
|
||||||
|
guidance = (
|
||||||
|
"Task submitted for PM review. The PM will verify and complete the task.\n"
|
||||||
|
"Call roboco_task_scan to find more work, or roboco_agent_idle if none."
|
||||||
|
)
|
||||||
|
return format_task_response(resp.json(), "AWAITING_PM_REVIEW", guidance)
|
||||||
|
|
||||||
|
|
||||||
def _validate_task_cancellable(task: dict[str, Any]) -> dict[str, Any] | None:
|
def _validate_task_cancellable(task: dict[str, Any]) -> dict[str, Any] | None:
|
||||||
"""Validate task can be cancelled. Returns error or None."""
|
"""Validate task can be cancelled. Returns error or None."""
|
||||||
current_status = task.get("status")
|
current_status = task.get("status")
|
||||||
|
|||||||
@@ -130,6 +130,54 @@ class MessagingService(BaseService):
|
|||||||
)
|
)
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
|
async def get_or_create_channel_by_slug(self, slug: str) -> ChannelTable | None:
|
||||||
|
"""Get a channel by slug, auto-creating from config if needed.
|
||||||
|
|
||||||
|
If the channel doesn't exist in the database but is defined in
|
||||||
|
DEFAULT_CHANNELS, it will be automatically created.
|
||||||
|
|
||||||
|
This allows the system to work without requiring explicit database
|
||||||
|
seeding while still validating that only configured channels are used.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
slug: Channel slug to look up
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Channel if found or created, None if not a valid channel
|
||||||
|
"""
|
||||||
|
# First try database
|
||||||
|
channel = await self.get_channel_by_slug(slug)
|
||||||
|
if channel:
|
||||||
|
return channel
|
||||||
|
|
||||||
|
# Not in DB - check if it's a valid channel from config
|
||||||
|
from roboco.models.base import ChannelType
|
||||||
|
from roboco.seeds import DEFAULT_CHANNELS
|
||||||
|
|
||||||
|
channel_data = next(
|
||||||
|
(c for c in DEFAULT_CHANNELS if c["slug"] == slug),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if not channel_data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Auto-create from config
|
||||||
|
channel = ChannelTable(
|
||||||
|
name=channel_data["name"],
|
||||||
|
slug=channel_data["slug"],
|
||||||
|
type=ChannelType(channel_data["channel_type"]),
|
||||||
|
description=channel_data.get("description", ""),
|
||||||
|
)
|
||||||
|
self.session.add(channel)
|
||||||
|
await self.session.flush()
|
||||||
|
|
||||||
|
self.log.info(
|
||||||
|
"Channel auto-created from config",
|
||||||
|
slug=slug,
|
||||||
|
type=channel_data["channel_type"],
|
||||||
|
)
|
||||||
|
return channel
|
||||||
|
|
||||||
async def list_channels_for_agent(
|
async def list_channels_for_agent(
|
||||||
self,
|
self,
|
||||||
agent_id: UUID,
|
agent_id: UUID,
|
||||||
|
|||||||
@@ -788,6 +788,58 @@ class TaskService(BaseService):
|
|||||||
)
|
)
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
async def submit_for_pm_review(
|
||||||
|
self,
|
||||||
|
task_id: UUID,
|
||||||
|
notes: str | None = None,
|
||||||
|
) -> TaskTable | None:
|
||||||
|
"""
|
||||||
|
Submit a task directly for PM review (any assigned agent).
|
||||||
|
|
||||||
|
Use this for tasks that don't follow the standard dev→QA→docs workflow,
|
||||||
|
such as PM validation tasks, QA audit tasks, or other directly-assigned work.
|
||||||
|
|
||||||
|
Transitions task from IN_PROGRESS to AWAITING_PM_REVIEW.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_id: The task to submit
|
||||||
|
notes: Optional completion notes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The updated task or None if not allowed
|
||||||
|
"""
|
||||||
|
task = await self.get(task_id)
|
||||||
|
if not task:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Only allow submission from in_progress status
|
||||||
|
if task.status != TaskStatus.IN_PROGRESS:
|
||||||
|
self.log.warning(
|
||||||
|
"Cannot submit for PM review - task not in progress",
|
||||||
|
task_id=str(task_id),
|
||||||
|
current_status=task.status.value,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Store notes in quick_context
|
||||||
|
if notes:
|
||||||
|
existing_context = task.quick_context or ""
|
||||||
|
note_entry = f"completion_notes:{notes}"
|
||||||
|
task.quick_context = (
|
||||||
|
f"{existing_context}\n{note_entry}".strip()
|
||||||
|
if existing_context
|
||||||
|
else note_entry
|
||||||
|
)
|
||||||
|
|
||||||
|
task.status = TaskStatus.AWAITING_PM_REVIEW
|
||||||
|
await self.session.flush()
|
||||||
|
|
||||||
|
self.log.info(
|
||||||
|
"Task submitted for PM review",
|
||||||
|
task_id=str(task_id),
|
||||||
|
)
|
||||||
|
return task
|
||||||
|
|
||||||
async def complete(
|
async def complete(
|
||||||
self,
|
self,
|
||||||
task_id: UUID,
|
task_id: UUID,
|
||||||
|
|||||||
Reference in New Issue
Block a user