mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Changes Made
roboco/agents/base.py - Added proper workflow methods: - _qa_pass(task_id, qa_notes) - Calls /pass-qa endpoint - _qa_fail(task_id, qa_notes, issues) - Calls /fail-qa endpoint - _docs_complete(task_id, doc_notes?) - Calls /docs-complete endpoint - Marked old _mark_needs_revision, _mark_awaiting_documentation, _mark_awaiting_pm_review as DEPRECATED roboco/agents/qa.py - Fixed verdict phase: - Now uses _qa_pass() instead of _mark_awaiting_documentation() - Now uses _qa_fail() instead of _mark_needs_revision() - Properly passes QA notes and issue lists roboco/agents/documenter.py - Fixed submit phase: - Now uses _docs_complete() instead of _mark_awaiting_pm_review() - Properly passes documentation summary as notes
This commit is contained in:
@@ -451,7 +451,8 @@ tools:
|
||||
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
- roboco_report_blocker
|
||||
|
||||
# Claude Code Built-in
|
||||
@@ -487,4 +488,8 @@ permissions:
|
||||
|
||||
journals_read:
|
||||
- backend cell members (be-dev-1, be-dev-2, be-qa, be-doc, be-pm)
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks assigned to your team
|
||||
self_review: blocked # You cannot QA or document your own work
|
||||
```
|
||||
|
||||
@@ -230,7 +230,8 @@ tools:
|
||||
# Team Journals (Read Cell Members)
|
||||
- roboco_journal_read_team, roboco_journal_scope
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
@@ -362,7 +362,8 @@ tools:
|
||||
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
- roboco_report_blocker
|
||||
|
||||
# Testing Tools
|
||||
@@ -395,4 +396,8 @@ permissions:
|
||||
- qa_pass_tasks
|
||||
- qa_fail_tasks
|
||||
- escalate_tasks
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks for your team
|
||||
self_review: blocked # You cannot QA tasks where you were the original developer
|
||||
```
|
||||
|
||||
@@ -361,7 +361,8 @@ tools:
|
||||
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
- roboco_report_blocker
|
||||
|
||||
# Claude Code Built-in
|
||||
@@ -397,4 +398,8 @@ permissions:
|
||||
|
||||
journals_read:
|
||||
- frontend cell members (fe-dev-1, fe-dev-2, fe-qa, fe-doc, fe-pm)
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks assigned to your team
|
||||
self_review: blocked # You cannot QA or document your own work
|
||||
```
|
||||
|
||||
@@ -228,7 +228,8 @@ tools:
|
||||
# Team Journals (Read Cell Members)
|
||||
- roboco_journal_read_team, roboco_journal_scope
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
@@ -248,7 +248,8 @@ tools:
|
||||
- roboco_journal_read_team, roboco_journal_scope
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
```
|
||||
|
||||
## Permissions
|
||||
@@ -276,4 +277,8 @@ permissions:
|
||||
- qa_pass_tasks
|
||||
- qa_fail_tasks
|
||||
- escalate_tasks
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks for your team
|
||||
self_review: blocked # You cannot QA tasks where you were the original developer
|
||||
```
|
||||
|
||||
@@ -331,7 +331,8 @@ tools:
|
||||
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
- roboco_report_blocker
|
||||
|
||||
# Design Tools
|
||||
@@ -364,4 +365,8 @@ permissions:
|
||||
|
||||
journals_read:
|
||||
- ux_ui cell members (ux-dev, ux-qa, ux-doc, ux-pm)
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks assigned to your team
|
||||
self_review: blocked # You cannot QA or document your own work
|
||||
```
|
||||
|
||||
@@ -228,7 +228,8 @@ tools:
|
||||
# Team Journals (Read Cell Members)
|
||||
- roboco_journal_read_team, roboco_journal_scope
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
@@ -246,7 +246,8 @@ tools:
|
||||
- roboco_journal_read_team, roboco_journal_scope
|
||||
# Communication
|
||||
- roboco_channel_list, roboco_channel_history
|
||||
- roboco_message_send, roboco_ask_question
|
||||
- roboco_message_send, roboco_message_get, roboco_ask_question
|
||||
- roboco_session_history_for_task # Get discussion history for your task
|
||||
```
|
||||
|
||||
## Permissions
|
||||
@@ -274,4 +275,8 @@ permissions:
|
||||
- qa_pass_tasks
|
||||
- qa_fail_tasks
|
||||
- escalate_tasks
|
||||
|
||||
# Enforced Constraints (code enforces these rules)
|
||||
task_visibility: team_only # You only see tasks for your team
|
||||
self_review: blocked # You cannot QA tasks where you were the original developer
|
||||
```
|
||||
|
||||
+52
-3
@@ -734,17 +734,66 @@ class Agent(ABC):
|
||||
await self._update_task_status(task_id, TaskStatus.AWAITING_QA)
|
||||
|
||||
async def _mark_needs_revision(self, task_id: UUID) -> None:
|
||||
"""Mark task as needing revision (QA failed)."""
|
||||
"""Mark task as needing revision (QA failed).
|
||||
|
||||
DEPRECATED: Use _qa_fail() instead which calls the proper endpoint.
|
||||
"""
|
||||
await self._update_task_status(task_id, TaskStatus.NEEDS_REVISION)
|
||||
|
||||
async def _mark_awaiting_documentation(self, task_id: UUID) -> None:
|
||||
"""Mark task as awaiting documentation."""
|
||||
"""Mark task as awaiting documentation.
|
||||
|
||||
DEPRECATED: Use _qa_pass() instead which calls the proper endpoint.
|
||||
"""
|
||||
await self._update_task_status(task_id, TaskStatus.AWAITING_DOCUMENTATION)
|
||||
|
||||
async def _qa_pass(self, task_id: UUID, qa_notes: str) -> None:
|
||||
"""Pass QA review - calls proper /pass-qa endpoint.
|
||||
|
||||
This handles full workflow: saves notes, updates status, creates audit.
|
||||
"""
|
||||
await self._api_call(
|
||||
"POST",
|
||||
f"/tasks/{task_id}/pass-qa",
|
||||
json={"notes": qa_notes},
|
||||
)
|
||||
self.log.info("QA passed", task_id=str(task_id))
|
||||
|
||||
async def _qa_fail(
|
||||
self, task_id: UUID, qa_notes: str, issues: list[str]
|
||||
) -> None:
|
||||
"""Fail QA review - calls proper /fail-qa endpoint.
|
||||
|
||||
This handles full workflow: saves notes, updates status, reassigns to dev.
|
||||
"""
|
||||
full_notes = f"{qa_notes}\n\nIssues:\n" + "\n".join(f"- {i}" for i in issues)
|
||||
await self._api_call(
|
||||
"POST",
|
||||
f"/tasks/{task_id}/fail-qa",
|
||||
json={"notes": full_notes},
|
||||
)
|
||||
self.log.info("QA failed", task_id=str(task_id), issues_count=len(issues))
|
||||
|
||||
async def _mark_awaiting_pm_review(self, task_id: UUID) -> None:
|
||||
"""Mark task as awaiting PM review."""
|
||||
"""Mark task as awaiting PM review.
|
||||
|
||||
DEPRECATED: Use _docs_complete() instead which calls the proper endpoint.
|
||||
"""
|
||||
await self._update_task_status(task_id, TaskStatus.AWAITING_PM_REVIEW)
|
||||
|
||||
async def _docs_complete(self, task_id: UUID, doc_notes: str | None = None) -> None:
|
||||
"""Mark documentation as complete - calls proper /docs-complete endpoint.
|
||||
|
||||
This handles full workflow: saves notes, updates status.
|
||||
"""
|
||||
payload = {"notes": doc_notes} if doc_notes else {}
|
||||
await self._api_call(
|
||||
"POST",
|
||||
f"/tasks/{task_id}/docs-complete",
|
||||
json=payload,
|
||||
)
|
||||
self.log.info("Documentation complete", task_id=str(task_id))
|
||||
|
||||
async def _mark_completed(self, task_id: UUID) -> None:
|
||||
"""Mark task as completed."""
|
||||
await self._update_task_status(task_id, TaskStatus.COMPLETED)
|
||||
|
||||
@@ -312,12 +312,28 @@ Add unit tests,tests/test_main.py,small
|
||||
{"description": response, "files": [], "complexity": "medium"}
|
||||
]
|
||||
|
||||
# Analyze risks based on subtask complexity
|
||||
risks = []
|
||||
complex_subtasks = [
|
||||
s for s in ctx.subtasks if s.get("complexity") == "high"
|
||||
]
|
||||
if complex_subtasks:
|
||||
risks.append(
|
||||
f"{len(complex_subtasks)} high-complexity subtasks may need extra time"
|
||||
)
|
||||
max_subtasks_per_phase = 5
|
||||
if len(ctx.subtasks) > max_subtasks_per_phase:
|
||||
risks.append("Large number of subtasks - may need to split into phases")
|
||||
|
||||
# Estimate sessions based on subtask count and complexity
|
||||
estimated_sessions = max(1, len(ctx.subtasks) // 3 + len(complex_subtasks))
|
||||
|
||||
# Save plan to task via API (REQUIRED before start can be called)
|
||||
plan_data = {
|
||||
"approach": f"Implement {ctx.title}",
|
||||
"steps": [s.get("description", str(s)) for s in ctx.subtasks],
|
||||
"risks": [],
|
||||
"estimated_sessions": 1,
|
||||
"risks": risks,
|
||||
"estimated_sessions": estimated_sessions,
|
||||
}
|
||||
await self._api_call("PATCH", f"/tasks/{ctx.task_id}", json={"plan": plan_data})
|
||||
|
||||
|
||||
@@ -437,13 +437,14 @@ good,complete,clear,helpful,None
|
||||
"Failed to publish", path=doc_spec.path, error=str(e)
|
||||
)
|
||||
|
||||
# Mark docs complete - task goes to PM for final review
|
||||
await self._mark_awaiting_pm_review(ctx.task_id)
|
||||
# Use proper docs-complete endpoint (handles notes, status)
|
||||
doc_summary = f"Published: {', '.join(ctx.written_docs)}"
|
||||
await self._docs_complete(ctx.task_id, doc_summary)
|
||||
|
||||
await self.send_message(
|
||||
ctx.session_id,
|
||||
f"TASK-{str(ctx.task_id)[:8]} documentation complete, awaiting PM review\n"
|
||||
f"Published: {', '.join(ctx.written_docs)}",
|
||||
f"{doc_summary}",
|
||||
message_type="action",
|
||||
task_id=ctx.task_id,
|
||||
)
|
||||
|
||||
+12
-4
@@ -381,8 +381,13 @@ PASS,All criteria verified successfully,No issues found
|
||||
task_id=ctx.task_id,
|
||||
)
|
||||
|
||||
# Update task status
|
||||
await self._mark_needs_revision(ctx.task_id)
|
||||
# Use proper QA fail endpoint (handles notes, status, reassignment)
|
||||
issue_list = [t.actual or t.name for t in failed_tests]
|
||||
await self._qa_fail(
|
||||
ctx.task_id,
|
||||
"Found issues that need fixing before approval.",
|
||||
issue_list,
|
||||
)
|
||||
|
||||
else:
|
||||
ctx.verdict = TestResult.PASS
|
||||
@@ -396,8 +401,11 @@ PASS,All criteria verified successfully,No issues found
|
||||
task_id=ctx.task_id,
|
||||
)
|
||||
|
||||
# Update task status
|
||||
await self._mark_awaiting_documentation(ctx.task_id)
|
||||
# Use proper QA pass endpoint (handles notes, status)
|
||||
await self._qa_pass(
|
||||
ctx.task_id,
|
||||
f"All {len(ctx.test_cases)} tests passed. Ready for documentation.",
|
||||
)
|
||||
|
||||
ctx.notes.append(
|
||||
f"[{datetime.now(UTC).isoformat()}] Verdict: {ctx.verdict.value.upper()}"
|
||||
|
||||
+20
-12
@@ -3,6 +3,26 @@ Agent Configuration
|
||||
|
||||
Single source of truth for agent roles, teams, and cell memberships.
|
||||
All enforcement modules and MCP servers should import from here.
|
||||
|
||||
PERMISSION ARCHITECTURE
|
||||
-----------------------
|
||||
The system uses TWO complementary permission layers:
|
||||
|
||||
1. MCP Layer (this module - agents_config.py):
|
||||
- Controls which MCP tools agents can access
|
||||
- Uses role-based helper functions: can_create_tasks(), can_cancel_tasks(), etc.
|
||||
- Works with agent slugs/UUIDs directly
|
||||
- Determines tool visibility at MCP server registration time
|
||||
|
||||
2. API Layer (roboco/services/permissions.py):
|
||||
- Controls fine-grained API endpoint access
|
||||
- Uses TaskAction enum and PermissionService class
|
||||
- Works with AgentContext and Team enums
|
||||
- Validates at request time with team-scoped checks
|
||||
|
||||
Both layers derive from the same source data (role definitions here) but serve
|
||||
different purposes. MCP is coarse-grained (tool-level), API is fine-grained
|
||||
(action + team context).
|
||||
"""
|
||||
|
||||
from typing import Final
|
||||
@@ -98,12 +118,6 @@ PM_ROLES: Final[set[str]] = {
|
||||
"ceo",
|
||||
}
|
||||
|
||||
# Developer-only tools (PMs, QA, Documenters cannot use these)
|
||||
DEVELOPER_ONLY_TOOLS: Final[frozenset[str]] = frozenset({
|
||||
"roboco_task_submit_verification",
|
||||
"roboco_task_submit_qa",
|
||||
})
|
||||
|
||||
# Escalation chain - who each agent escalates to
|
||||
ESCALATION_CHAIN: Final[dict[str, str]] = {
|
||||
# Developers → Cell PM
|
||||
@@ -224,12 +238,6 @@ def can_cancel_tasks(agent_id: str) -> bool:
|
||||
return role in _CANCEL_ROLES
|
||||
|
||||
|
||||
def can_submit_for_qa(agent_id: str) -> bool:
|
||||
"""Check if agent can submit work for QA (developers only)."""
|
||||
role = get_agent_role(agent_id)
|
||||
return role == "developer"
|
||||
|
||||
|
||||
def get_escalation_target(agent_id: str) -> str | None:
|
||||
"""Get the escalation target for an agent."""
|
||||
return ESCALATION_CHAIN.get(agent_id)
|
||||
|
||||
@@ -131,7 +131,6 @@ async def handle_task_pause(
|
||||
await client.post(
|
||||
f"/tasks/{data.task_id}/checkpoint",
|
||||
json={
|
||||
"agent_id": agent_id,
|
||||
"state_summary": data.checkpoint_summary,
|
||||
"remaining_work": data.remaining_work,
|
||||
"notes": data.reason,
|
||||
|
||||
@@ -142,7 +142,7 @@ async def handle_task_progress(
|
||||
|
||||
progress_resp = await client.post(
|
||||
f"/tasks/{task_id}/progress",
|
||||
json={"agent_id": agent_id, "message": message, "percentage": percentage},
|
||||
json={"message": message, "percentage": percentage},
|
||||
)
|
||||
|
||||
if not progress_resp.ok:
|
||||
|
||||
Reference in New Issue
Block a user