mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* feat(lifecycle): add the in-path PR-review gate status + reviewer verbs
Insert awaiting_pr_review between the assembled-PR submit and the PM merge,
giving the merge level the rejection capability it structurally lacks — today
only qa_fail and ceo_reject ever reach needs_revision, so a PM review is a
merge button with no teeth.
- New Status awaiting_pr_review + submit_for_review / pr_pass / pr_fail actions
(pr_pass -> awaiting_pm_review, pr_fail -> needs_revision, mirroring the QA gate).
- Reviewer verbs claim_gate_review / pr_pass / pr_fail, and a main-PM submit_root
verb (the root analogue of the cell PM's submit_up; opens the root->master PR).
- Extend the self-review-symmetry validator to the new sign-off actions.
- Mirror the value into the ORM TaskStatus enum + the A2A state map, and add the
postgres taskstatus enum value (migration 040, forward-only like 037).
- Regenerate the per-role verb tables; add gate spec tests.
Spec surface only; the gateway methods + dispatch are wired in follow-ups, so the
verbs are advertised but dormant (flow_server tolerates unregistered verbs).
* feat(identity): add the three cell PR-review-gate reviewers
The in-path gate needs a reviewer per cell so each cell's assembled cell->root
PR is reviewed by a stack-specialized agent, while pr-reviewer-1 serves the
root->master gate (and keeps doing inbound external PRs).
- be/fe/ux-pr-reviewer: PR_REVIEWER role, team-scoped (so dispatch routes each
cell's gate to its own reviewer); seeded identities + ROLE_TEAM_RULES + names.
AI agent count 22 -> 25.
- They reuse the existing roboco-agent-pr-reviewer image (AGENT_IMAGES maps the
three slugs to it, as be-dev-1/-2 share one image) — no new image.
- Tracing table: pr_pass/pr_fail require a learning entry (parity with
post_pr_review), submit_root mirrors submit_up, claim_gate_review is waived
(its tracing applies on pr_pass/pr_fail) — completes the verb surface added
in the prior commit.
- Update the roster-pinning identity tests.
* feat(gateway): wire the in-path PR-review gate end to end
Make the assembled-PR review gate operational across the choreographer, the
TaskService transitions, and the v1 flow surface.
- TaskService: submit_for_review (in_progress→awaiting_pr_review), pr_gate_claim
(no-transition reviewer claim), pr_pass (→awaiting_pm_review), pr_fail
(→needs_revision); mirror qa_pass/qa_fail (clear claim, actor-mismatch warn,
issues appended for the PM's revision). VerbRunner gains the matching atomic
handlers + a create_root_pr side effect.
- Repoint submit_up to compose submit_for_review (cell→root PR enters the gate),
and add a main-PM submit_root verb (opens the root→master PR, enters the gate).
- Split main_pm_complete: a code root must pass the gate first (requires
awaiting_pm_review; rejects an in_progress code root toward submit_root and no
longer reopens the PR), while a branchless coordination root still walks
straight through, ungated.
- PRGateMixin (claim_gate_review / pr_pass / pr_fail) composed onto the
Choreographer; flow_server forwarders + v1 routes (pr_reviewer + main_pm) +
request schemas.
- Tests: gate spec + the updated submit_up / main_pm_complete expectations + new
real-DB integration tests driving submit_for_review→pr_gate_claim→pr_pass and
pr_fail through the real enforcement layer.
* feat(orchestrator): dispatch the in-path PR-review gate
Make the gate live in the dispatch loop.
- _dispatch_pr_gate_work: route awaiting_pr_review tasks to reviewers by level —
a cell→root task to its cell reviewer (be/fe/ux-pr-reviewer), the root→master
task to pr-reviewer-1. The reviewer self-claims via claim_gate_review (no
pre-claim, mirroring the external-PR dispatcher); registered in
_dispatch_all_work. _select_agent_for_cell learns the pr_reviewer role.
- _build_pr_gate_prompt: anchors the reviewer to the parent objective + full
acceptance criteria + the FE<->BE contract, then pr_pass / pr_fail.
- _readiness_check_role_for_status: awaiting_pr_review -> pr_reviewer.
- Fail routing: pr_fail reassigns the failed assembled task to its PM
(_revision_pm_for_task: cell PM for a cell team, Main PM for the root), and the
revision dispatcher is generalized from coordination-roots-only to any
PM-owned needs_revision task so the gate-failed task is re-coordinated instead
of deadlocking.
* docs: document the in-path PR-review gate + the cell reviewers (22→25)
Reflect the shipped gate across the canonical + RAG docs.
- CLAUDE.md: agent count 22→25, the cell reviewers in the org chart, an
awaiting_pr_review state + the gate transitions + a gate note in the lifecycle
section, and submit_root / claim_gate_review / pr_pass / pr_fail in the verb
surface table.
- docs/rag/architecture: org-structure (count, cell-reviewer roster, cells
table), agent-uuids (be/fe/ux-pr-reviewer rows), agent-model (role + team
rows).
- docs/rag/roles/pr-reviewer: the in-path gate section + the gate verbs.
- Wrap reviewer.id with UUID(str(...)) in the gate DB tests for mypy.
* docs: finish the gate doc sweep across README + RAG + generated artifacts
Catch the remaining surfaces beyond the canonical docs.
- README + how-to: agent count 22→25, the 6-agent cells (+ PR Reviewer), the
main reviewer's root→master gate role.
- RAG: permissions + tool-permissions + task-tools list the gate verbs
(claim_gate_review / pr_pass / pr_fail) for pr_reviewer; regenerate the
lifecycle artifacts (intent-verbs, status-transitions, the per-role
lifecycle-*.md prompts, panel lifecycle.json) from the spec via
build_lifecycle_artifacts.py so they carry the new status + verbs.
* fix(migration): shorten the 040 revision id to fit alembic_version VARCHAR(32)
The revision id '040_taskstatus_awaiting_pr_review' is 33 chars; alembic's
alembic_version.version_num column is VARCHAR(32), so recording the migration on
a real 'alembic upgrade head' failed with 'value too long for type character
varying(32)' (surfaced on the NAS deploy). The test suite missed it: the test DB
is built via Base.metadata.create_all and the parity test only renders SQL
offline, so nothing actually applied the migration chain.
- Rename to '040_awaiting_pr_review' (22 chars).
- Add a guard test asserting every revision id fits the VARCHAR(32) column.
- Verified by applying the full chain 001->040 against real Postgres: it now
reaches head and records '040_awaiting_pr_review' without truncation.
* fix(migration): land the actual 040 revision-id shortening + guard test
The prior commit captured only the file rename (git add aborted on the deleted
old path), leaving the long revision id and missing the guard test. This commit
carries the real content: revision id '040_awaiting_pr_review' (22 chars) and the
revision-id length guard. Re-verified against real Postgres — the full chain
reaches head and records the short id without truncation.
* fix(product): flush cell deletes before inserts when re-mapping projects
Editing a product's cell->project map (PATCH /api/products/{id}) 409'd with
'duplicate key value violates unique constraint uq_product_projects_product_team'
whenever a team already had a mapping. _replace_cells clears the old rows and
appends the new ones, but within a single flush SQLAlchemy orders INSERTs before
DELETEs for the same table, so the new (product_id, team) rows collided with the
not-yet-deleted old ones. Flush the deletes first.
Pre-existing bug (unrelated to the PR-review gate); surfaced on the NAS. New
real-Postgres regression test re-maps all three cells to different projects —
it fails with the unique violation without the fix and passes with it. The
existing update test only changed WHICH team was mapped, so it never collided.
* fix(gateway): let main_pm submit_root past the shared submit-up guard
submit_root reused the cell PM's _submit_up_ownership_guard, which
hardcoded agent.role != cell_pm and rejected the Main PM with
"submit_up is reserved for cell_pm". A branch-bearing code root could
then never close: submit_root bounced to complete, while complete
required awaiting_pm_review (reachable only via submit_root) and bounced
back — a circular rejection.
Both callers already run the spec gate (can_invoke_intent), which
enforces submit_up→cell_pm and submit_root→main_pm, so the guard's role
re-check was redundant for submit_up and wrong for submit_root. Broaden
it to accept either PM role as a defense-in-depth non-PM reject.
Adds the first choreographer-level submit_root test (the gap that let
this ship).
* fix(gateway): proactively steer both PMs to their bubble-up verb
The submit_root deadlock had a sibling steering gap: nothing told a PM
which verb opens the gate. The delegate next-hint said only 'i_am_idle
when done', and complete's in_progress rejection named submit_root for
the Main PM but left the Cell PM with a bare 'not ready for completion'
— no submit_up pointer, the same guess-the-verb trap.
- delegate hint now names the role-correct verb (root → submit_root,
cell parent → submit_up) proactively, before any rejection.
- cell_pm_complete's in_progress rejection now steers to submit_up,
mirroring the Main PM's submit_root gate hint.
Tests cover both the cell-PM steer and the role-aware delegate hint.
* docs: correct who-merges-which-PR across the gate docs + complete description
Audit of the gate docs found the merge actors mis-stated in several
places — the exact ambiguity that risks 'the reviewer/PM merges the root
PR' confusion:
- complete IntentSpec description said 'Main PM merges root PR' — false;
main_pm_complete escalates and the CEO merges root→master. Corrected
(propagated to intent-verbs.md, lifecycle.json, generated role prompts
via build_lifecycle_artifacts.py).
- task-tools.md: submit_up target was awaiting_pm_review (should be
awaiting_pr_review); Main PM flow had no submit_root — added it.
- README.md: lifecycle diagram now shows the awaiting_pr_review gate.
- cell-pm.md / main-pm.md: dropped the stale 'submit_up hands work to the
Main PM who merges your cell branch' model — the cell PM merges its own
gated cell→root PR; the Main PM owns the root + submit_root; the CEO
merges master. Added submit_root to the main-pm manifest.
- git-commits.md, pr-creation.md, tool-permissions.md, git-tools.md:
stopped attributing root→master PR opening to complete (it's submit_root).
No behavior change; verb wiring + state machine verified gap-free this
session (the pr_fail→needs_revision→PM respawn loop closes correctly).
* fix(orchestrator): stop closure respawn waiting the reaper window
A PM that finished its subtasks and idled left its parent 'paused' with a
fresh last_heartbeat_at. _is_recently_paused gated closure respawn on
_claim_heartbeat_ttl — the REAPER window (stale_claim_reap_seconds: 600s
default, 1800s on the NAS) — so the parent sat untouched for up to 10-30
minutes before its PM was respawned to close it. The whole chain stalled
behind it.
The race that guard actually protects against (i_am_idle auto-pauses, then
the agent is marked IDLE + its container tears down) is seconds, and the
live-session case is already covered by _is_agent_active. Introduce a
dedicated short debounce (pm_closure_recently_paused_seconds, default 45s)
and gate closure on that instead.
The existing test fixture masked this by setting _claim_heartbeat_ttl to
claim_stale_seconds (180s), not the production reaper value. Fixture now
mirrors production; adds a regression test that a parent paused past the
debounce but within the reaper window respawns immediately.
* feat(gate): post the in-path review verdict on the assembled PR
The in-path gate previously left no trace on the PR it gated — pr_pass /
pr_fail were pure status transitions. Now each verdict is posted as a
GitHub review on the assembled PR itself (server-side, bot account), so
the decision is visible on the very PR the PM merges.
- pr_pass → APPROVE, pr_fail → REQUEST_CHANGES on a cell→root PR.
- The root→master PR ALWAYS gets a plain COMMENT, never APPROVE/REQUEST_
CHANGES: only the CEO acts on master, so the gate must never leave an
approval that could satisfy branch protection (letting someone else
merge) nor a blocking review that could impede the CEO's merge.
- Best-effort and AFTER the DB transition — a GitHub failure is logged,
never rolls back the gate decision. Reuses git.post_pr_review's existing
self-review→COMMENT downgrade for the org's own PRs.
Adds _project_slug_for to the ChoreographerHelpers protocol (mypy) and a
unit suite covering event selection, the master-bound COMMENT rule, the
no-PR skip, and failure-swallowing. Docs updated (pr-reviewer, task-tools).
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
529 lines
27 KiB
Markdown
529 lines
27 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Licensing
|
|
|
|
RoboCo is licensed under **AGPL-3.0** (see `LICENSE`). Copyright (c) 2026 Renzo Franceschini. Do NOT reintroduce an MIT or other license reference anywhere (README, headers, package metadata) — the project is AGPL.
|
|
|
|
Contributions require a signed **Contributor License Agreement** (`CLA.md`), automated via the CLA Assistant workflow (`.github/workflows/cla.yml`). The CLA preserves the option to dual-license / offer a commercial edition later; keep copyright assignment language intact. See `CONTRIBUTING.md`.
|
|
|
|
## Project Overview
|
|
|
|
**RoboCo** is an AI Agentic Company - a virtual organization of 25 AI agents + 1 human CEO, designed to operate as a complete software development workforce. The system implements a structured organizational hierarchy with formal communication protocols, task management, and quality controls.
|
|
|
|
### Core Architecture
|
|
|
|
```
|
|
CEO (Renzo - Human)
|
|
|
|
|
+-- Intake (on-demand interviewer: chats only with the CEO to draft a task)
|
|
+-- Secretary (on-demand chief-of-staff: reads company state, runs gated CEO directives)
|
|
+-- PR Reviewer (read-only: the main reviewer — inbound external/fork + internal PRs, and the root→master in-path gate)
|
|
|
|
|
+-- Board (3 agents)
|
|
+-- Product Owner
|
|
+-- Head of Marketing
|
|
+-- Auditor (silent observer, reports to CEO)
|
|
|
|
|
+-- Main PM (coordinates all cells)
|
|
|
|
|
+-- Backend Cell (6 agents: 2 Devs, 1 QA, 1 PM, 1 Documenter, 1 PR Reviewer)
|
|
+-- Frontend Cell (6 agents: 2 Devs, 1 QA, 1 PM, 1 Documenter, 1 PR Reviewer)
|
|
+-- UX/UI Cell (6 agents: 2 Devs, 1 QA, 1 PM, 1 Documenter, 1 PR Reviewer)
|
|
```
|
|
|
|
### Hardware Infrastructure
|
|
|
|
- **Olares One (Powerhouse)**: Intel Ultra 9 + RTX 5090, runs Claude Code instances and AI inference - NOT YET ARRIVED
|
|
- **UGREEN NAS (Warehouse)**: 36TB RAID6, 128GB RAM, hosts PostgreSQL, Redis
|
|
- **Pi Cluster (Operations)**: Monitoring, notifications, smart home
|
|
|
|
## Development Standards
|
|
|
|
### Python (Backend)
|
|
```bash
|
|
# Package manager
|
|
uv
|
|
|
|
# Before any commit
|
|
uv run ruff format .
|
|
uv run ruff check .
|
|
uv run mypy roboco/
|
|
uv run pytest
|
|
|
|
# Coverage target: 80%
|
|
```
|
|
|
|
### TypeScript (Frontend)
|
|
```bash
|
|
# Package manager
|
|
pnpm
|
|
|
|
# Before any commit
|
|
pnpm format
|
|
pnpm lint
|
|
pnpm typecheck
|
|
pnpm test
|
|
|
|
# Coverage target: 80%
|
|
```
|
|
|
|
## Technology Stack
|
|
|
|
| Layer | Technology |
|
|
|-------|------------|
|
|
| API Framework | FastAPI |
|
|
| Database | PostgreSQL + asyncpg |
|
|
| Vector Store | PostgreSQL + pgvector (in-house engine) |
|
|
| RAG Engine | in-house (asyncpg + pgvector, hybrid retrieval) |
|
|
| Cache/Queue | Redis |
|
|
| Container Runtime | Docker + Docker Compose |
|
|
| Cloud LLM | Claude API (claude-opus-4-6) + xAI Grok (official `grok` CLI, SuperGrok subscription) |
|
|
| Local LLM | Ollama (glm-5:cloud for RAG/hybrid retrieval) |
|
|
| Embeddings | qwen3-embedding:0.6b (1024 dim) |
|
|
| Frontend | Next.js 16 + TypeScript + Tailwind + Radix UI (in `panel/`) |
|
|
| Edge / Proxy | nginx (single entry point on port 3000) |
|
|
|
|
## Multi-Agent Workspace Structure
|
|
|
|
Each agent gets their own git clone of a project, enabling parallel development without conflicts:
|
|
|
|
```
|
|
{ROBOCO_WORKSPACES_ROOT}/ # Default: /data/workspaces
|
|
+-- {project-slug}/
|
|
+-- {team}/
|
|
+-- {agent-slug}/
|
|
+-- [git repository]
|
|
```
|
|
|
|
**Example:**
|
|
```
|
|
/data/workspaces/
|
|
+-- roboco/
|
|
+-- backend/
|
|
| +-- be-dev-1/ # be-dev-1's workspace
|
|
| +-- be-dev-2/ # be-dev-2's workspace
|
|
+-- frontend/
|
|
+-- fe-dev-1/
|
|
+-- fe-dev-2/
|
|
```
|
|
|
|
Note: the Next.js control panel now lives at `roboco/panel/` inside this repo (no longer a separate `roboco-panel` project or workspace).
|
|
|
|
**Key Configuration (roboco/config.py):**
|
|
- `ROBOCO_WORKSPACES_ROOT`: Root directory for workspaces (default: `/data/workspaces`)
|
|
- `ROBOCO_WORKSPACE_AUTO_CLONE`: Auto-clone repos on first access (default: `true`)
|
|
- `ROBOCO_WORKSPACE_CLONE_TIMEOUT`: Clone timeout in seconds (default: `300`)
|
|
|
|
On a Python workspace, `WorkspaceService` runs `uv sync --extra dev` (not plain `uv sync`) so the clone's `.venv` carries the full gate toolchain (ruff/mypy/xenon/pytest) — the lint/type/complexity tools live in the `dev` **extra**, which plain `uv sync` skips. Without it an agent's `make quality` fails on `ruff: command not found` and the agent can't gate its own work.
|
|
|
|
## Git Workflow
|
|
|
|
### Branch Naming Convention
|
|
|
|
Branch names follow the pattern: `{type}/{team}/{task-hierarchy}`
|
|
|
|
**Types:** `feature`, `bug`, `chore`, `docs`, `hotfix`
|
|
|
|
**Task Hierarchy:** Uses `--` separator (not `/`) to avoid git ref conflicts.
|
|
|
|
**Examples:**
|
|
- Root task: `feature/backend/ABC12345`
|
|
- Subtask: `feature/backend/ABC12345--DEF67890`
|
|
- Sub-subtask: `feature/backend/ABC12345--DEF67890--GHI11111`
|
|
|
|
### Commit Format
|
|
|
|
Commits are automatically prefixed with the task ID:
|
|
|
|
```
|
|
[{task-id[:8]}] {message}
|
|
```
|
|
|
|
**Example:**
|
|
```
|
|
[ABC12345] Add user authentication endpoint
|
|
```
|
|
|
|
### Work Sessions
|
|
|
|
When a developer claims a task, a **WorkSession** is created that tracks:
|
|
- Branch name and base/target branches
|
|
- All commits made during the session
|
|
- Files modified
|
|
- PR number/URL when created
|
|
- Merge status and who merged
|
|
|
|
### Git Credentials
|
|
|
|
Git authentication is managed **per-project** through encrypted GitHub PATs:
|
|
|
|
- **Each project stores its own git token** - no global fallback
|
|
- **Tokens are encrypted at rest** using Fernet symmetric encryption
|
|
- **API never exposes tokens** - only returns `has_git_token: boolean`
|
|
- **Self-service via UI** - users set/update tokens in project settings
|
|
|
|
**Project fields:**
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `git_token_encrypted` | Fernet-encrypted GitHub PAT (DB column) |
|
|
| `has_git_token` | Boolean indicator for API responses |
|
|
|
|
**Token flow:**
|
|
1. User creates project in UI, enters GitHub PAT
|
|
2. Token encrypted and stored in `projects.git_token_encrypted`
|
|
3. WorkspaceService decrypts token when cloning repos
|
|
4. GitService decrypts token for PR operations (gh CLI)
|
|
|
|
**HTTPS URLs require tokens** - attempting to clone without a token will raise `WorkspaceError`.
|
|
|
|
## Task Lifecycle
|
|
|
|
### Task States
|
|
|
|
The complete task lifecycle is defined in `roboco/foundation/policy/lifecycle.py` (`roboco/enforcement/task_lifecycle.py` is a backwards-compat shim over it):
|
|
|
|
```
|
|
backlog -> pending -> claimed -> in_progress -> [blocked|paused] -> verifying
|
|
| |
|
|
v v
|
|
awaiting_qa <------------------+ awaiting_documentation
|
|
| (needs_revision) | |
|
|
v | v
|
|
awaiting_documentation --------+ awaiting_pm_review
|
|
| |
|
|
v v
|
|
awaiting_pm_review awaiting_ceo_approval
|
|
| |
|
|
v v
|
|
completed completed
|
|
```
|
|
|
|
**In-path PR-review gate** (`awaiting_pr_review`): each assembled PR is reviewed before the PM merges. The cell PM's `submit_up` opens the cell→root PR and the Main PM's `submit_root` opens the root→master PR; both enter `awaiting_pr_review`, where a reviewer `pr_pass`es it on to `awaiting_pm_review` or `pr_fail`s it back to `needs_revision` — the merge-level reject the PM otherwise lacks. Leaf dev tasks and branchless coordination roots skip the gate.
|
|
|
|
**States:**
|
|
| State | Description |
|
|
|-------|-------------|
|
|
| `backlog` | PM setup phase - dependencies or session setup needed |
|
|
| `pending` | Ready for work - orchestrator can spawn agents |
|
|
| `claimed` | Agent has locked the task |
|
|
| `in_progress` | Active development |
|
|
| `blocked` | External dependency blocking progress |
|
|
| `paused` | Temporarily stopped (can resume) |
|
|
| `verifying` | Self-verification by developer |
|
|
| `needs_revision` | QA or CEO requested changes |
|
|
| `awaiting_qa` | Submitted for QA review — PR must already exist |
|
|
| `awaiting_documentation` | Documentation phase — PR already open from pre-QA; doc writes docs |
|
|
| `awaiting_pr_review` | In-path PR-review gate: a reviewer checks the assembled cell→root / root→master PR before the PM merges (assembled, PR-bearing tasks only) |
|
|
| `awaiting_pm_review` | Docs complete, PM reviews + merges |
|
|
| `awaiting_ceo_approval` | Major tasks escalated for CEO final approval |
|
|
| `completed` | Terminal state - work done and merged |
|
|
| `cancelled` | Terminal state - work cancelled |
|
|
|
|
### Role-Based Transitions
|
|
|
|
All status transitions are validated through the enforcement layer. Key restrictions:
|
|
|
|
| Transition | Allowed Roles |
|
|
|------------|---------------|
|
|
| `backlog` → `pending` (activate) | PM roles only |
|
|
| `pending` → `claimed` (claim) | Role must match task type (QA for awaiting_qa, etc.) |
|
|
| `claimed` → `pending` (unclaim) | Assignee or PM |
|
|
| `awaiting_qa` → `awaiting_documentation` (pass) | QA only |
|
|
| `awaiting_qa` → `needs_revision` (fail) | QA only |
|
|
| `awaiting_documentation` → `awaiting_pm_review` | Documenter or Developer (parallel completion) |
|
|
| `in_progress` → `awaiting_pr_review` (submit_up / submit_root) | PM roles (opens the assembled cell→root / root→master PR) |
|
|
| `awaiting_pr_review` → `awaiting_pm_review` (pr_pass) | PR reviewer only |
|
|
| `awaiting_pr_review` → `needs_revision` (pr_fail) | PR reviewer only |
|
|
| `awaiting_pm_review` → `completed` | PM roles only |
|
|
| `awaiting_pm_review` → `awaiting_ceo_approval` | PM roles only |
|
|
| `awaiting_ceo_approval` → `completed/needs_revision/cancelled` | CEO only |
|
|
| Any → `cancelled` | PM roles only |
|
|
|
|
**Unclaim Operation**: Agents can release claimed tasks back to the pool using `unclaim()`. This transitions `claimed` → `pending` and optionally reassigns to another agent.
|
|
|
|
### Git Integration Requirements
|
|
|
|
All tasks follow git workflow. PR is created BEFORE QA review (not after) so QA can review the real PR diff on GitHub and downstream PM/CEO approval chain off a PR that already exists:
|
|
|
|
1. **claimed -> in_progress**: `branch_name` is auto-set on claim (hierarchical branches)
|
|
2. **verifying -> awaiting_qa** (submit-qa): Requires `self_verified`, `commits`, `pr_number` (PR open), and at least one `progress_updates` entry
|
|
3. **awaiting_qa -> awaiting_documentation** (pass-qa): Requires `pr_number` and substantive QA notes
|
|
4. **awaiting_documentation -> awaiting_pm_review**: Requires `docs_complete=True` (PR already exists from step 2 above)
|
|
5. **awaiting_pm_review -> awaiting_ceo_approval**: Must have `pr_number` set and all subtasks in a terminal state
|
|
|
|
### CEO Approval Workflow
|
|
|
|
Major tasks are escalated to CEO for final approval:
|
|
1. PM reviews and approves, escalates to `awaiting_ceo_approval`
|
|
2. CEO can:
|
|
- **Approve**: Merges PR, task -> `completed`
|
|
- **Request changes**: Task -> `needs_revision`
|
|
- **Cancel**: Task -> `cancelled`
|
|
|
|
## Data Models
|
|
|
|
### Core Models (roboco/models/)
|
|
|
|
| Model | Purpose |
|
|
|-------|---------|
|
|
| `Task` | Atomic unit of work with acceptance criteria |
|
|
| `Project` | Git repository configuration and CI/CD commands |
|
|
| `WorkSession` | Links agent work to task, tracks branch/commits/PR |
|
|
| `Agent` | AI agent with role, team, capabilities |
|
|
| `Session` | Communication session with messages |
|
|
| `Channel` | Team communication channel |
|
|
| `Message` | Extracted message from agent streams |
|
|
| `Notification` | Formal notification requiring acknowledgment |
|
|
| `Journal` | Agent personal log for reflections/learnings |
|
|
|
|
### Task Model Key Fields
|
|
|
|
```python
|
|
# Git configuration (all tasks follow git workflow)
|
|
task_type: TaskType # code, documentation, research, planning, design, administrative
|
|
project_id: UUID # Project this task works on (required)
|
|
branch_name: str # Branch for this task (auto-created on claim)
|
|
work_session_id: UUID # Active work session
|
|
|
|
# PR tracking (parallel execution in awaiting_documentation)
|
|
pr_number: int # GitHub/GitLab PR number
|
|
pr_url: str # Full URL to PR
|
|
docs_complete: bool # Documenter has finished
|
|
pr_created: bool # Developer has created PR
|
|
|
|
# Commits linked to task
|
|
commits: list[CommitRef] # All commits made for this task
|
|
```
|
|
|
|
## Communication Model
|
|
|
|
**Communication** = constant stream (always flowing, logged, observed) **Notifications** = formal signals (require acknowledgment, sent by PMs/Board only)
|
|
|
|
### Channel Structure
|
|
- Cell channels: `#backend-cell`, `#frontend-cell`, `#uxui-cell`
|
|
- Cross-cell: `#dev-all`, `#qa-all`, `#pm-all`, `#doc-all`
|
|
- Management: `#main-pm-board`, `#board-private`
|
|
- Special: `#announcements` (read-only except Board/Main PM), `#all-hands`
|
|
|
|
The Auditor has silent read access to ALL channels.
|
|
|
|
## Key Principles
|
|
|
|
1. **Everything is a task** - All work is tracked and documented
|
|
2. **No work without a task** - Create task record first
|
|
3. **No task without acceptance criteria** - How do we know it's done?
|
|
4. **No closure without documentation** - Future agents need context
|
|
5. **Communication is constant** - Stream reasoning, log everything
|
|
6. **State is sacred** - If interrupted, state must be recoverable
|
|
7. **The Auditor sees all** - Quality monitored silently
|
|
8. **Commits linked to tasks** - Every commit references its task ID
|
|
9. **CEO approves major changes** - Escalation path for important work
|
|
|
|
## Agent Gateway
|
|
|
|
Agents do not call the API or per-domain MCP tools directly. They go through two thin MCP servers (`roboco-flow`, `roboco-do`) backed by the server-side **Choreographer** in `roboco/services/gateway/`. The Choreographer composes the existing services (TaskService, JournalService, GitService, etc.) into intent-verb sequences. Tracing, claim-locking, evidence assembly, and remediation hints are all centralized there.
|
|
|
|
Each agent gets a **spawn manifest** at `/app/tool-manifest.json` listing the verbs its role is allowed to call. The orchestrator builds the manifest from `roboco/services/gateway/role_config.py` and mounts it read-only into the agent container.
|
|
|
|
### Verb surface (canonical source: `lifecycle.intents_for_role`; every role also gets `i_am_idle`)
|
|
|
|
| Role | Flow verbs (beyond `i_am_idle`) |
|
|
|---------------|--------------------------------------------------------------------------------------------------|
|
|
| developer | `give_me_work`, `i_will_work_on`, `open_pr`, `i_am_done`, `i_am_blocked`, `resume`, `unclaim` |
|
|
| qa | `give_me_work`, `claim_review`, `pass_review`, `fail_review`, `i_am_blocked`, `resume`, `unclaim` |
|
|
| documenter | `give_me_work`, `claim_doc_task`, `i_documented`, `i_am_blocked`, `resume`, `unclaim` |
|
|
| cell_pm | `give_me_work`, `i_will_plan`, `delegate`, `complete`, `submit_up`, `triage`, `unblock`, `escalate_up`, `reassign`, `resume`, `unclaim` |
|
|
| main_pm | `give_me_work`, `i_will_plan`, `delegate`, `complete`, `submit_root`, `triage`, `triage_all`, `unblock`, `escalate_up`, `escalate_to_ceo`, `resume`, `unclaim` |
|
|
| pr_reviewer | `give_me_work`, `claim_pr_review`, `post_pr_review` (inbound external/fork PRs), `claim_gate_review`, `pr_pass`, `pr_fail` (in-path assembled-PR gate) |
|
|
| product_owner | `triage`, `escalate_to_ceo` |
|
|
| head_marketing| `triage`, `escalate_to_ceo` |
|
|
| auditor | `triage` (read-only — no `say`/`dm`) |
|
|
| prompter | (none beyond `i_am_idle` — not a delivery-lifecycle role; intake interviewer, human-only) |
|
|
| secretary | (none beyond `i_am_idle` — human-only chief-of-staff; reads company state + runs gated CEO directives) |
|
|
|
|
Content tools (do_server) — most roles: `commit`, `note`, `say`, `dm`, `evidence`. Auditor is restricted to `note` (scope=reflect) + `evidence`. The `pr_reviewer` posts its change-request on the PR itself (no agent comms). The `prompter` (intake) and `secretary` are restricted to `note` + `evidence` — human-only, no `say`/`dm`/`notify`.
|
|
|
|
### MCP servers running per agent container
|
|
|
|
| Server | Purpose |
|
|
|----------------------|----------------------------------------------------------------------|
|
|
| `roboco-flow` | Intent verbs (give_me_work, i_am_done, claim_review, complete, ...) |
|
|
| `roboco-do` | Content tools (commit, note, say, dm, evidence) |
|
|
| `roboco-git-readonly`| Read-only git: status, log, diff, branches |
|
|
| `roboco-optimal` | RAG: `roboco_ask_mentor`, `roboco_kb_search` |
|
|
| `roboco-docs` | Project docs file management (selected roles) |
|
|
|
|
Every verb returns a standardized **Envelope**:
|
|
- ok: `{status, task_id, next, evidence?, context_briefing}`
|
|
- error: `{error, message, remediate, missing}`
|
|
|
|
The `next` field tells the agent what to call next; the `remediate` field on errors tells them exactly how to fix and retry. Agents should not guess state — trust the response.
|
|
|
|
## Agent Providers
|
|
|
|
Agent backends are pluggable. `roboco/llm/providers/` defines an `AgentProvider` lifecycle ABC (`base.py`) and a `ProviderRegistry` keyed by `ModelProvider` (`registry.py`), with `ClaudeCodeProvider` (default) and `GrokCliProvider`. The orchestrator resolves a provider at spawn from the agent's `ModelProvider`; when no dedicated provider is registered it falls back to the built-in Claude Code spawn. `ModelProvider` (`roboco/models/base.py`) is `ANTHROPIC` (default), `GROK`, `LOCAL`, `OLLAMA_CLOUD`, `OPENAI` (reserved). The seam is additive: only `GROK` routes through `GrokCliProvider`; Anthropic / Ollama Cloud / self-hosted spawns are unchanged, and every provider gets the same MCP gateway + tool-manifest wiring by construction.
|
|
|
|
**Grok runtime.** `GROK` agents run xAI's official `grok` CLI (model `grok-build`) authenticated by a **SuperGrok subscription**, not a metered API key — so a Grok workforce can't stall mid-task on out-of-credits. The host `~/.grok/auth.json` is mounted **read-only** into each agent (`GrokCliProvider._append_grok_auth_mount`; `ROBOCO_HOST_GROK_DIR` is the host mount source, set up once with `grok login`). It reaches parity with the Claude path by construction: same MCP gateway + manifest, per-role tool-removal and git-operation deny rules, a prompt-injection guard on the task prompt, headless tool auto-approval, and per-agent token/cost capture from the grok session store. It covers both one-shot delivery roles and the interactive Intake (Prompter) and Secretary chats (per-turn `grok -p` with session resume).
|
|
|
|
**Token auto-refresh.** The grok access token has a fixed ~6h server-set TTL and the CLI cannot refresh it headlessly — on an expired token it hangs forever at an interactive login prompt. The orchestrator mints a fresh token from the offline-access refresh token (xAI's OIDC `refresh_token` grant) before expiry and rewrites the shared `auth.json` in place (`roboco/llm/providers/grok_auth.py` `refresh_if_stale`, run once per dispatch tick; the orchestrator's `~/.grok` mount is read-write so it can rewrite it). As a backstop the agent entrypoint runs `python -m roboco.llm.providers.grok_auth --check` and refuses to start (exit 78) on a missing/expired token instead of hanging.
|
|
|
|
## Self-Healing & Feature Flags
|
|
|
|
**Self-healing CI loop (default-off).** RoboCo can watch its own repository's CI (a single named workflow) and, on a detected regression, open a fix task that is held out of dispatch until the CEO approves it (it terminates at `awaiting_ceo_approval`), then dispatch it through the normal delivery flow. It is dormant by default and armed by `ROBOCO_SELF_HEAL_ENABLED` plus a second opt-in `ROBOCO_SELF_HEAL_ORIGINATE_ENABLED`; origination is bounded by `ROBOCO_SELF_HEAL_MAX_OPEN_TASKS` / `_MAX_PER_CYCLE` so it can't flood the backlog. It never auto-merges or self-deploys (`roboco/services/self_heal_engine.py`).
|
|
|
|
**Feature flags / company-in-a-box.** Env-gated, default-off subsystems toggle from the panel's Settings → Feature Flags card (`panel/src/components/settings/feature-flags-card.tsx`) instead of hand-editing env: web research (`ROBOCO_RESEARCH_ENABLED`), the strategy engine (`ROBOCO_STRATEGY_ENGINE_ENABLED`), pitch provisioning (`ROBOCO_PROVISIONING_*`), external / internal PR review, and the self-heal flags above. A toggle persists in the settings store and takes effect on the next backend restart; an unset flag falls back to its environment / config default.
|
|
|
|
## Services
|
|
|
|
Core services in `roboco/services/`:
|
|
|
|
| Service | Purpose |
|
|
|---------|---------|
|
|
| `TaskService` | Task CRUD and state transitions |
|
|
| `WorkSessionService` | Git session management, PR lifecycle |
|
|
| `WorkspaceService` | Multi-agent workspace resolution and cloning |
|
|
| `ProjectService` | Project/repository management |
|
|
| `MessagingService` | Channels, sessions, messages |
|
|
| `NotificationService` | Formal notifications |
|
|
| `JournalService` | Agent journals and entries |
|
|
| `OptimalService` | RAG queries (in-house pgvector engine) |
|
|
| `PermissionsService` | Role-based access control |
|
|
|
|
## Configuration
|
|
|
|
Key settings in `roboco/config.py` (env prefix: `ROBOCO_`):
|
|
|
|
```bash
|
|
# Database
|
|
ROBOCO_DATABASE_HOST=localhost
|
|
ROBOCO_DATABASE_PORT=5432
|
|
ROBOCO_DATABASE_USER=roboco
|
|
ROBOCO_DATABASE_PASSWORD=roboco
|
|
ROBOCO_DATABASE_NAME=roboco
|
|
|
|
# Redis
|
|
ROBOCO_REDIS_HOST=localhost
|
|
ROBOCO_REDIS_PORT=6379
|
|
|
|
# Security (REQUIRED)
|
|
# Generate with: python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'
|
|
ROBOCO_ENCRYPTION_KEY=<your-fernet-key>
|
|
|
|
# Workspaces
|
|
ROBOCO_WORKSPACES_ROOT=/data/workspaces
|
|
ROBOCO_WORKSPACE_AUTO_CLONE=true
|
|
ROBOCO_WORKSPACE_CLONE_TIMEOUT=300
|
|
|
|
# RAG (in-house pgvector engine)
|
|
ROBOCO_RAG_CHUNK_STRATEGY=fixed
|
|
ROBOCO_RAG_CHUNK_SIZE=512
|
|
ROBOCO_RAG_USE_HYDE=true
|
|
ROBOCO_RAG_USE_HYBRID_SEARCH=true
|
|
|
|
# AI/LLM
|
|
ROBOCO_DEFAULT_EMBEDDING_MODEL=qwen3-embedding:0.6b
|
|
ROBOCO_LOCAL_LLM_MODEL=glm-5:cloud
|
|
ROBOCO_LOCAL_LLM_BASE_URL=http://roboco-ollama:11434/v1
|
|
ROBOCO_OLLAMA_BASE_URL=http://roboco-ollama:11434
|
|
```
|
|
|
|
## Docker Deployment
|
|
|
|
### Container Architecture
|
|
|
|
The system runs as Docker Compose services. All Dockerfiles live under `docker/` at the project root; every service uses `context: .` plus `dockerfile: docker/<name>.Dockerfile`.
|
|
|
|
| Service | Purpose | Healthcheck |
|
|
|---------|---------|-------------|
|
|
| `postgres` | PostgreSQL + pgvector | `pg_isready` |
|
|
| `redis` | Cache, sessions, event bus | `redis-cli ping` |
|
|
| `ollama` | Local LLM + embeddings | `ollama list` |
|
|
| `ollama-init` | Pulls models on startup | One-shot |
|
|
| `agent-base-image` / `agent-*-image` | Pre-built images spawned per agent | One-shot |
|
|
| `orchestrator` | API + agent spawner | Depends on all above |
|
|
| `panel` | Next.js control panel (internal, port 3000) | — |
|
|
| `nginx` | Reverse proxy fronting panel + orchestrator | — |
|
|
|
|
### Single Entry Point
|
|
|
|
`nginx` is the only externally-exposed service. It listens on `localhost:3000` and routes:
|
|
|
|
- `/api/*` and `/ws/*` → `orchestrator:8000`
|
|
- everything else → `panel:3000`
|
|
|
|
This avoids CORS since the browser sees one origin. The Next.js code uses relative URLs (`/api`, `/ws`) and lets nginx do the dispatch.
|
|
|
|
### WebSocket streams
|
|
|
|
The orchestrator exposes WebSocket endpoints under `/ws` (router in `roboco/api/websocket.py`, `ConnectionManager` + `broadcast_*` helpers):
|
|
|
|
| Endpoint | Purpose |
|
|
|----------|---------|
|
|
| `/ws/channels/{id}`, `/ws/agents/{id}`, `/ws/sessions/{id}`, `/ws/notifications/{id}` | Per-resource live streams |
|
|
| `/ws/system` | Operator/system-wide stream (no per-agent keying) — the rate-limit lifecycle (`RATE_LIMIT_HIT` / `RATE_LIMIT_LIFTED`) and live usage (`USAGE_SNAPSHOT`, pushed to the usage dashboard) |
|
|
|
|
Server-side events reach these sockets through `roboco/api/websocket_bridge.py`, which subscribes to the `StreamEventBus` and forwards each event to the matching connections. To add a new live event: define an `EventType` (dotted value), publish it to the bus, add a `_handle_*` forwarder in `websocket_bridge`, and consume it on the panel via the `useWebSocket("/<endpoint>", …)` hook — do not stand up a parallel endpoint or client stack.
|
|
|
|
### Rate limiting & usage
|
|
|
|
- **Provider rate limits** are tracked in Redis (`RateLimitStateTracker`, `roboco/services/gateway/`). On a provider 429 an agent calls `i_am_blocked(reason="rate_limited")`; the spawn gate then **queues** (never drops) further work for that provider, and a background probe-and-resume loop in the orchestrator clears the limit and revives parked agents when it lifts.
|
|
- **Token usage** is captured per agent session from the Claude Code transcript via the SDK server's `/usage/sync` (hook → orchestrator finalize → `agent_spawn_sessions` → `daily_usage_rollups` → dashboard). Cost uses provider-aware pricing in `roboco/billing/pricing.py` (Anthropic priced; local/Ollama intentionally `$0`). The token sweep also publishes `USAGE_SNAPSHOT` to `/ws/system`, so the dashboard's "Token Usage & Cost" panel updates live and falls back to HTTP polling when the stream is down.
|
|
|
|
### Startup Sequence
|
|
|
|
The startup order is critical due to dependencies:
|
|
|
|
```
|
|
postgres ──┐
|
|
redis ─────┼──> ollama ──> ollama-init ──> orchestrator ──> panel ──> nginx
|
|
│ │ │
|
|
│ │ └── Pulls qwen3-embedding:0.6b, glm-5:cloud
|
|
│ └── Healthcheck: ollama list
|
|
└── Healthcheck: pg_isready, redis-cli ping
|
|
```
|
|
|
|
**Important timing notes:**
|
|
1. `ollama-init` pulls models (~30s for embedding model, ~2min for LLM)
|
|
2. Orchestrator waits for models before starting
|
|
3. FastAPI lifespan indexes documents using Ollama (~30-60s)
|
|
4. Orchestrator polls `/health` until API is ready before starting dispatcher
|
|
5. After orchestrator is up, `panel` (Next.js) builds/starts, then `nginx`
|
|
|
|
### Database migrations
|
|
|
|
Schema changes ship as Alembic migrations under `alembic/versions/`. Run:
|
|
|
|
```bash
|
|
docker compose exec orchestrator alembic upgrade head
|
|
```
|
|
|
|
after pulling any change that adds a new migration.
|
|
|
|
### Ollama Configuration
|
|
|
|
Ollama provides two APIs:
|
|
- `/v1/*` - OpenAI-compatible API (for LLM chat/completion)
|
|
- `/api/*` - Native Ollama API (for embeddings, model management)
|
|
|
|
The embedder uses `/api/embed` endpoint with the `qwen3-embedding:0.6b` model.
|
|
|
|
**Environment variables for Docker:**
|
|
```bash
|
|
ROBOCO_LOCAL_LLM_BASE_URL=http://roboco-ollama:11434/v1 # OpenAI-compat
|
|
ROBOCO_OLLAMA_BASE_URL=http://roboco-ollama:11434 # Native API
|
|
```
|
|
|
|
### Common Issues
|
|
|
|
| Symptom | Cause | Fix |
|
|
|---------|-------|-----|
|
|
| `404 /api/embed` | Model not pulled | Check `docker logs roboco-ollama-init` |
|
|
| `All connection attempts failed` | API not ready | Orchestrator starts before FastAPI lifespan completes |
|
|
| Healthcheck failing | Wrong endpoint | Use `ollama list` not `curl` |
|
|
|
|
## Blueprint Reference
|
|
|
|
The organizational structure, communication matrix, role descriptions, and access-control model are documented inline above and in the published `docs/` tree (per-area `README.md` files, `usage.md`, `deployment.md`).
|