Many fixes and cleanups

This commit is contained in:
Renn F
2026-05-09 03:15:09 +02:00
parent d819c28893
commit 73e1e96851
23 changed files with 1321 additions and 1059 deletions
+7 -3
View File
@@ -460,9 +460,13 @@ tools:
- roboco_session_history_for_task # Get discussion history for your task
- roboco_report_blocker
# Git Operations (via roboco MCP tools)
- roboco_git_status, roboco_git_log, roboco_git_diff
- roboco_git_commit, roboco_git_push, roboco_git_create_pr
# Git Operations
# Read-only inspection (via roboco-git-readonly MCP):
- roboco_git_status, roboco_git_log, roboco_git_diff, roboco_git_branch_list
# Write path (via roboco-do MCP) — `commit` auto-prefixes [task-id], pushes
# to your branch, and triggers PR creation through the choreographer.
# There is NO separate roboco_git_commit / push / create_pr tool.
- commit (message, files)
```
## Permissions
+15 -2
View File
@@ -15,12 +15,25 @@ RUN corepack enable pnpm
FROM base AS builder
WORKDIR /app
# pnpm 11 prompts for confirmation on modules-purge unless told this is CI.
ENV CI=true
# Copy package manifests first (for layer caching)
COPY panel/package.json panel/pnpm-lock.yaml ./
# Install dependencies with shamefully-hoist to flatten node_modules
# This prevents symlink issues with styled-jsx and other peer deps
RUN pnpm install --frozen-lockfile --shamefully-hoist
# (prevents symlink issues with styled-jsx and other peer deps).
#
# pnpm 11 hard-errors on packages with install scripts unless explicitly
# approved. `sharp` and `unrs-resolver` both ship platform-specific
# prebuilt binaries via @img/sharp-* and napi-postinstall, so the install
# scripts are verification-only — skipping them is safe at runtime.
# `strictDepBuilds=false` downgrades the hard error to a warning while
# keeping the install reproducible against the frozen lockfile.
RUN pnpm install \
--frozen-lockfile \
--shamefully-hoist \
--config.strictDepBuilds=false
# Copy panel source code
COPY panel/ ./
+26 -8
View File
@@ -40,12 +40,30 @@ low=$(printf '%s' "$cmd" | tr "[:upper:]" "[:lower:]")
if echo "$low" | grep -qE '(^|[[:space:];&|])git[[:space:]]+(fetch|pull|push|clone|remote|ls-remote|checkout|commit|merge|rebase|reset|cherry-pick|revert|tag[[:space:]]+-d|update-ref|reflog[[:space:]]+delete)'; then
cat <<'EOF' >&2
Denied: shell git for network / auth / branch-mutating ops is blocked.
Use the roboco-git MCP tools instead:
- roboco_git_status / _log / _diff / _branch_list (read-only local)
- roboco_git_commit / _push / _create_pr (write ops)
They route through the orchestrator which injects the GitHub PAT and
tracks commits against the task. Raw `git fetch` etc. don't have auth
and will fail with "could not read Username for 'https://github.com'".
Read-only inspection (any role):
- roboco-git-readonly MCP: roboco_git_status / _log / _diff / _branch_list
Write paths — there is NO direct shell-git or "roboco_git_commit" tool.
Use the verb that matches your role; the choreographer handles git for you:
- developer / documenter: roboco-do `commit(message, files)`
→ auto-prefixes [task-id], pushes to your branch, opens a PR via
the choreographer when the task transitions out of in_progress.
Your branch is auto-created when you call i_will_work_on().
- cell_pm / main_pm: roboco-flow `complete(task_id, notes)`
→ cell_pm merges the leaf PR; main_pm opens the master PR and
escalates to CEO. PMs never run git directly — they delegate
code work to devs and complete to merge.
- any role: branches are NOT something you set up. They are created
on claim/i_will_work_on. If you don't see your branch, check that
you're actually claimed on the task.
Raw `git fetch` etc. don't have auth (the PAT is injected only inside
the MCP layer) and will fail with "could not read Username for
'https://github.com'".
EOF
exit 2
fi
@@ -57,7 +75,7 @@ fi
# post-clone so the file is uninteresting, but a leaked PAT is unrecoverable
# so belt + suspenders applies.
if echo "$low" | grep -qE '(\.git/config|\.gitconfig|\.git-credentials|\.netrc|\.ssh/|id_rsa|id_ed25519|id_ecdsa|known_hosts)'; then
echo "Denied: command references a credential file or SSH key. Use roboco_git_* MCP tools — the PAT is injected subprocess-side and never lands in these files." >&2
echo "Denied: command references a credential file or SSH key. Don't read git credentials — the PAT is injected subprocess-side by the MCP layer (commit / complete verbs) and never lands in these files." >&2
exit 2
fi
@@ -68,7 +86,7 @@ if echo "$low" | grep -qE '/proc/(self|[0-9]+|\$\$|\$\{.*\})/(environ|cmdline|cw
fi
if echo "$low" | grep -qE '(^|[[:space:];&|])(curl|wget|http|https)[[:space:]][^|]*(github\.com|api\.github\.com)'; then
echo "Denied: direct GitHub HTTP calls bypass the PAT handler. Use roboco_git_* MCP tools." >&2
echo "Denied: direct GitHub HTTP calls bypass the PAT handler. Use the role-appropriate MCP verb: roboco-do commit (devs/docs), roboco-flow complete (PMs), or roboco-git-readonly status/log/diff/branch_list (any role)." >&2
exit 2
fi
+100 -52
View File
@@ -2,78 +2,126 @@
## Overview
Agents have role-specific tool permissions enforced via Claude Code settings.
Native tools are blocked; use `roboco_*` MCP tools instead.
Agents call gateway verbs through three MCP servers, scoped per role:
| MCP server | Provides |
|------------|----------|
| `roboco-flow` | Lifecycle verbs (give_me_work, i_will_work_on, open_pr, complete, …) |
| `roboco-do` | Content/write verbs (commit, note, say, dm, notify, evidence) |
| `roboco-git-readonly` | Read-only git inspection (status, log, diff, branch_list) |
Native shell git is blocked by the bash-guard hook for everyone. There is
**no** `roboco_git_commit / _push / _create_pr / _merge_pr / _checkout`
tool — write operations happen through the lifecycle verbs and the
choreographer handles git as a side-effect.
The canonical source of role → verb mapping is
`roboco/services/gateway/role_config.py`. The tables below summarise it.
## Developer
**Allowed:**
- `roboco_task_*` - task lifecycle
- `roboco_git_*` - all git operations
- `roboco_test_*` - run tests, lint, format
- `roboco_journal_*` - journaling
- `roboco_kb_*`, `roboco_rag_*` - knowledge base
- `Read(*)` - read any file
- `Write/Edit` - workspace only
**Flow verbs (roboco-flow):**
`give_me_work`, `i_will_work_on`, `open_pr`, `i_am_done`,
`i_am_blocked`, `unclaim`, `resume`, `i_am_idle`
**Blocked:**
- `Bash(git:*)` - use roboco_git_* instead
- `Write/Edit` outside workspace
**Content verbs (roboco-do):**
`commit`, `note`, `say`, `dm`, `evidence`
**Workspace:** `/data/workspaces/{project}/{team}/{agent-id}/`
**Read-only git (roboco-git-readonly):** all 4 (`status`, `log`, `diff`,
`branch_list`)
**Workspace writes:** `Write` / `Edit` in
`/data/workspaces/{project}/{team}/{agent-id}/` only.
## QA
**Allowed:**
- `roboco_git_status`, `roboco_git_log`, `roboco_git_diff` - read-only
- `roboco_test_*` - run tests
- `roboco_task_qa_pass`, `roboco_task_qa_fail`
- `Read(*)` - read any file
**Flow verbs:**
`give_me_work`, `claim_review`, `pass`, `fail`, `unclaim`, `resume`,
`i_am_idle`
**Blocked:**
- `roboco_git_commit`, `roboco_git_push` - QA doesn't write code
- All `Write/Edit` - review only
**Content verbs:**
`note`, `say`, `dm`, `evidence` (no `commit` QA does not write code)
**Read-only git:** all 4
**Workspace writes:** none — QA reviews only.
## Documenter
**Allowed:**
- `roboco_docs_*` - documentation tools
- `roboco_git_*` - all git operations
- `Write/Edit` in `/app/docs/**` only
**Flow verbs:**
`give_me_work`, `claim_doc_task`, `i_documented`, `unclaim`, `resume`,
`i_am_idle`
**Blocked:**
- `Write/Edit` outside docs directory
**Content verbs:**
`commit`, `note`, `say`, `dm`, `evidence`
## PM (Cell PM, Main PM)
**Read-only git:** all 4
**Allowed:**
- `roboco_git_*` - all git operations
- `roboco_docs_*` - documentation
- `roboco_task_*` - full task management
- `roboco_notify_send` - send notifications
**Workspace writes:** docs files inside the agent's own workspace
(`/data/workspaces/{project}/{team}/{agent-id}/`).
**Blocked:**
- `Bash(git:*)` - use roboco_git_*
## Cell PM
**Flow verbs:**
`give_me_work`, `i_will_plan`, `delegate`, `submit_up`, `triage`,
`unblock`, `complete`, `escalate_up`, `unclaim`, `resume`, `i_am_idle`
**Content verbs:**
`note`, `say`, `dm`, `notify`, `evidence` (no `commit` — PMs delegate
code; merging the leaf PR happens automatically inside `complete`)
**Read-only git:** all 4
**Workspace writes:** none.
## Main PM
**Flow verbs:**
`give_me_work`, `i_will_plan`, `delegate`, `triage_all`, `unblock`,
`complete`, `escalate_up`, `escalate_to_ceo`, `unclaim`, `resume`,
`i_am_idle`
**Content verbs:**
`note`, `say`, `dm`, `notify`, `evidence`
**Read-only git:** all 4
**Workspace writes:** none. `complete` on a root parent task opens the
master PR via the choreographer and escalates to CEO.
## Board (Product Owner, Head of Marketing)
**Flow verbs:** `triage`, `escalate_to_ceo`, `i_am_idle`
**Content verbs:** `note`, `say`, `dm`, `notify`, `evidence`
**Read-only git:** none.
## Auditor
**Allowed:**
- `roboco_git_status`, `roboco_git_log`, `roboco_git_diff` - read-only
- `Read(*)` - read any file
**Flow verbs:** `triage`, `i_am_idle` (read-only)
**Blocked:**
- All write operations - observer role
**Content verbs:** `note` (scope=reflect), `evidence` (no `say` / `dm`
Auditor observes silently)
## Project Tools
**Read-only git:** none.
| Tool | Dev/QA/Doc | Cell PM | Main PM | CEO |
|------|------------|---------|---------|-----|
| `roboco_project_list` | Own cell | Own cell | All | All |
| `roboco_project_get` | Yes | Yes | Yes | Yes |
| `roboco_project_create` | No | No | Yes | Yes |
| `roboco_project_update` | No | Own cell | All | All |
| `roboco_workspace_ensure` | Yes | Yes | Yes | Yes |
| `roboco_workspace_status` | Yes | Yes | Yes | Yes |
| `roboco_workspace_list` | No | Own cell | All | All |
## Tool Permissions Summary
**CEO Bypass:** CEO has full access to all project operations.
| Capability | Dev | Doc | QA | Cell PM | Main PM | Board | Auditor |
|---|---|---|---|---|---|---|---|
| `commit` (writes code) | ✓ | ✓ | — | — | — | — | — |
| `open_pr` (opens PR) | ✓ | — | — | — | — | — | — |
| `pass` / `fail` (QA verdict) | — | — | ✓ | — | — | — | — |
| `i_documented` | — | ✓ | — | — | — | — | — |
| `delegate` (creates subtasks) | — | — | — | ✓ | ✓ | — | — |
| `complete` (merges PR) | — | — | — | ✓ | ✓ | — | — |
| `escalate_to_ceo` | — | — | — | — | ✓ | ✓ | — |
| `notify` (ack-required) | — | — | — | ✓ | ✓ | ✓ | — |
| `say` / `dm` (channel / A2A) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| `note` (journal entry) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (reflect) |
| `roboco_git_*` (read-only) | ✓ | ✓ | ✓ | ✓ | ✓ | — | — |
| `Write` / `Edit` (own workspace) | ✓ | ✓ | — | — | — | — | — |
**CEO** is human and never inside an agent container; the panel runs as
the CEO via `X-Agent-Role: ceo` against the orchestrator API directly.
+125 -130
View File
@@ -2,170 +2,165 @@
## Identity
- **Agents**: be-pm, fe-pm, ux-pm
- **Role**: `cell_pm`
- **Teams**: backend, frontend, ux_ui
- **Reports to**: Main PM (main-pm)
- **Agents:** be-pm, fe-pm, ux-pm
- **Role:** `cell_pm`
- **Teams:** `backend`, `frontend`, `ux_ui`
- **Reports to:** Main PM (main-pm)
## Core Responsibilities
1. Create and manage tasks for cell
2. Activate tasks (backlog → pending)
3. Assign work to cell members
4. Complete tasks after full workflow
5. Handle escalations from cell
6. Review and merge PRs
1. Plan parent tasks for your cell
2. Delegate subtasks to your dev / QA / documenter
3. Triage incoming work and unblock stalled tasks
4. Complete tasks after QA + docs sign off (which merges the leaf PR)
5. Handle escalations from your cell; bubble up to Main PM when needed
## What You CAN Do
- Create tasks in `backlog` status
- Activate tasks (`backlog``pending`)
- Assign tasks to cell members
- Complete `awaiting_pm_review` tasks
- Cancel any task in cell
- Unblock blocked tasks
- Send notifications
- Index code and documentation
- Merge PRs: `roboco_git_merge_pr()`
- Pull pending parent tasks via `give_me_work()`
- Plan and start a parent task via `i_will_plan(task_id, plan)` (this
also auto-creates the parent branch)
- Create subtasks via `delegate(parent_task_id, title, description, body)`
- Triage your cell's queue via `triage()`
- Unblock blocked tasks via `unblock(task_id, restore=True)`
- Complete tasks via `complete(task_id, notes)` — this merges the leaf
PR (no separate `merge_pr` tool exists; the choreographer does it)
- Submit a finished cell-scoped task up to Main PM via
`submit_up(task_id, notes)`
- Send `notify` (ack-required notifications) — devs/QA/doc cannot
- Read-only inspect git via `roboco_git_status / _log / _diff /
_branch_list`
## What You CANNOT Do
- Access other cells' tasks (Main PM only)
- Clear/refresh KB indexes (Main PM/CEO only)
- Pass/fail QA (QA only)
- Complete documentation (Documenter only)
- Access other cells' tasks Main PM only (`triage_all`)
- Pass / fail QA → QA only
- Write code or commit → devs / documenters only (`commit` is in their
manifest, not yours)
- Open the master PR → that's Main PM's `complete` on the root parent
- Run shell git — blocked by the bash-guard hook
## Task Creation Flow
## Task Flow (gateway verbs)
```python
# 1. Create task in backlog
roboco_task_create({
title: "Implement rate limiting",
description: "Add Redis-based rate limiter",
team: "backend",
status: "backlog",
assigned_to: "be-dev-1" # Optional pre-assign
})
```
give_me_work() → returns a pending parent task assigned to you
i_will_plan(task_id, plan) → claims + starts + auto-creates the parent
branch feature/{team}/{root}/{your_id}
delegate(parent_task_id=..., title=..., description=...,
body={"assigned_to": "be-dev-1", "team": "backend",
"task_type": "code", "acceptance_criteria": [...]})
→ creates a subtask, child branch will
fork off yours when the dev claims it
# 2. Activate when ready
roboco_task_activate(task_id) # backlog → pending
triage() → scan your cell's queue
unblock(task_id, restore=True) → unblock + restore prior status
complete(task_id, notes) → merges the leaf PR; transitions task
to completed (or escalates root parent
to CEO via Main PM)
# 3. Notify developer
roboco_notify_send({
recipient: "be-dev-1",
type: "task_assignment",
task_id: task_id
})
submit_up(task_id, notes) → bubble cell-scoped completion up
escalate_up(task_id, reason) → ask Main PM for help (cross-cell, etc.)
unclaim(task_id) / resume(task_id) / i_am_idle()
```
## Git Workflow
## Tool Surface (per-spawn manifest)
All tasks follow the git workflow:
| MCP server | Verbs you can call |
|-----------------------|--------------------|
| `roboco-flow` | `give_me_work`, `i_will_plan`, `delegate`, `submit_up`, `triage`, `unblock`, `complete`, `escalate_up`, `unclaim`, `resume`, `i_am_idle` |
| `roboco-do` | `note`, `say`, `dm`, `notify`, `evidence` (no `commit`) |
| `roboco-git-readonly` | `roboco_git_status`, `roboco_git_log`, `roboco_git_diff`, `roboco_git_branch_list` |
| `roboco-optimal` | `roboco_ask_mentor`, `roboco_kb_search` |
| `roboco-docs` | project doc file ops |
**Branches are auto-created when tasks are claimed:**
- When you claim your task: `feature/team/MAIN_PM_ID/YOUR_ID`
- When devs claim their subtasks: `feature/team/MAIN_PM_ID/YOUR_ID/DEV_ID`
There is **no** `roboco_git_merge_pr / _create_pr / _checkout` tool —
PR mutations happen as a side-effect of `complete(task_id, notes)`.
**No manual branch creation needed.** Just claim the task and the hierarchical branch is auto-created.
## Branches
PRs merge bottom-up: dev branch → your branch → main PM branch → main.
You don't `checkout` or `branch` by hand. `i_will_plan(task_id, plan)`
creates and switches to the parent branch. Subtask branches fork
automatically when devs call `i_will_work_on(subtask_id)`.
## Delegating Subtasks
```python
delegate(
parent_task_id="<your-parent>",
title="Implement Redis rate limiter",
description="Token-bucket per-route, 100 req/s default.",
body={
"assigned_to": "be-dev-1",
"team": "backend",
"task_type": "code",
"acceptance_criteria": [
"POST /api/foo with 101 reqs in 1s returns 429",
"Redis key TTL matches the configured window",
"Tests cover happy path + boundary",
],
"estimated_complexity": "medium",
},
)
```
`assigned_to` must be a slug your role can delegate to (cell PMs only
delegate to their own team's dev / QA / doc — see
`_validate_delegation_chain` in
`roboco/services/gateway/choreographer/_impl.py`).
## Completing Tasks
After QA passes, docs complete, and PR created:
After QA passed and docs complete (`awaiting_pm_review` state):
```python
# Review and complete
roboco_task_complete(task_id)
# Or escalate major tasks to CEO
roboco_task_escalate_to_ceo(task_id, notes)
```
## Monitoring Cell
```python
# Scan for tasks needing attention
roboco_task_scan(team="backend")
# Check notifications
roboco_notify_list()
# Read team journals
roboco_journal_read_team("be-dev-1", task_id=task_id)
```
## Tool Restrictions
**Full MCP access, but use `roboco_git_*` not native git.**
| Allowed | Blocked |
|---------|---------|
| `roboco_git_*` | Native `Bash(git:*)` |
| `roboco_docs_*` | - |
| `roboco_notify_send` | - |
See: `roboco_kb_search("tool permissions")`
## Key Tools
| Tool | Purpose |
|------|---------|
| `roboco_task_create` | Create new task |
| `roboco_task_activate` | backlog → pending |
| `roboco_task_complete` | Finish task |
| `roboco_task_cancel` | Cancel task |
| `roboco_task_unblock` | Unblock blocked task |
| `roboco_git_merge_pr` | Merge developer PRs |
| `roboco_notify_send` | Send notification |
| `roboco_project_update` | Update own cell's projects |
| `roboco_workspace_list` | List own cell's workspaces |
## Project Management
Update projects assigned to your cell:
```python
roboco_project_update(
slug="roboco",
test_command="uv run pytest -v"
complete(
task_id="<task>",
notes="QA green; docs landed; merging.",
)
```
Create tasks with project selection:
The choreographer:
1. Verifies all subtasks are in a terminal state
2. Verifies the PR is reviewed
3. Merges the leaf PR into the parent branch
4. Transitions the task to `completed` (or escalates the root parent
chain upward — see Main PM)
## Monitoring Your Cell
```python
roboco_task_create(
title="Backend task",
team="backend",
project_slug="roboco" # Required
)
triage() # surfaces tasks waiting on you
roboco_git_status(...) # workspace state
roboco_git_log(...) # cell branch history
note(text="...", scope="reflect") # journal observations
```
**Note:** Cannot create projects (Main PM only) or update other cells' projects.
## Handling Escalations
When receiving escalation:
1. ACK notification: `roboco_notify_ack(notification_id)`
2. Investigate: Read task, journals, messages
3. Decide or escalate to Main PM
4. Communicate decision
5. Unblock if needed: `roboco_task_unblock(task_id)`
## A2A
## A2A and Notifications
```python
roboco_agent_request("fe-pm", "coordination", "Cross-cell dependency on...", task_id)
roboco_a2a_check() # Check inbox
# Cross-cell coordination
dm(recipient="fe-pm", text="Need to align on shared schema; task X.",
task_id="...", skill="api_design")
# Cell-wide announcement (visible to whole cell)
say(channel="backend-cell", text="Heads up — sprint cut at 18:00 UTC.")
# Ack-required notification (PMs / Board only)
notify(target="be-dev-1", text="Please prioritise task X by EOD.",
priority="high", task_id="...")
```
## Escalation
## Escalating to Main PM
Escalate to Main PM when:
- Cross-cell coordination needed
- Resource conflict
- Priority conflict
- Scope change beyond cell
Use `escalate_up(task_id, reason)` when:
Tool: `roboco_task_escalate(task_id, reason)`
- Cross-cell coordination is required
- Resource / priority conflict
- Scope grew beyond the cell
- A non-cell agent is blocking you
```python
escalate_up(task_id="<task>",
reason="Frontend cell needs the new auth endpoint we own; "
"they're blocked. Want to confirm priority swap.")
```
+80 -71
View File
@@ -2,107 +2,116 @@
## Identity
- **Agents**: be-dev-1, be-dev-2, fe-dev-1, fe-dev-2, ux-dev-1, ux-dev-2
- **Role**: `developer`
- **Teams**: backend, frontend, ux_ui
- **Reports to**: Cell PM (be-pm, fe-pm, ux-pm)
- **Agents:** be-dev-1, be-dev-2, fe-dev-1, fe-dev-2, ux-dev-1, ux-dev-2
- **Role:** `developer`
- **Teams:** `backend`, `frontend`, `ux_ui`
- **Reports to:** Cell PM (be-pm, fe-pm, ux-pm)
## Core Responsibilities
1. Claim and complete coding tasks
1. Pick up coding tasks from your team's queue
2. Write quality code that passes QA
3. Create commits linked to tasks
4. Submit work for verification and QA
5. Journal decisions and learnings
3. Make commits linked to your active task
4. Hand off to QA when work is ready
5. Journal decisions and learnings as you go
## What You CAN Do
- Claim tasks in `pending` or `needs_revision` status
- Start, pause, resume work on claimed tasks
- Submit for verification (`verifying`) and QA (`awaiting_qa`)
- Block tasks when waiting on dependencies
- Index code and documentation
- Search and query knowledge base
- Create commits with `roboco_git_commit()`
- Pull pending or needs-revision work via `give_me_work()`
- Start, pause, resume your own claimed tasks
- Make code commits via `commit(message, files)` (auto-prefixed with
`[task-id]`, auto-pushed by the choreographer)
- Submit for QA when implementation is done
- Block your own task if you hit an external dependency
- Search the knowledge base via `roboco_ask_mentor` / `roboco_kb_search`
- Read-only inspect git via `roboco_git_status / _log / _diff /
_branch_list`
## What You CANNOT Do
- Create or assign tasks (PM only)
- Pass or fail QA (QA only)
- Complete tasks (PM only)
- Create or assign tasks → PMs delegate
- Pass or fail QA QA only
- Complete a task / merge a PR → PMs only
- Cancel tasks
- Send notifications
- Send `notify` (ack-required notifications) — devs use `say` (channel)
and `dm` (A2A) only
- Run shell git (`git commit`, `git push`, `git checkout`, etc.) —
blocked by the bash-guard hook
## Task Flow
## Task Flow (gateway verbs)
```
pending → claim → plan → start → work → submit_verification → submit_qa
↑ ↓
└──────────── needs_revision ←──────── (QA fails)
give_me_work()returns a pending task assigned to you
i_will_work_on(task_id) → claims + auto-creates and checks out
feature/{team}/{task-hierarchy}
commit(message, files) → repeat as you make changes
(choreographer auto-pushes to your branch)
open_pr(task_id) → opens the PR, transitions to awaiting_qa
├── QA passes → moves to awaiting_documentation (Documenter takes over)
└── QA fails → returns to needs_revision; fix + commit + open_pr again
i_am_blocked(task_id, reason) → external dependency; cell PM unblocks
i_am_done(task_id, notes) → batched verify + open_pr shortcut
unclaim(task_id) → release a task back to the queue
resume(task_id) → recover after compact / restart
i_am_idle() → no work in your queue right now
```
## Workflow States
## Tool Surface (per-spawn manifest)
| State | Meaning |
|-------|---------|
| `NEEDS_PLAN` | Must call `roboco_task_plan()` first |
| `READY_TO_START` | Call `roboco_task_start()` |
| `EXECUTING` | Work in progress |
| `REVISION_REQUIRED` | Fix QA/PM feedback |
| MCP server | Verbs you can call |
|-----------------------|--------------------|
| `roboco-flow` | `give_me_work`, `i_will_work_on`, `open_pr`, `i_am_done`, `i_am_blocked`, `unclaim`, `resume`, `i_am_idle` |
| `roboco-do` | `commit`, `note`, `say`, `dm`, `evidence` |
| `roboco-git-readonly` | `roboco_git_status`, `roboco_git_log`, `roboco_git_diff`, `roboco_git_branch_list` |
| `roboco-optimal` | `roboco_ask_mentor`, `roboco_kb_search` |
Note: Git branches are auto-created when you claim the task. No waiting needed.
There is **no** `roboco_git_commit / _push / _create_pr / _merge_pr /
_checkout` tool. The single `commit` verb covers commit + push + PR
opening (the PR opens at `open_pr` time).
## Tool Restrictions
## Branch Discipline
Use `roboco_*` MCP tools, not native Claude tools:
- Git: `roboco_git_*` (native git blocked)
- Write/Edit: workspace only
- See: `roboco_kb_search("tool permissions")`
## Key Tools
| Tool | Purpose |
|------|---------|
| `roboco_task_claim` | Take ownership of a task |
| `roboco_task_start` | Begin work (status: in_progress) |
| `roboco_git_commit` | Commit with task ID prefix |
| `roboco_task_submit_qa` | Submit for QA review |
| `roboco_journal_entry` | Log progress and decisions |
| `roboco_kb_search` | Search knowledge base |
## Before Starting Any Task
1. Search KB for similar past work: `roboco_kb_search()`
2. Read proactive context: `roboco_get_proactive_context()`
3. Check standards: `roboco_get_standards(domain="coding")`
4. Announce to cell channel: `roboco_message_send()`
- Branches are auto-created on `i_will_work_on()`.
- Don't checkout branches by hand — call the verb on the right task.
- If you see a `BRANCH_MISMATCH` envelope, you're on the wrong task.
Use `give_me_work()` again or `unclaim` and re-pick the intended task.
## Before Submitting to QA
1. Run tests: `uv run pytest` (backend) or `pnpm test` (frontend)
2. Run linter: `uv run ruff check .` or `pnpm lint`
3. Run type check: `uv run mypy roboco/` or `pnpm typecheck`
4. Write journal reflection: `roboco_journal_reflect()`
5. Push branch: `roboco_git_push()`
1. **Tests:** `uv run pytest` (backend) or `pnpm test` (frontend)
2. **Lint:** `uv run ruff check .` or `pnpm lint`
3. **Types:** `uv run mypy roboco/` or `pnpm typecheck`
4. **Format:** `uv run ruff format .` or `pnpm format`
5. **Reflect:** `note(text="...", scope="reflect")` on what changed and
why — useful for QA's diff review.
6. `open_pr(task_id)` — the choreographer pushes any unpushed
commits and opens the PR.
## A2A Collaboration
Direct peer-to-peer messaging:
```python
# Request review (task_id required)
roboco_agent_request("be-qa", "code_review", "Please review", task_id)
# Direct A2A inside your cell (same team — no policy gate)
dm(recipient="be-qa", text="Quick sanity check: ...", task_id="...")
# Check inbox for incoming messages
roboco_a2a_check()
# Channel post (visible to cell)
say(channel="backend-cell", text="Started on task X — anyone hit Y before?")
```
Cross-cell A2A is denied by policy. Route through your Cell PM via
`escalate_up(task_id, reason)`.
## Escalation
Escalate to Cell PM when:
- Requirements are unclear
- Blocked by external factor
- Scope question arises
- Need architectural decision
Escalate to your Cell PM when:
Tool: `roboco_task_escalate(task_id, reason)`
- Requirements are unclear
- Blocked by an external factor (use `i_am_blocked` for in-band block;
`escalate_up` if PM intervention is needed)
- Scope question arises
- Architectural decision is required
```python
escalate_up(task_id, reason="Need architectural call on caching layer")
```
+92 -89
View File
@@ -2,129 +2,132 @@
## Identity
- **Agents**: be-qa, fe-qa, ux-qa
- **Role**: `qa`
- **Teams**: backend, frontend, ux_ui
- **Reports to**: Cell PM (be-pm, fe-pm, ux-pm)
- **Agents:** be-qa, fe-qa, ux-qa
- **Role:** `qa`
- **Teams:** `backend`, `frontend`, `ux_ui`
- **Reports to:** Cell PM (be-pm, fe-pm, ux-pm)
## Core Responsibilities
1. Review developer work for quality
2. Verify acceptance criteria are met
3. Run tests and check code quality
4. Pass or fail QA with clear reasoning
5. Journal review findings
1. Review developer PR diffs against the task's acceptance criteria
2. Run tests / lint / typecheck where applicable
3. Pass or fail with concrete reasoning and concrete findings
4. Journal evidence of what was checked
## What You CAN Do
- Claim tasks in `awaiting_qa` status
- Pass QA (`awaiting_qa` `awaiting_documentation`)
- Fail QA (`awaiting_qa` `needs_revision`)
- Block tasks when waiting on information
- Search and query knowledge base
- Pull awaiting-QA tasks via `give_me_work()` / `claim_review(task_id)`
- Pass via `pass(task_id, notes)` (transitions to `awaiting_documentation`)
- Fail via `fail(task_id, issues)` (returns to `needs_revision`)
- Read-only inspect git via `roboco_git_status / _log / _diff /
_branch_list`
- Search the knowledge base via `roboco_ask_mentor` / `roboco_kb_search`
- Note evidence via `note(text=..., scope="...")` and `evidence(...)`
## What You CANNOT Do
- Claim `pending` tasks (developer only)
- Create or assign tasks (PM only)
- Index content
- Complete documentation
- Complete tasks (PM only)
- Cancel tasks
- Send notifications
- Review your own development work (self-review prevention)
- Claim pending tasks (devs only)
- Modify code, commit, push — `commit` is **not** in your manifest
- Open / merge PRs
- Complete tasks → PMs only
- Send `notify` (ack-required notifications) → PMs / Board only
- Review your own dev work — the self-review guard rejects it on claim
## Task Flow
## Task Flow (gateway verbs)
```
awaiting_qa → claim → start → review → pass/fail
pass: awaiting_documentation
fail: needs_revision (back to developer)
give_me_work() → returns an awaiting_qa task
claim_review(task_id) → claim for review
(auto-checks-out the dev's branch)
pass(task_id, notes) → moves to awaiting_documentation
fail(task_id, issues=[...]) → moves to needs_revision; the dev's
original assignee gets it back
unclaim(task_id) / resume(task_id) / i_am_idle()
```
## Tool Restrictions
## Tool Surface (per-spawn manifest)
**You are read-only.** Cannot modify code or commit.
| MCP server | Verbs you can call |
|-----------------------|--------------------|
| `roboco-flow` | `give_me_work`, `claim_review`, `pass`, `fail`, `unclaim`, `resume`, `i_am_idle` |
| `roboco-do` | `note`, `say`, `dm`, `evidence` (no `commit`, no `notify`) |
| `roboco-git-readonly` | `roboco_git_status`, `roboco_git_log`, `roboco_git_diff`, `roboco_git_branch_list` |
| `roboco-optimal` | `roboco_ask_mentor`, `roboco_kb_search` |
| Allowed | Blocked |
|---------|---------|
| `roboco_git_status/log/diff` | `roboco_git_commit/push` |
| `roboco_test_*` | All `Write/Edit` |
| `Read(*)` | Native git commands |
See: `roboco_kb_search("tool permissions")`
## Key Tools
| Tool | Purpose |
|------|---------|
| `roboco_task_claim` | Take ownership for QA |
| `roboco_task_start` | Begin review |
| `roboco_task_qa_pass` | Approve and advance |
| `roboco_task_qa_fail` | Reject with issues |
| `roboco_journal_read_team` | Read developer's journey |
| `roboco_git_diff` | View code changes |
There is **no** `commit` / `roboco_git_commit / _push / _create_pr` tool
in your surface — QA is read-only by design. Branches are auto-checked-
out on `claim_review`; you don't run `git checkout` either.
## Review Checklist
Before passing QA:
1. Read developer's journal: `roboco_journal_read_team(developer_id, task_id=task_id)`
2. Check acceptance criteria in task
3. Run tests: `uv run pytest` or `pnpm test`
4. Review code changes: `roboco_git_diff()`
5. Verify functionality works as expected
6. Check code quality and standards
Before deciding, gather evidence:
1. Read the task: criteria + dev's notes are on the task object.
2. Read the dev's journal: filter on the developer's slug + this task.
3. Inspect the diff: `roboco_git_diff(project_slug=...)` against the
PR head.
4. Run the suite if relevant:
- Backend: `uv run pytest`, `uv run ruff check .`, `uv run mypy roboco/`
- Frontend: `pnpm test`, `pnpm lint`, `pnpm typecheck`
5. Verify the acceptance criteria *line by line* — that's what `pass`
is asserting.
6. `note(text="<what you checked>", scope="evidence")` so the trail
survives compaction.
## Passing QA
```python
roboco_task_qa_pass(task_id, {
notes: "All acceptance criteria met. Tests pass. Code follows standards."
})
pass(
task_id="<task>",
notes=(
"All 3 acceptance criteria verified: 429 on 101st req, "
"Redis TTL matches, tests cover the boundary. ruff + mypy "
"clean. Journal logged."
),
)
```
`notes` must be substantive — the enforcement layer rejects empty or
near-empty notes. The transition takes the task to
`awaiting_documentation`; the documenter and the dev work in parallel
from there.
## Failing QA
```python
roboco_task_qa_fail(task_id, {
notes: "Issues found during review",
issues: [
"Bug: Login fails with special characters in password",
"Missing: Error handling for timeout case"
]
})
fail(
task_id="<task>",
issues=[
"Bug: 100th request also returns 429 — boundary off-by-one.",
"Missing: tests for Redis-down failover path; AC #3 unmet.",
],
)
```
Task returns to original developer with `needs_revision` status.
The task goes back to `needs_revision`. The original developer is
re-assigned automatically (see `extract_original_developer` in
`roboco/services/task.py`).
## Self-Review Prevention
System enforces: QA agent cannot review tasks they originally developed.
The `original_developer` is tracked in `quick_context`. If QA agent == original developer, the claim is FORBIDDEN.
## Before Making Decision
1. Journal your review: `roboco_journal_entry({type: "qa_review"})`
2. Write reflection: `roboco_journal_reflect()`
3. Provide clear reasoning in pass/fail notes
## A2A Requests
Developers send code review requests via A2A:
```python
# Check inbox (auto-notified via hook)
roboco_a2a_check()
```
The system blocks QA from reviewing their own dev work. The
`original_developer` is recorded in `quick_context` at submit-for-qa
time; if `qa_agent_id == original_developer_id` the `claim_review`
returns a `not_authorized` envelope.
## Escalation
Escalate to Cell PM when:
- Cannot reproduce reported issue
- Test criteria unclear
- Critical security flaw found
- Test environment issues
`escalate_up` is **not** in your manifest. Use `dm` to your Cell PM if
something needs attention beyond pass/fail:
Tool: `roboco_task_escalate(task_id, reason)`
```python
dm(recipient="be-pm",
text="Task X — security concern, can you take a look before we "
"merge?",
task_id="...")
```
If the situation is unresolvable from the QA side (e.g. test
environment broken, can't reproduce), `fail(task_id, issues)` with the
full context is the right move; the Cell PM will pick it up from
`needs_revision`.
+43 -63
View File
@@ -1,6 +1,10 @@
# Git Tools
## Read Operations
There is **no** "roboco_git_commit / _push / _create_pr / _merge_pr / _checkout"
MCP tool. Anything mutating the repo goes through one of two role-scoped
verbs and the choreographer handles git for you.
## Read Operations (any role) — `roboco-git-readonly`
| Tool | Purpose |
|------|---------|
@@ -9,83 +13,55 @@
| `roboco_git_branch_list` | List branches |
| `roboco_git_diff` | View changes |
## Status and Diff
```python
# Check status
status = roboco_git_status(project_slug="roboco")
# View changes
diff = roboco_git_diff(project_slug="roboco")
# View history
log = roboco_git_log(
project_slug="roboco",
branch="feature/backend/a1b2c3d4"
)
```
## Branch Operations
**Branches are auto-created when tasks are claimed:**
- Root task claim → `feature/team/ROOT_ID`
- Subtask claim → `feature/team/ROOT_ID/SUB_ID`
- Sub-subtask claim → `feature/team/ROOT_ID/SUB_ID/SUBSUB_ID`
No manual branch creation needed.
```python
# List branches
log = roboco_git_log(project_slug="roboco", branch="feature/backend/a1b2c3d4")
branches = roboco_git_branch_list(project_slug="roboco")
# Checkout branch (if needed)
roboco_git_checkout(
project_slug="roboco",
branch="feature/backend/a1b2c3d4"
)
```
## Commit and Push
## Branch Lifecycle — automatic
Branches are auto-created when an agent transitions a task to `in_progress`:
- Root task → `feature/team/ROOT_ID`
- Subtask → `feature/team/ROOT_ID--SUB_ID`
- Sub-subtask → `feature/team/ROOT_ID--SUB_ID--SUBSUB_ID`
You never run `git checkout` or `git branch` yourself; calling
`i_will_work_on(task_id)` (developers) or `i_will_plan(task_id, plan)` (PMs)
creates the branch and switches your workspace to it.
## Write Path — by role
### Developers and Documenters → `commit` (roboco-do)
```python
# Commit with task link
roboco_git_commit(
project_slug="roboco",
task_id=task_id,
message="Add rate limiting endpoint",
commit_type="feat" # Required
)
# Creates: [a1b2c3d4] feat: Add rate limiting endpoint
# Push to remote
roboco_git_push(project_slug="roboco", task_id=task_id)
# Commit on your active task's branch. The choreographer:
# - prefixes the message with [task-id]
# - validates against commit_validator
# - pushes to the remote branch
# - opens a PR when the task transitions out of in_progress
commit(message="Add rate limiting endpoint", files=["roboco/api/routes/rate.py"])
```
## Pull Requests
There is no separate `push` step and no separate `create_pr` step. Both are
side-effects of the lifecycle transitions the verbs already drive.
### PMs → `complete` (roboco-flow)
```python
# Create PR
roboco_git_create_pr(
project_slug="roboco",
task_id=task_id,
title="[TASK-a1b2c3d4] Add rate limiting",
body="## Summary\n..."
)
# Merge PR (PM only)
roboco_git_merge_pr(
project_slug="roboco",
pr_number=123,
task_id=task_id,
merge_method="squash" # squash, merge, rebase
)
# Cell PM completing a leaf task: merges the leaf PR.
# Main PM completing a parent task: opens the master PR + escalates to CEO.
complete(task_id="a1b2c3d4-...", notes="QA passed; docs complete; ready to ship.")
```
## Branch Naming
PMs never run `git` directly and have no commit/push tools. PMs `delegate`
code work to devs, then `complete` to merge once QA + docs sign off.
```
{type}/{team}/{task-id-prefix}
```
## Branch Naming Convention
`{type}/{team}/{task-hierarchy}`
| Type | Use |
|------|-----|
@@ -94,3 +70,7 @@ roboco_git_merge_pr(
| `chore/` | Maintenance |
| `docs/` | Documentation |
| `hotfix/` | Urgent fixes |
Hierarchy uses `--` (two hyphens) as the separator, not `/`, so a hierarchy
slug like `ABC12345--DEF67890--GHI11111` is one git branch segment, not
three nested directories.
+34 -23
View File
@@ -2,18 +2,25 @@
## Native Git Commands Blocked
**Symptom:** `Bash(git commit)` or similar git command denied
**Symptom:** `Bash(git commit)`, `Bash(git push)`, `Bash(git checkout)`, etc.
denied by the bash-guard hook.
**Cause:** Native git commands are blocked for all agents
**Cause:** Shell git for network / auth / branch-mutating ops bypasses the
PAT injection done by the MCP layer; raw `git fetch` etc. would fail with
`could not read Username for 'https://github.com'` anyway.
**Solution:** Use MCP tools instead:
| Blocked | Use Instead |
|---------|-------------|
| `git commit` | `roboco_git_commit()` |
| `git push` | `roboco_git_push()` |
| `git status` | `roboco_git_status()` |
| `git diff` | `roboco_git_diff()` |
| `git log` | `roboco_git_log()` |
**Solution:** Use the role-scoped MCP verb that matches what you're trying
to do. There is **no** `roboco_git_commit / _push / _create_pr / _merge_pr
/ _checkout` MCP tool — the surface is smaller than that:
| Blocked shell command | Use instead |
|-----------------------|-------------|
| `git status` / `git diff` / `git log` / `git branch` | `roboco_git_status` / `roboco_git_diff` / `roboco_git_log` / `roboco_git_branch_list` (roboco-git-readonly) |
| `git commit` + `git push` (devs / docs) | `commit(message, files)` (roboco-do) — auto-prefixes [task-id], pushes |
| `git checkout` of a task branch | None — branch is auto-checked-out by `i_will_work_on(task_id)` (devs) or `i_will_plan(task_id, plan)` (PMs) |
| Open a PR | None — PR is opened by the choreographer when the dev calls `open_pr(task_id)` |
| Merge a PR | `complete(task_id, notes)` (PMs only) — Cell PM merges leaf PR; Main PM merges parent and escalates to CEO |
| `git fetch` / `git pull` / `git rebase` | None at the agent layer — task branches are short-lived; if yours diverged, `unclaim` and re-`claim` |
## Write/Edit Outside Workspace
@@ -22,36 +29,40 @@
**Cause:** Write operations restricted to your workspace
**Solution:**
- Developers: Only write in `/data/workspaces/{project}/{team}/{agent-id}/`
- Documenters: Only write in `/app/docs/`
- QA: No write access (review only)
## QA Cannot Commit
**Symptom:** `roboco_git_commit()` denied for QA agent
**Symptom:** `commit()` returns `not_authorized` for a QA agent
**Cause:** QA role is read-only, cannot modify code
**Cause:** QA role is read-only cannot modify code or open PRs.
**Solution:** QA reviews and provides feedback. Developers make fixes.
**Solution:** QA `pass(task_id, notes)` or `fail(task_id, issues)` only.
Developers fix issues and re-submit.
## NO_PLAN Error
## NO_PLAN Error on Start
**Symptom:** `roboco_task_start()` returns NO_PLAN error
**Symptom:** Lifecycle transition rejected with NO_PLAN
**Cause:** Task has no plan submitted
**Cause:** Parent tasks require a plan before they can leave `pending`.
**Solution:** Call `roboco_task_plan()` before `roboco_task_start()`
See: `roboco_kb_search("task planning workflow")`
**Solution:** PMs call `i_will_plan(task_id, plan)`; the verb both records
the plan and transitions the task into `in_progress`.
## Parent Branch Required
**Symptom:** Can't claim subtask, error "Parent task must be claimed first"
**Cause:** Parent task hasn't been claimed yet, so it has no branch
**Cause:** Parent task hasn't been claimed/started yet, so it has no
branch for the subtask's branch to fork from.
**Solution:**
1. Parent task must be claimed first (branch auto-creates on claim)
2. Then subtask can be claimed (its branch forks from parent's)
Note: Branches are auto-created hierarchically. No manual creation needed.
1. Parent task must transition to `in_progress` first (PMs:
`i_will_plan(parent_id, plan)`; devs: `i_will_work_on(parent_id)`).
2. Then the subtask's branch will auto-fork from the parent's on claim.
Branches are auto-created hierarchically. No manual creation needed.
+89 -56
View File
@@ -2,87 +2,120 @@
## Missing Git Token
**Error**: "Project requires a git token for HTTPS repositories"
**Error:** `Project requires a git token for HTTPS repositories`
(also surfaces as `WorkspaceError` during clone)
**Cause**: No GitHub PAT configured for this project
**Cause:** No encrypted GitHub PAT on
`projects.git_token_encrypted` for this project.
**Solutions**:
1. Open project settings in UI
2. Add GitHub token (Personal Access Token)
3. Token needs `repo` scope for clone/push/PR
**Fix:**
**Notes**:
- Each project requires its own token (no global fallback)
- Tokens are encrypted at rest
- Token never exposed in API responses
1. Open the project's settings tab in the panel
2. Paste a GitHub Personal Access Token with `repo` scope
3. Save — the panel encrypts and stores it; the API never returns
the plaintext
Notes:
- Each project has its own token (no global fallback)
- Tokens are encrypted at rest with Fernet
- The token is injected only at the MCP layer (commit / clone / PR ops);
`.git/config` is scrubbed post-clone so a leaked PAT from there is
not a recovery path
## Workspace Not Found
**Error**: "Workspace does not exist"
**Error:** `Workspace does not exist`
**Cause**: Workspace not cloned yet
**Cause:** Workspace not cloned yet (or `ROBOCO_WORKSPACE_AUTO_CLONE`
is `false` and no manual clone has run).
**Solutions**:
- If auto_clone enabled: workspace creates on first access
- Manual: Wait for workspace service to clone
- Check config: `ROBOCO_WORKSPACE_AUTO_CLONE=true`
**Fix:**
## Cannot Push
- If `ROBOCO_WORKSPACE_AUTO_CLONE=true` (default), the first MCP verb
that touches the workspace will trigger the clone. Just call your
next verb (`i_will_work_on`, `commit`, etc.).
- Otherwise check `ROBOCO_WORKSPACE_CLONE_TIMEOUT` and the
orchestrator logs for a stuck clone.
**Error**: "Push failed"
## BRANCH_MISMATCH
**Causes**:
1. No commits to push
2. Remote branch doesn't exist
3. Conflicts with remote
**Error envelope:**
`Workspace is on '<other-branch>' but task requires '<task-branch>'`
**Solutions**:
- Create commits first: `roboco_git_commit(...)`
- Check branch exists: `roboco_git_branches()`
- Pull and resolve conflicts
**Cause:** You're trying to act on task A while your workspace is still
on task B's branch.
## Branch Already Exists
**Fix:** Don't checkout by hand — there is no `roboco_git_checkout`
tool. Call the verb on the *intended* task instead:
**Error**: "Branch already exists"
- Devs: `i_will_work_on(task_id)` switches to that task's branch
- PMs: `i_will_plan(task_id, plan)` switches to that parent task's
branch
- QA: `claim_review(task_id)` switches to the dev's branch under review
**Cause**: Trying to create existing branch
If your workspace is dirty, the verb returns an envelope telling you to
either `commit(...)` first or escalate via `i_am_blocked`.
**Solution**: Checkout existing branch:
```python
roboco_git_checkout(project_slug, branch_name)
```
## NO_COMMITS on open_pr
## Merge Conflicts
**Cause:** No commits on the task yet — the choreographer has nothing
to open a PR over.
**Error**: "Merge conflict"
**Fix:** `commit(message=..., files=...)` at least once, then call
`open_pr(task_id)` again.
**Cause**: Conflicting changes between branches
## NO_PR on pass / fail
**Solutions**:
1. Pull latest from target branch
2. Resolve conflicts manually
3. Commit resolution
4. Push again
**Cause:** The PR was never created — usually because
`open_pr(task_id)` did not run cleanly.
## PR Creation Failed
**Fix:** Roll back to the dev: have them re-call `open_pr(task_id)`
after fixing whatever blocked the PR opening (see PR Creation Failed,
below). QA cannot create the PR.
**Error**: "PR creation failed"
## PR Creation Failed (during open_pr)
**Causes**:
1. No commits on branch
2. Branch not pushed
3. GitHub CLI not configured
**Causes:**
**Solutions**:
- Push branch first: `roboco_git_push()`
- Verify commits exist: `roboco_git_log()`
1. Nothing to push — no commits on the branch
2. Branch is on the workspace but not pushed yet (rare; the choreographer
pushes during `commit`, but a stale workspace can drift)
3. Project has no git token configured
4. The GitHub repo doesn't allow PRs from your branch (rare; usually
org-level branch protection)
## Checkout Failed
**Fix:**
**Error**: "Cannot checkout - uncommitted changes"
- Verify commits exist with `roboco_git_log(project_slug=...)`
- Verify the project has a git token (Missing Git Token, above)
- If the task is in a stuck state, `unclaim(task_id)` and re-`claim`
to rebuild the branch
**Cause**: Working directory has uncommitted changes
## FORCE_PUSH_FORBIDDEN
**Solutions**:
- Commit changes: `roboco_git_commit(...)`
- Or stash changes (if supported)
**Cause:** Force-push is CEO-only. Anyone else attempting it (typically
because their branch diverged) is denied.
**Fix:** `unclaim(task_id)` and re-`claim` it. The choreographer
rebuilds the branch from the parent's HEAD; replay your commits with
`commit(...)`.
## Merge Conflicts on `complete`
**Cause:** The leaf PR conflicts with the parent branch (cell branch
or master).
**Fix:** This currently surfaces as an error envelope from `complete`.
The recovery path:
1. PM `unblock(task_id, restore=False)` — frees the task back to the
dev
2. Dev re-claims, the choreographer rebuilds the branch off the latest
parent, and they replay their commits
3. Dev `open_pr` again
4. QA re-runs `pass` (or `fail` if the rebase changed behaviour)
5. PM `complete` again
We don't expose a "resolve conflicts in place" path at the agent layer
— rebuilds via the lifecycle are the recovery.
+31 -3
View File
@@ -19,8 +19,36 @@ Links:
- Journal: {api}/journals/{agent-slug}
```
**Required:** `commit_type` (feat, fix, chore, docs, refactor, test, style, perf, ci, build)
**Required:** the conventional `type` (feat, fix, chore, docs, refactor,
test, style, perf, ci, build) at the start of the subject. The
`commit_validator` rejects messages that don't start with one of these
followed by `(scope)?:`.
**Optional:** `scope` (api, auth, db, ui), `body`, `files`
**Optional:** `scope` (api, auth, db, ui), `body`, the `files` argument
to scope the commit.
Use `roboco_git_commit()` - message built automatically with task context.
## How to commit
Use the **`commit`** verb on the roboco-do MCP — devs and documenters
only. There is no `roboco_git_commit` tool.
```python
commit(
message="feat(api): add Redis rate limiter",
files=["roboco/api/routes/rate.py", "tests/integration/test_rate.py"],
# files is optional; defaults to all staged + modified tracked files
)
```
The choreographer:
1. Strips any leading `[task-id]` you might have included
2. Runs `commit_validator` on the subject
3. Re-prefixes with the canonical `[task-id-first-8]`
4. Stages the listed files (or everything tracked + modified)
5. Commits in the agent's workspace
6. Pushes to the agent's branch on origin
7. Records the commit on the task (`commits[]` field on `TaskTable`)
You don't need a separate `push` step. There is no `roboco_git_push`
tool.
+39 -52
View File
@@ -2,56 +2,44 @@
## Commit Format
All commits are automatically prefixed with task ID:
All commits are automatically prefixed with the task ID by the choreographer:
```
[{task-id-prefix}] {message}
```
Example:
```
[a1b2c3d4] Add rate limiting endpoint
```
Example: `[a1b2c3d4] Add rate limiting endpoint`
You write the message — the prefix is added for you. Don't include
`[task-id]` yourself; it gets stripped and re-applied.
## Who Can Commit
`commit` is in the **roboco-do** MCP and is mounted only for **developers**
and **documenters**. PMs delegate code work and call `complete` to merge.
There is **no** `roboco_git_commit / _push / _create_pr` MCP tool. The
single `commit` verb covers commit + push + PR-trigger via the
choreographer.
## Creating Commits
```python
roboco_git_commit(
project_slug="roboco",
task_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
commit(
message="Add rate limiting endpoint",
commit_type="feat" # Required
files=["roboco/api/routes/rate.py"], # optional; defaults to all staged
)
```
This automatically:
1. Prefixes commit with task ID (first 8 chars)
2. Records commit in task's commit history
3. Links to work session
## Commit Message Types
| Type | Description |
|------|-------------|
| `feat` | New feature |
| `fix` | Bug fix |
| `docs` | Documentation |
| `style` | Formatting |
| `refactor` | Code restructure |
| `test` | Tests |
| `chore` | Maintenance |
| `perf` | Performance |
## Full Commit Format
```
{type}({scope}): {description}
{body}
Task: {task-id}
Co-authored-by: {agent-name}
```
1. Prefixes the commit with `[task-id-first-8-chars]`
2. Validates the message via `commit_validator`
3. Stages the listed files (or everything tracked + modified if omitted)
4. Pushes to the agent's auto-created branch
5. Records the commit on the task (`commits[]` field on `TaskTable`)
6. Opens a PR through the choreographer when the task transitions out of
`in_progress` (no separate `create_pr` call required)
## Before Committing
@@ -60,23 +48,22 @@ Co-authored-by: {agent-name}
3. Run type check: `uv run mypy roboco/` or `pnpm typecheck`
4. Format code: `uv run ruff format .` or `pnpm format`
## Push Commits
## After Committing
You don't push or create a PR yourself. The choreographer pushed the
commit during `commit()`, and the PR is opened/merged as part of the
lifecycle transitions:
- `open_pr(task_id)` — opens the PR (devs)
- `pass(task_id)` (QA) → `i_documented(task_id)` (doc) → `complete(task_id)`
(cell PM merges the leaf PR; main PM opens the master PR)
## Viewing Commits and History
```python
roboco_git_push(project_slug="roboco", task_id="a1b2c3d4...")
```
Push before:
- Submitting for QA
- Creating PR
- Ending work session
## Viewing Commits
```python
# View commit history
roboco_git_log(project_slug="roboco", branch="feature/backend/a1b2c3d4")
# View changes
roboco_git_diff(project_slug="roboco")
# Read-only inspection (any role) — roboco-git-readonly MCP
status = roboco_git_status(project_slug="roboco")
log = roboco_git_log(project_slug="roboco", branch="feature/backend/a1b2c3d4--def67890")
diff = roboco_git_diff(project_slug="roboco")
branches = roboco_git_branch_list(project_slug="roboco")
```
+31 -6
View File
@@ -1,10 +1,35 @@
# Git PR Types
| `is_root_pr` | Target | Reviewer | Content |
|--------------|--------|----------|---------|
| `True` | main | CEO | Full task tree, all commits, all agent links |
| `False` | parent branch | PM | Simple summary, task commits only |
| `is_root_pr` | Target | Reviewer / Merger | Content |
|--------------|--------|-------------------|---------|
| `True` | `master` | CEO approves; Main PM opens + merges | Full task tree, all commits, all agent links |
| `False` | parent branch | Cell PM merges | Task commits only, scoped to the cell |
**Auto-checkout:** `roboco_task_start()` checks out branch automatically. Blocks if uncommitted changes exist.
## How PRs Are Created
**PR creation:** Use `roboco_git_create_pr()`. Title/body auto-generated from templates.
There is **no** `roboco_git_create_pr` MCP tool. PRs are side-effects of
lifecycle transitions, driven by the choreographer:
- **Leaf PR (cell-scoped, `is_root_pr=False`)**:
Opened automatically when the assigned developer calls
`open_pr(task_id)` after their `commit(...)` calls. Merged when
the Cell PM calls `complete(task_id, notes)` after QA + docs sign off.
- **Master PR (`is_root_pr=True`)**:
Opened by the choreographer when the **Main PM** calls
`complete(task_id, notes)` on the root parent task. Merged by the CEO
via the dashboard once all cell-scoped PRs have been merged into it.
Title and body are generated from the task templates in
`roboco/templates/git/pr_*.py`. Don't hand-write PR descriptions in the
agent prompts — they'll be overridden.
## Auto-Checkout
Branches and checkout are handled automatically:
- `i_will_work_on(task_id)` (devs) creates the task's branch and checks it
out in the agent's workspace.
- `i_will_plan(task_id, plan)` (PMs) does the same for parent tasks.
- Workspace dirty? The verb returns an error envelope; clean up first
with `commit(...)` or escalate via `i_am_blocked(task_id, reason)`.
+69 -57
View File
@@ -1,81 +1,93 @@
# Pull Request Creation
## When to Create PR
## When PRs Are Created
Create PR in `awaiting_documentation` phase (parallel with documenter).
PRs are opened **before** QA review, not during `awaiting_documentation`.
The choreographer creates the PR as a side-effect of the developer's
`open_pr(task_id)` transition (`verifying → awaiting_qa`).
## Creating a PR
This is by design: QA reviews the real PR diff on GitHub, and the
downstream PM/CEO approval chain operates on a PR that already exists.
You do **not** call any tool to create a PR. There is no
`roboco_git_create_pr` MCP tool.
## How the dev triggers it
```python
roboco_git_create_pr(
project_slug="roboco",
task_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
title="[TASK-a1b2c3d4] Add rate limiting",
body="## Summary\n- Implemented sliding window...\n\n## Test Plan\n..."
)
# 1. Make commits as you work (auto-pushes, no separate push step)
commit(message="feat(api): add Redis rate limiter",
files=["roboco/api/routes/rate.py", "tests/integration/test_rate.py"])
# 2. Once acceptance criteria are implemented + tested, hand off to QA.
# The choreographer opens the PR here, sets pr_number/pr_url on the
# task, and transitions verifying → awaiting_qa.
open_pr(task_id="<task>")
```
This automatically:
1. Creates PR via GitHub CLI (`gh pr create`)
2. Targets project's default branch
3. Sets `pr_created=True` on task
4. Records PR number and URL
The transition enforces (`enforcement/task_lifecycle.py`):
## PR Title Format
- `self_verified=True` — set when you call `i_am_done()` or
`verify(task_id)` first
- `commits` non-empty — at least one commit on the task
- `progress_updates` non-empty — at least one note on what changed
- `pr_number` is set automatically by the choreographer; you don't pass it
```
[TASK-{id-prefix}] {description}
```
If any precondition is missing, the verb returns an envelope explaining
what's missing and how to remediate.
Example: `[TASK-a1b2c3d4] Add rate limiting endpoint`
## PR Title and Body
## PR Body Template
Generated from templates in `roboco/templates/git/pr_internal.py` and
`roboco/templates/git/pr_root.py`. You don't write the body by hand —
it's filled with task title, acceptance criteria, the dev's notes, and
the standard traceability links.
```markdown
## Summary
- What was implemented
- Key changes
Title format: `[TASK-{root-id:8}:{task-id:8}] {task-title}`.
## Test Plan
- How to test the changes
- Test coverage
## Parallel Documenter Phase
Task: {task-id}
```
After QA passes, the task transitions to `awaiting_documentation` and
runs documenter + dev in parallel:
## Parallel Execution
| Agent | Action | Flag set |
|-------|--------|----------|
| Documenter | Writes docs files, then `i_documented(task_id, notes, files)` | `docs_complete=True` |
| Developer | (already done by the time we get here) | `pr_created=True` |
In `awaiting_documentation`:
Task transitions to `awaiting_pm_review` when both are true.
| Agent | Action | Flag |
|-------|--------|------|
| Developer | Creates PR | `pr_created=True` |
| Documenter | Writes docs | `docs_complete=True` |
## PM Merges via `complete`
Task advances to `awaiting_pm_review` when BOTH are done.
After `awaiting_pm_review`, the Cell PM calls `complete(task_id, notes)`.
The choreographer:
1. Verifies all subtasks are in a terminal state
2. Verifies the PR is reviewable
3. Merges the leaf PR into the parent branch (squash by default)
4. Transitions the task to `completed`
For the root parent, **Main PM**'s `complete` opens the master PR and
escalates to CEO via `escalate_to_ceo` semantics.
There is no `roboco_git_merge_pr` MCP tool.
## Prerequisites
- **Git token configured**: Project must have a GitHub PAT set
- Token must have `repo` scope for PR creation
- If missing, error: "Project has no git token configured"
- **Git token:** the project must have an encrypted GitHub PAT set on
`projects.git_token_encrypted`. Without it, the workspace clone — and
therefore everything downstream — fails with `WorkspaceError`.
- **Token scope:** `repo` (for branch push, PR create, PR merge).
- **Default branch:** `projects.default_branch` is the merge target for
the master PR (typically `master`).
## Before Creating PR
## Troubleshooting
1. Push all commits: `roboco_git_push()`
2. Verify tests pass
3. Ensure code quality checks pass
4. Branch is up to date with target
## PM Merges PR
After completing task:
```python
roboco_git_merge_pr(
project_slug="roboco",
pr_number=123,
merge_method="squash" # or "merge", "rebase"
)
```
Only PM can merge PRs.
- `NO_COMMITS` on `open_pr` → call `commit(...)` first; nothing to
open a PR over.
- `NO_PR` on `pass`/`fail` → the choreographer didn't open a PR; check
the workspace state with `roboco_git_status` and re-call
`open_pr` once the workspace is clean.
- `FORCE_PUSH_FORBIDDEN` → only the CEO may force-push. If your branch
diverged, `unclaim` and re-`claim` the task; the choreographer
rebuilds the branch.
+86 -55
View File
@@ -1,93 +1,124 @@
# QA Review Workflow
## When QA Starts
## Preconditions
Task must be in `awaiting_qa` status.
- Task is in `awaiting_qa` status
- The developer's PR is open (the choreographer opened it during their
`open_pr(task_id)` call)
- You are not the original developer of the task (self-review guard)
## QA Review Steps
## Steps
```python
# 1. Claim the task
roboco_task_claim(task_id)
# 1. Pick up an awaiting-QA task
give_me_work()
# 2. Start review
roboco_task_start(task_id)
# 2. Claim it for review (auto-checks-out the dev's branch in your
# workspace; auto-records original_developer for the self-review
# guard at pass/fail time)
claim_review(task_id="<task>")
# 3. Announce to cell
roboco_message_send({
channel: "backend-cell",
content: "Starting QA review of [task title]",
task_id: task_id
})
# 3. Announce to your cell channel (optional, but helpful when QA pulls
# are slow)
say(channel="backend-cell",
text="Starting QA review of <task title>",
task_id="<task>")
# 4. Read developer's journey (REQUIRED)
roboco_journal_read_team(original_developer, task_id=task_id)
# 4. Inspect the diff
roboco_git_diff(project_slug="roboco")
roboco_git_log(project_slug="roboco", branch="<dev's branch>")
# 5. Checkout branch and review
roboco_git_checkout(project_slug, branch_name)
roboco_git_diff(project_slug)
# 5. Run the relevant suite
# Backend: uv run pytest && uv run ruff check . && uv run mypy roboco/
# Frontend: pnpm test && pnpm lint && pnpm typecheck
# 6. Run tests
# Backend: uv run pytest
# Frontend: pnpm test
# 6. Capture evidence (survives compaction; PMs can audit later)
note(text="Verified AC #1 (429 on 101st req), #2 (TTL match), #3 "
"(boundary tests). pytest 1635 passed; ruff clean; mypy clean.",
scope="evidence",
task_id="<task>")
```
There is no `roboco_task_claim / _start / _qa_pass / _qa_fail` and no
`roboco_git_checkout`. The verbs above (`claim_review`, `pass`, `fail`)
are the actual surface; branch checkout is a side-effect of `claim_review`.
## Review Checklist
Before making decision:
- [ ] Read developer's handoff notes
- [ ] Check all acceptance criteria
- [ ] Run tests (must pass)
- [ ] Verify functionality
- [ ] Check code quality
- [ ] Review against standards
Before deciding:
- [ ] Read the dev's notes and journal entries on the task
- [ ] Walk every acceptance criterion against the diff
- [ ] Tests pass on the dev's branch
- [ ] Lint / typecheck clean
- [ ] No layer-separation regressions (routes/ vs services/ etc.)
- [ ] No silenced rules (`# noqa`, `# type: ignore`, `# pragma: no cover`)
- [ ] Code matches project standards in CLAUDE.md
## Passing QA
```python
roboco_task_qa_pass(task_id, {
notes: "All acceptance criteria met. Tests pass. Code follows standards."
})
pass(
task_id="<task>",
notes=(
"All 3 acceptance criteria verified against the diff. "
"pytest 1635 passed; ruff and mypy clean. "
"PR #123."
),
)
```
Result: Task advances to `awaiting_documentation`
Result:
- Task advances to `awaiting_documentation`
- Documenter and the original dev work in parallel from here
- The PR stays open; it will be merged later by the Cell PM via
`complete(task_id, ...)`
## Failing QA
```python
roboco_task_qa_fail(task_id, {
notes: "Issues found during review",
issues: [
"Bug: X doesn't work",
"Missing: Y not implemented"
]
})
fail(
task_id="<task>",
issues=[
"Bug: 100th request also returns 429 — boundary off-by-one.",
"Missing: tests for Redis-down failover path; AC #3 unmet.",
],
)
```
Result:
- Task returns to `needs_revision`
- Assigned back to original developer
- Developer receives notification
- Re-assigned to the original developer (recorded at submit-for-qa time)
- Developer receives a notification
## Before Decision
## Reflect (recommended)
After pass or fail, journal the review for future QA agents to learn from:
Write reflection (REQUIRED):
```python
roboco_journal_reflect({
task_id: task_id,
what_done: "Reviewed X, Y, Z",
what_learned: "Discovered patterns...",
what_struggled: "Edge cases unclear"
})
note(
text=(
"Reviewed task <id>. Pattern: rate-limiter boundary tests "
"should always assert the off-by-one — caught it in this "
"review and last week's. Worth a regression checklist item."
),
scope="reflect",
task_id="<task>",
)
```
## Self-Review Prevention
QA CANNOT review tasks they originally developed.
The system blocks QA from reviewing their own dev work. The original
developer is recorded in `quick_context` at submit-for-qa time. If
`qa_agent_id == original_developer_id`, **all** QA actions on the task
return `not_authorized`:
System tracks `original_developer` in `quick_context`. If QA == original_developer:
- **Claim**: FORBIDDEN
- **Pass**: FORBIDDEN
- **Fail**: FORBIDDEN
- `claim_review` — FORBIDDEN
- `pass` — FORBIDDEN (defence-in-depth even if claim somehow succeeded)
- `fail` FORBIDDEN (same)
This applies to ALL QA actions on the task, not just claiming. The system enforces this at both the API and MCP tool level.
Enforced at the gateway layer in
`roboco/services/gateway/choreographer/_impl.py`.
+6
View File
@@ -59,5 +59,11 @@
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"typescript": "^5"
},
"pnpm": {
"onlyBuiltDependencies": [
"sharp",
"unrs-resolver"
]
}
}
+3
View File
@@ -0,0 +1,3 @@
allowBuilds:
sharp: true
unrs-resolver: true
+8 -5
View File
@@ -904,17 +904,20 @@ async def submit_for_qa(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
"NO_COMMITS: Cannot submit for QA without at least one "
"commit on this task. Use roboco_git_commit() before "
"i_am_done() via gateway, or POST /api/tasks/{id}/submit-qa."
"commit on this task. Use the roboco-do `commit(message, "
"files)` verb before `i_am_done()` via gateway, or POST "
"/api/tasks/{id}/submit-qa."
),
)
if task.pr_number is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
"NO_PR: Cannot submit for QA without a PR. Run "
"roboco_git_push() then roboco_git_create_pr() so QA can "
"review the diff on GitHub."
"NO_PR: Cannot submit for QA without a PR. The PR is "
"opened automatically by the choreographer when you call "
"`submit_for_qa(task_id)` (gateway flow verb) — make sure "
"you have at least one `commit(...)` on this task first "
"so the choreographer has something to push."
),
)
if not task.progress_updates:
+9 -2
View File
@@ -643,8 +643,15 @@ async def enrich_task_with_context(
task_dict = task_response.model_dump()
if include_work_session and hasattr(task_response, "id"):
query = select(WorkSessionTable).where(
WorkSessionTable.task_id == task_response.id
# A task can have multiple work sessions over its lifetime
# (one per claim/unclaim cycle). Pick the most recent so we
# don't crash with MultipleResultsFound on tasks that have
# been re-claimed.
query = (
select(WorkSessionTable)
.where(WorkSessionTable.task_id == task_response.id)
.order_by(WorkSessionTable.created_at.desc())
.limit(1)
)
result = await db.execute(query)
work_session = result.scalar_one_or_none()
+3 -1
View File
@@ -378,7 +378,9 @@ def validate_git_requirements(
message=(
"Blocked: PR not yet created. "
"In awaiting_documentation, Documenter and Developer work in "
"parallel. Wait for Developer to call roboco_git_create_pr()."
"parallel. Wait for the Developer's submit_for_qa(task_id) "
"call to complete — the choreographer opens the PR as part "
"of that transition."
),
)
+8 -4
View File
@@ -532,8 +532,11 @@ class GitService(BaseService):
if current_branch and current_branch != task_branch:
raise ValidationError(
f"BRANCH_MISMATCH: Workspace is on '{current_branch}' but "
f"task requires '{task_branch}'. Use roboco_git_checkout("
f"branch=task_branch) first."
f"task requires '{task_branch}'. Branches are auto-checked-"
f"out when you call `i_will_work_on(task_id)` (devs) or "
f"`i_will_plan(task_id, plan)` (PMs) — call your role's "
f"verb on the right task instead of switching branches by "
f"hand."
)
async def _link_commit_to_task(
@@ -933,8 +936,9 @@ class GitService(BaseService):
action="force_push",
reason=(
"FORCE_PUSH_FORBIDDEN: Force-push is CEO-only. If your "
"branch diverged, roboco_git_checkout a fresh branch "
"and replay your commits."
"branch diverged, `unclaim` the task and re-`claim` it — "
"the choreographer will rebuild the branch and you can "
"replay your commits via `commit(...)`."
),
)
+2 -1
View File
@@ -2698,7 +2698,8 @@ class TaskService(BaseService):
"""
Mark that developer has created a PR for the task.
Called when developer uses roboco_git_create_pr(). This method:
Called by the choreographer when the developer's submit_for_qa()
flow opens the PR. This method:
1. Sets pr_created=True, pr_number, pr_url on the task
2. Checks if docs_complete is also True
3. If both complete, transitions to awaiting_pm_review
Generated
+415 -376
View File
File diff suppressed because it is too large Load Diff