From 15f5be3f8b39bc9b68bbc0ba3885a762aef67213 Mon Sep 17 00:00:00 2001 From: Renn F Date: Wed, 24 Dec 2025 23:34:42 +0100 Subject: [PATCH] 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 --- agents/blueprints/backend/be-dev.md | 7 ++- agents/blueprints/backend/be-documenter.md | 3 +- agents/blueprints/backend/be-qa.md | 7 ++- agents/blueprints/frontend/fe-dev.md | 7 ++- agents/blueprints/frontend/fe-documenter.md | 3 +- agents/blueprints/frontend/fe-qa.md | 7 ++- agents/blueprints/ux_ui/ux-dev.md | 7 ++- agents/blueprints/ux_ui/ux-documenter.md | 3 +- agents/blueprints/ux_ui/ux-qa.md | 7 ++- roboco/agents/base.py | 55 +++++++++++++++++++-- roboco/agents/developer.py | 20 +++++++- roboco/agents/documenter.py | 7 +-- roboco/agents/qa.py | 16 ++++-- roboco/agents_config.py | 32 +++++++----- roboco/mcp/tasks/handlers/blocking.py | 1 - roboco/mcp/tasks/handlers/work.py | 2 +- 16 files changed, 149 insertions(+), 35 deletions(-) diff --git a/agents/blueprints/backend/be-dev.md b/agents/blueprints/backend/be-dev.md index 9215fe7e..ddbdf807 100644 --- a/agents/blueprints/backend/be-dev.md +++ b/agents/blueprints/backend/be-dev.md @@ -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 ``` diff --git a/agents/blueprints/backend/be-documenter.md b/agents/blueprints/backend/be-documenter.md index 38d089f1..9d7fc178 100644 --- a/agents/blueprints/backend/be-documenter.md +++ b/agents/blueprints/backend/be-documenter.md @@ -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 diff --git a/agents/blueprints/backend/be-qa.md b/agents/blueprints/backend/be-qa.md index b2ebdf27..10d6eacd 100644 --- a/agents/blueprints/backend/be-qa.md +++ b/agents/blueprints/backend/be-qa.md @@ -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 ``` diff --git a/agents/blueprints/frontend/fe-dev.md b/agents/blueprints/frontend/fe-dev.md index 030a2a03..0ae4453d 100644 --- a/agents/blueprints/frontend/fe-dev.md +++ b/agents/blueprints/frontend/fe-dev.md @@ -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 ``` diff --git a/agents/blueprints/frontend/fe-documenter.md b/agents/blueprints/frontend/fe-documenter.md index fbd7f9d8..6eb9a43c 100644 --- a/agents/blueprints/frontend/fe-documenter.md +++ b/agents/blueprints/frontend/fe-documenter.md @@ -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 diff --git a/agents/blueprints/frontend/fe-qa.md b/agents/blueprints/frontend/fe-qa.md index 12a4fef7..f0208732 100644 --- a/agents/blueprints/frontend/fe-qa.md +++ b/agents/blueprints/frontend/fe-qa.md @@ -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 ``` diff --git a/agents/blueprints/ux_ui/ux-dev.md b/agents/blueprints/ux_ui/ux-dev.md index 68939196..f5c13e9e 100644 --- a/agents/blueprints/ux_ui/ux-dev.md +++ b/agents/blueprints/ux_ui/ux-dev.md @@ -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 ``` diff --git a/agents/blueprints/ux_ui/ux-documenter.md b/agents/blueprints/ux_ui/ux-documenter.md index 8c046d26..6a82fc65 100644 --- a/agents/blueprints/ux_ui/ux-documenter.md +++ b/agents/blueprints/ux_ui/ux-documenter.md @@ -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 diff --git a/agents/blueprints/ux_ui/ux-qa.md b/agents/blueprints/ux_ui/ux-qa.md index 1a85e236..7ae10675 100644 --- a/agents/blueprints/ux_ui/ux-qa.md +++ b/agents/blueprints/ux_ui/ux-qa.md @@ -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 ``` diff --git a/roboco/agents/base.py b/roboco/agents/base.py index 5ea9d4ee..2eed0d4a 100644 --- a/roboco/agents/base.py +++ b/roboco/agents/base.py @@ -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) diff --git a/roboco/agents/developer.py b/roboco/agents/developer.py index 24d9f49f..ac225199 100644 --- a/roboco/agents/developer.py +++ b/roboco/agents/developer.py @@ -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}) diff --git a/roboco/agents/documenter.py b/roboco/agents/documenter.py index 871f4163..4cb389b2 100644 --- a/roboco/agents/documenter.py +++ b/roboco/agents/documenter.py @@ -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, ) diff --git a/roboco/agents/qa.py b/roboco/agents/qa.py index 582c80aa..3d17461a 100644 --- a/roboco/agents/qa.py +++ b/roboco/agents/qa.py @@ -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()}" diff --git a/roboco/agents_config.py b/roboco/agents_config.py index 875d9b1c..4afaf63a 100644 --- a/roboco/agents_config.py +++ b/roboco/agents_config.py @@ -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) diff --git a/roboco/mcp/tasks/handlers/blocking.py b/roboco/mcp/tasks/handlers/blocking.py index 87bd423b..24984cef 100644 --- a/roboco/mcp/tasks/handlers/blocking.py +++ b/roboco/mcp/tasks/handlers/blocking.py @@ -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, diff --git a/roboco/mcp/tasks/handlers/work.py b/roboco/mcp/tasks/handlers/work.py index 955e71a4..67798f56 100644 --- a/roboco/mcp/tasks/handlers/work.py +++ b/roboco/mcp/tasks/handlers/work.py @@ -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: