mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Many fixes and cleanups
This commit is contained in:
@@ -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
@@ -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/ ./
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
```
|
||||
|
||||
@@ -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)`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -59,5 +59,11 @@
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"sharp",
|
||||
"unrs-resolver"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
allowBuilds:
|
||||
sharp: true
|
||||
unrs-resolver: true
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -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(...)`."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user