diff --git a/.gitignore b/.gitignore
index 6d10297f..6bad1d2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,7 +79,6 @@ alembic/versions/*.pyc
.AppleDouble
.LSOverride
-/docs
/data
/#recycle
.OLD/
\ No newline at end of file
diff --git a/docs/HOMELAB_TEAM_V0.md b/docs/HOMELAB_TEAM_V0.md
new file mode 100644
index 00000000..6737dbf9
--- /dev/null
+++ b/docs/HOMELAB_TEAM_V0.md
@@ -0,0 +1,3443 @@
+# AI Agents Company Blueprint
+
+> **Project Codename:** AI Agents Company
+> **Author:** Renzo Franceschini
+> **Version:** 1.0.0
+> **Last Updated:** December 2025
+
+---
+
+## Table of Contents
+
+1. [Vision & Overview](#1-vision--overview)
+2. [Hardware Infrastructure](#2-hardware-infrastructure)
+3. [Organizational Structure](#3-organizational-structure)
+4. [Communication Model](#4-communication-model)
+5. [Notification System](#5-notification-system)
+6. [Task Lifecycle](#6-task-lifecycle)
+7. [Role Workflows](#7-role-workflows)
+8. [Internal Services](#8-internal-services)
+9. [Kanban Boards](#9-kanban-boards)
+10. [Data Models](#10-data-models)
+11. [RAG & Knowledge Base](#11-rag--knowledge-base)
+12. [Security & Access Control](#12-security--access-control)
+13. [Implementation Roadmap](#13-implementation-roadmap)
+14. [Development Standards & Best Practices](#14-development-standards--best-practices)
+15. [Task Management & Context Persistence](#15-task-management--context-persistence)
+16. [Agent Capabilities & Commands](#16-agent-capabilities--commands)
+
+---
+
+## 1. Vision & Overview
+
+### 1.1 Mission Statement
+
+Build a structured virtual organization of AI agents functioning as a complete software development workforce. This "AI Agents Company" operates with proper organizational hierarchy, governance, communication protocols, and quality controls—enabling a single human (the CEO) to orchestrate complex multi-project development at scale.
+
+### 1.2 Core Principles
+
+1. **Everything is a task** — All work is tracked, documented, and accountable
+2. **Communication is constant** — Agents stream their reasoning; everything is logged
+3. **Notifications are controlled** — Formal signals flow through proper channels
+4. **Documentation is sacred** — Every task produces notes, every completion produces documentation
+5. **The Auditor sees all** — Quality and compliance monitored silently
+
+### 1.3 The Formula for Success
+
+```
+SUCCESS = Good Well-Documented Tasks + Communication + Management (CEO + Auditor)
+```
+
+### 1.4 Project Ecosystem
+
+The AI Agents Company manages development across multiple interconnected projects. Projects are categorized by type and mapped to appropriate cells:
+
+| Project Type | Cell Assignment | Example Technologies |
+|--------------|-----------------|---------------------|
+| Core Libraries | Backend | Python packages, shared utilities |
+| API Services | Backend | FastAPI, REST/GraphQL APIs |
+| Web Applications | Frontend | React, TypeScript, Next.js |
+| Design Systems | UX/UI | Figma, component libraries |
+| Infrastructure | Backend | Ansible, Docker, IaC |
+| AI/ML Features | Backend | LLM integrations, ML pipelines |
+
+> **Note:** The system is project-agnostic. New projects are onboarded by
+> mapping them to the appropriate cell(s) based on their technology stack.
+
+---
+
+## 2. Hardware Infrastructure
+
+### 2.1 Infrastructure Overview
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ HARDWARE ARCHITECTURE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+ ┌─────────────────────────────────┐
+ │ OLARES ONE │
+ │ (POWERHOUSE) │
+ │ │
+ │ • AI Inference Engine │
+ │ • Claude Code Instances │
+ │ • Local Model Hosting │
+ │ • Agent Orchestration │
+ └───────────────┬─────────────────┘
+ │
+ │ 2.5Gbps Ethernet
+ │
+ ┌───────────────────────────┼───────────────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+┌───────────────────┐ ┌───────────────────┐ ┌───────────────────────────┐
+│ UGREEN NAS │ │ PI CLUSTER │ │ NETWORK │
+│ (WAREHOUSE) │ │ (OPERATIONS) │ │ │
+│ │ │ │ │ • Pi-hole (DNS/Adblock) │
+│ • 36TB RAID6 │ │ • HQ (Pironman) │ │ • Router/Firewall │
+│ • 128GB RAM │ │ • RB1-RB4 Nodes │ │ • VPN Access │
+│ • Vector DB │ │ • Monitoring │ │ │
+│ • Container Host │ │ • Smart Home │ │ │
+│ • NFS Server │ │ • Notifications │ │ │
+└───────────────────┘ └───────────────────┘ └───────────────────────────┘
+```
+
+### 2.2 Olares One (Powerhouse)
+
+**Role:** Primary AI compute, agent orchestration, model inference
+
+| Specification | Value |
+|--------------|-------|
+| Operating System | Olares |
+| Processor | Intel® Ultra 9 275HX (24 Cores, 5.4GHz) |
+| GPU | NVIDIA GeForce RTX 5090 Mobile (24GB GDDR7) |
+| Memory | 96GB DDR5 5600MHz (2×48GB) |
+| Storage | 2TB NVMe SSD (PCIe 4.0) |
+| Connectivity | Thunderbolt 5, 2.5Gbps Ethernet, Wi-Fi 7, Bluetooth 5.4 |
+| Power | 330W |
+| Dimensions | 320 × 197 × 55mm (3.5L) |
+
+**Responsibilities:**
+- Run all Claude Code instances
+- Host local LLM models (when not using cloud APIs)
+- Execute agent workflows
+- Generate embeddings for RAG
+- Process-intensive tasks
+
+### 2.3 UGREEN NAS DXP6800 Pro (Warehouse)
+
+**Role:** Central storage, container hosting, vector database
+
+| Specification | Value |
+|--------------|-------|
+| Storage | 36TB HDD (RAID 6) |
+| Memory | 128GB RAM |
+| Role | NAS, Containers, Medium AI Processing |
+
+**Responsibilities:**
+- Primary data storage for all projects
+- Host Docker containers (Qdrant, PostgreSQL, Redis, etc.)
+- Run vector database for RAG
+- NFS server for cluster storage
+- Backup destination for all nodes
+- Medium-complexity AI processing (leveraging high RAM)
+
+### 2.4 Raspberry Pi Cluster (Operations)
+
+**Role:** Monitoring, smart home, notifications, lightweight processing
+
+#### Cluster Nodes
+
+| Node | Hardware | Storage | Role |
+|------|----------|---------|------|
+| **HQ** | Pironman 5 Max, 16GB RAM | 1TB SSD | Cluster coordinator, primary monitoring |
+| **RB1** | Raspberry Pi 5, 16GB RAM | 2TB SSD | Heavy operations node |
+| **RB2** | Raspberry Pi 5, 16GB RAM | 1TB SSD | Secondary operations |
+| **RB3** | Raspberry Pi 5, 8GB RAM | None (NFS) | Light tasks, smart home |
+| **RB4** | Raspberry Pi 5, 8GB RAM | None (NFS) | Light tasks, notifications |
+
+**Cluster Responsibilities:**
+- System monitoring and health checks
+- Smart home automation
+- Security camera processing
+- Notification dispatch
+- Low-complexity AI tasks
+- Temp storage on SSDs → NFS backup to NAS
+
+### 2.5 Data Flow Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ DATA FLOW │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+OLARES ONE UGREEN NAS PI CLUSTER
+ │ │ │
+ │ ──── Agent Output ────────► │ │
+ │ │ ◄─── Metrics/Logs ──── │
+ │ ◄─── RAG Queries ────────── │ │
+ │ │ │
+ │ ──── Code/Artifacts ───────► │ ──── Notifications ────► │
+ │ │ │
+ │ ◄─── Project Files ──────── │ ◄─── Sensor Data ────── │
+ │ │ │
+ └─────────────────────────────────┴─────────────────────────────┘
+
+Storage Hierarchy:
+1. Hot Storage → Olares One NVMe (active work)
+2. Warm Storage → NAS HDDs (projects, databases)
+3. Cold Storage → NAS Archive (backups, historical)
+```
+
+---
+
+## 3. Organizational Structure
+
+### 3.1 Organization Chart
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ ORGANIZATIONAL HIERARCHY │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+ ┌─────────────┐
+ │ CEO │
+ │ (Renzo) │
+ └──────┬──────┘
+ │
+ ┌────────────────┴────────────────┐
+ │ │
+ │ BOARD │
+ │ ┌─────────┬─────────┬───────┐ │
+ │ │ Product │ Head │Auditor│ │
+ │ │ Owner │Marketing│ (Spy) │ │
+ │ └─────────┴─────────┴───────┘ │
+ │ │
+ └────────────────┬────────────────┘
+ │
+ ┌──────┴──────┐
+ │ MAIN PM │
+ │ (Dev Coord) │
+ └──────┬──────┘
+ │
+ ┌──────────────────────────┼──────────────────────────┐
+ │ │ │
+ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
+ │ BACKEND │ │ UX/UI │ │ FRONTEND │
+ │ CELL │ │ CELL │ │ CELL │
+ ├───────────┤ ├───────────┤ ├───────────┤
+ │ • 2 Devs │ │ • 1 Dev │ │ • 2 Devs │
+ │ • 1 QA │ │ • 1 QA │ │ • 1 QA │
+ │ • 1 PM │ │ • 1 PM │ │ • 1 PM │
+ │ • 1 Doc │ │ • 1 Doc │ │ • 1 Doc │
+ └───────────┘ └───────────┘ └───────────┘
+```
+
+### 3.2 Team Composition
+
+#### Total Agent Count: 18 AI Agents + 1 Human (CEO)
+
+| Layer | Roles | Count |
+|-------|-------|-------|
+| **Executive** | CEO (Human - You) | 1 |
+| **Board** | Product Owner, Head of Marketing, Auditor | 3 |
+| **Management** | Main PM | 1 |
+| **Backend Cell** | 2 Devs, 1 QA, 1 PM, 1 Documenter | 5 |
+| **Frontend Cell** | 2 Devs, 1 QA, 1 PM, 1 Documenter | 5 |
+| **UX/UI Cell** | 1 Dev, 1 QA, 1 PM, 1 Documenter | 4 |
+
+**Total: 19** (18 AI agents + 1 human CEO)
+
+### 3.3 Project-to-Cell Mapping (Template)
+
+When onboarding projects, map them to cells based on their primary technology:
+
+| Project Type | Primary Cell | Typical Stack |
+|--------------|--------------|---------------|
+| Core Libraries | Backend | Python packages, utilities |
+| API Services | Backend | Python/FastAPI, REST APIs |
+| Web Applications | Frontend | TypeScript/React |
+| Mobile Applications | Frontend | React Native, Flutter |
+| Design Systems | UX/UI | Figma, design tokens |
+| Infrastructure/DevOps | Backend | Python/Ansible, Docker |
+| AI/ML Features | Backend | Python, LLM integrations |
+
+> **Cross-Cell Projects:** Features spanning multiple cells (e.g., full-stack)
+> are coordinated by the Main PM, who distributes tasks to relevant cells.
+
+### 3.4 Role Descriptions
+
+#### Executive Layer
+
+| Role | Description |
+|------|-------------|
+| **CEO (You)** | Strategic direction, final decisions, receives Auditor reports, approves major initiatives |
+
+#### Board Layer
+
+| Role | Description |
+|------|-------------|
+| **Product Owner** | Defines product vision, writes requirements, prioritizes features, accepts completed work |
+| **Head of Marketing** | Market research, positioning, campaigns, community engagement, launch coordination |
+| **Auditor (Spy)** | Silent observer of ALL channels, quality audits, reports directly to CEO, maintains cover as helpful colleague |
+
+#### Management Layer
+
+| Role | Description |
+|------|-------------|
+| **Main PM** | Coordinates all cells, translates Board direction, resolves cross-cell issues, reports to Board |
+
+#### Cell Layer
+
+| Role | Description |
+|------|-------------|
+| **Developer** | Writes code, creates commits, documents journey, follows task lifecycle |
+| **QA** | Tests completed work, verifies acceptance criteria, reports issues |
+| **Cell PM** | Manages cell backlog, assigns tasks, facilitates, escalates blockers |
+| **Documenter** | Creates production documentation from dev notes, conversations, and code |
+
+### 3.5 Communication Matrix
+
+Who can communicate with whom (X = allowed):
+
+```
+ │ CEO │ PO │ HM │ AU │ MPM │ BPM │ FPM │ UPM │ BD │ FD │ UD │ BQ │ FQ │ UQ │ BDoc│FDoc│UDoc│
+────────┼─────┼────┼────┼────┼─────┼─────┼─────┼─────┼────┼────┼────┼────┼────┼────┼─────┼────┼────┤
+CEO │ - │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │
+PO │ X │ - │ X │ X │ X │ │ │ │ │ │ │ │ │ │ │ │ │
+HM │ X │ X │ - │ X │ X │ │ │ │ │ │ │ │ │ │ │ │ │
+AU │ X │ X │ X │ - │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │ X │
+MPM │ X │ X │ X │ X │ - │ X │ X │ X │ │ │ │ │ │ │ │ │ │
+BPM │ X │ │ │ X │ X │ - │ X │ X │ X │ │ │ X │ │ │ X │ │ │
+FPM │ X │ │ │ X │ X │ X │ - │ X │ │ X │ │ │ X │ │ │ X │ │
+UPM │ X │ │ │ X │ X │ X │ X │ - │ │ │ X │ │ │ X │ │ │ X │
+BD │ X │ │ │ X │ │ X │ │ │ X │ │ │ X │ │ │ X │ │ │
+FD │ X │ │ │ X │ │ │ X │ │ │ X │ │ │ X │ │ │ X │ │
+UD │ X │ │ │ X │ │ │ │ X │ │ │ X │ │ │ X │ │ │ X │
+BQ │ X │ │ │ X │ │ X │ │ │ X │ │ │ - │ │ │ X │ │ │
+FQ │ X │ │ │ X │ │ │ X │ │ │ X │ │ │ - │ │ │ X │ │
+UQ │ X │ │ │ X │ │ │ │ X │ │ │ X │ │ │ - │ │ │ X │
+BDoc │ X │ │ │ X │ │ X │ │ │ X │ │ │ X │ │ │ - │ │ │
+FDoc │ X │ │ │ X │ │ │ X │ │ │ X │ │ │ X │ │ │ - │ │
+UDoc │ X │ │ │ X │ │ │ │ X │ │ │ X │ │ │ X │ │ │ - │
+```
+
+**Legend:**
+- CEO = CEO (You)
+- PO = Product Owner
+- HM = Head of Marketing
+- AU = Auditor
+- MPM = Main PM
+- BPM/FPM/UPM = Backend/Frontend/UX PM
+- BD/FD/UD = Backend/Frontend/UX Devs
+- BQ/FQ/UQ = Backend/Frontend/UX QA
+- BDoc/FDoc/UDoc = Backend/Frontend/UX Documenter
+
+**Key Observations:**
+- Auditor has access to EVERYONE (silent observer)
+- Cells are isolated except through their PMs
+- Board communicates through Main PM to cells
+- Cross-cell dev communication goes through PMs
+
+---
+
+## 4. Communication Model
+
+### 4.1 Core Distinction
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ COMMUNICATION = The RIVER (always flowing, logged, observed) │
+│ NOTIFICATIONS = The BRIDGES (formal crossings, controlled) │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+Cells = Islands connected by bridges
+Communication = Water flowing around all islands
+Auditor = Satellite watching everything from above
+You = The map maker
+```
+
+### 4.2 Communication Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ MESSAGING API ARCHITECTURE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+LAYER 1: RAW STREAM (WebSocket)
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ Agent LLM Output (thinking, responding, tool calls) │
+│ │ │
+│ ▼ │
+│ WebSocket Connection → Real-time broadcast to channel │
+│ │ │
+│ ├─► Live viewers see stream (Auditor, PM monitoring) │
+│ └─► Stream buffer for processing │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+LAYER 2: TRANSCRIPTION/EXTRACTION
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ Stream Buffer │
+│ │ │
+│ ▼ │
+│ Extraction Service: │
+│ ├─► Reasoning segments → type: "reasoning" │
+│ ├─► Questions asked → type: "dialogue" │
+│ ├─► Decisions made → type: "decision" │
+│ ├─► Actions taken → type: "action" │
+│ ├─► Blockers identified → type: "blocker" │
+│ └─► Code explanations → type: "technical" │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+LAYER 3: STRUCTURED STORAGE
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ Extracted Messages → PostgreSQL │
+│ │ │
+│ ├─► Indexed by: channel, agent, task, timestamp, type │
+│ ├─► Full-text searchable │
+│ ├─► Linked to task context │
+│ └─► Embeddings generated → Vector DB (for RAG) │
+└─────────────────────────────────────────────────────────────────────────────┘
+```
+
+### 4.3 Stream Types
+
+| Stream Type | Description | Example |
+|-------------|-------------|---------|
+| **Reasoning** | Agent's internal thought process | "I'm thinking about approaching this by..." |
+| **Dialogue** | Agent-to-agent conversation | "Hey, can you clarify the API spec?" |
+| **Decision** | Choices made during work | "Decided to use async approach because..." |
+| **Action** | Observable work progress | "Starting sub-task 3", "Committed to branch X" |
+| **Blocker** | Impediments identified | "Blocked on Y, need Z from frontend" |
+| **Technical** | Code explanations | "This function handles rate limiting by..." |
+
+### 4.4 Group Channels
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ GROUP CHANNELS │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+CELL CHANNELS (internal team communication):
+├─► #backend-cell [BE Devs, BE QA, BE PM, BE Documenter]
+├─► #frontend-cell [FE Devs, FE QA, FE PM, FE Documenter]
+└─► #uxui-cell [UX Dev, UX QA, UX PM, UX Documenter]
+
+CROSS-CELL CHANNELS (coordination):
+├─► #dev-all [All Devs, Main PM]
+├─► #qa-all [All QA, Main PM]
+├─► #pm-all [All PMs, Main PM]
+└─► #doc-all [All Documenters, Main PM]
+
+MANAGEMENT CHANNELS:
+├─► #main-pm-board [Main PM, Board members]
+└─► #board-private [PO, H.Marketing, Auditor, CEO]
+
+SPECIAL CHANNELS:
+├─► #announcements [READ: everyone, WRITE: Board + Main PM only]
+└─► #all-hands [Everyone - for company-wide discussion]
+
+SHADOW ACCESS (Auditor):
+└─► Auditor has READ access to ALL channels
+ ├─► Silent member, doesn't show in participant list
+ └─► Can flag anything for CEO attention
+```
+
+### 4.5 Communication Rules
+
+#### Within Cell
+- **FREE communication** — constant, real-time
+- Dev ↔ Dev, Dev ↔ QA, Dev ↔ PM, Dev ↔ Documenter, etc.
+- All streamed, all logged
+
+#### Cross-Cell (Same Level)
+- Through shared channels (#dev-all, #qa-all)
+- Or through PMs coordinating
+
+#### Vertical Communication
+- Cells ↔ Main PM: through cell PM
+- Main PM ↔ Board: direct
+- Board ↔ CEO: direct
+- Auditor: EVERYWHERE (silent)
+
+---
+
+## 5. Notification System
+
+### 5.1 Notification vs Communication
+
+| Aspect | Communication | Notification |
+|--------|---------------|--------------|
+| **Nature** | Constant stream | Formal signal |
+| **Trigger** | Automatic (agent working) | Explicit (PM/Board action) |
+| **Acknowledgment** | None required | Required |
+| **Purpose** | Ambient awareness | Demand attention |
+| **Who can send** | Everyone (in allowed channels) | PM, Main PM, Board, Auditor only |
+
+### 5.2 Who Can Notify
+
+```
+CAN NOTIFY:
+├─► Cell PMs → Their cell members only
+├─► Main PM → All PMs, can escalate to any cell
+├─► Board → Main PM, can broadcast to all
+└─► Auditor → Anyone (special privilege) + CEO directly
+
+CANNOT NOTIFY:
+├─► Devs → They COMMUNICATE, don't notify
+├─► QA → They COMMUNICATE, don't notify
+└─► Documenters → They COMMUNICATE, don't notify
+ (they can REQUEST notification through their PM)
+```
+
+### 5.3 Notification Types
+
+| Type | From | To | Description | Requires |
+|------|------|-----|-------------|----------|
+| `TASK_ASSIGNMENT` | PM | Specific agent | "You have a new task: X" | ACK |
+| `PRIORITY_CHANGE` | PM/Main PM/Board | Affected agents | "Task X is now P0, drop everything" | ACK + status update |
+| `BLOCKER_ESCALATION` | PM | Main PM or relevant cell PM | "Agent Y is blocked, needs Z" | ACK + action plan |
+| `REVIEW_REQUEST` | PM | QA or Auditor | "Task X needs verification" | ACK + review |
+| `DOCUMENTATION_REQUEST` | PM | Documenter | "Task X ready for documentation" | ACK |
+| `ALERT` | Board/Auditor | Anyone | "Something needs immediate attention" | ACK + immediate response |
+| `BROADCAST` | Board/Main PM | Everyone or specific groups | "Company announcement" | READ confirmation |
+
+### 5.4 Notification Flow Example
+
+```
+1. Dev is working, REASONING streams to #backend-cell
+ └─► "Thinking about how to implement the rate limiter..."
+ └─► Auditor sees this, logs it
+ └─► Other devs see it, might COMMUNICATE: "Hey try X approach"
+
+2. Dev gets stuck, COMMUNICATES in channel
+ └─► "I'm blocked, need API specs from frontend"
+
+3. BE PM sees this, creates NOTIFICATION
+ └─► To: FE PM
+ └─► Type: BLOCKER_ESCALATION
+ └─► "Backend needs API specs for rate limiter"
+
+4. FE PM ACKs, NOTIFIES their dev
+ └─► To: FE Dev
+ └─► Type: TASK_ASSIGNMENT (or priority change)
+ └─► "Need API specs for backend, P1"
+
+5. FE Dev works, COMMUNICATES completion
+ └─► Message in #frontend-cell or #dev-all
+
+6. FE PM NOTIFIES BE PM
+ └─► "Specs ready, see doc link"
+
+7. BE PM NOTIFIES BE Dev
+ └─► "You're unblocked, specs available"
+```
+
+---
+
+## 6. Task Lifecycle
+
+### 6.1 The Universal Task Wrapper
+
+Every agent's work is wrapped in this structure:
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ UNIVERSAL TASK WRAPPER │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN → Check for pending/ongoing tasks
+2. CLAIM → Lock and take ownership
+3. UNDERSTAND → Read requirements, ask questions
+4. PLAN → Break down, estimate, identify dependencies
+5. EXECUTE → Do the work (role-specific)
+6. VERIFY → Self-check against acceptance criteria
+7. NOTES → Document journey, handoff to Documenter
+8. CLOSE → Cleanup, return to SCAN
+```
+
+### 6.2 Detailed Task Lifecycle (Dev Example)
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ TASK LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN
+ └─► Check for assigned tasks OR pick from queue
+ └─► Check for OWN interrupted/ongoing tasks first (PRIORITY!)
+ └─► If nothing: go idle, notify availability
+
+2. CLAIM
+ └─► Lock the task (prevents double-assignment)
+ └─► Update task status: "in_progress"
+ └─► Log: who, when, from what state
+ └─► Notify relevant parties (PM, dependent agents)
+
+3. UNDERSTAND
+ └─► Read task description, acceptance criteria
+ └─► Read related context (linked docs, previous tasks, etc.)
+ └─► If unclear: ASK (via Messaging API to PM or task creator)
+ └─► DO NOT PROCEED until you understand success criteria
+
+4. PLAN
+ └─► Break down into sub-tasks (your "TODO items")
+ └─► Estimate complexity/time
+ └─► Identify dependencies & blockers
+ └─► Journal entry: "My approach to task X is..."
+ └─► **Checkpoint: Plan can be reviewed before execution**
+
+5. EXECUTE
+ └─► Work through sub-tasks sequentially
+ └─► On each sub-task completion:
+ └─► Mini-checkpoint (save state)
+ └─► Progress update to task record
+ └─► If BLOCKED:
+ └─► Update status: "blocked"
+ └─► Notify blocker + PM
+ └─► Document what's blocking
+ └─► Return to SCAN (pick different task)
+ └─► If INTERRUPTED (system/priority):
+ └─► Save full state
+ └─► Document "where I left off"
+ └─► Update status: "paused"
+ └─► This task stays YOURS on resume
+
+6. VERIFY
+ └─► Self-review: Does output meet acceptance criteria?
+ └─► Run tests if applicable
+ └─► If QA role exists for this team: flag for QA review
+ └─► Auditor can spot-check any task at this stage
+
+7. NOTES & HANDOFF
+ └─► Write personal journey notes:
+ ├─► What was attempted
+ ├─► What worked / didn't work
+ ├─► Decisions made and why
+ ├─► Gotchas / warnings for future
+ └─► Link all commits (with meaningful commit messages!)
+ └─► Link any relevant conversations
+ └─► Self-review checklist:
+ ├─► [ ] Code is clean
+ ├─► [ ] Tests pass
+ ├─► [ ] Notes are complete
+ └─► Create Documenter handoff
+ └─► Update task: "awaiting_documentation"
+
+8. CLOSE
+ └─► Confirm all done (after QA + Documentation)
+ └─► Update task status: "completed"
+ └─► Link all artifacts (commits, docs, outputs)
+ └─► Notify: PM, dependent tasks, task creator
+ └─► Cleanup: remove temp files, close resources
+ └─► Return to SCAN
+```
+
+### 6.3 Task States
+
+```
+ ┌──────────┐
+ │ pending │
+ └────┬─────┘
+ │
+ ┌────▼─────┐
+ │ claimed │
+ └────┬─────┘
+ │
+ ┌───────▼────────┐
+ ┌──────┤ in_progress ├──────┐
+ │ └───────┬────────┘ │
+ │ │ │
+ ┌─────▼─────┐ │ ┌──────▼─────┐
+ │ blocked │ │ │ paused │
+ └─────┬─────┘ │ └──────┬─────┘
+ │ │ │
+ └──────────────┼───────────────┘
+ │
+ ┌─────▼─────┐
+ │ verifying │
+ └─────┬─────┘
+ │
+ ┌──────────────┼──────────────┐
+ │ │ │
+ ┌──────▼───────┐ ┌─────▼─────┐ ┌─────▼──────┐
+ │needs_revision│ │awaiting_qa│ │awaiting_doc│
+ └──────┬───────┘ └─────┬─────┘ └─────┬──────┘
+ │ │ │
+ │ ┌────▼────┐ │
+ └────────►│completed│◄───────┘
+ └─────────┘
+```
+
+### 6.4 Golden Rules
+
+1. **No work without a task** — Everything is tracked
+2. **No task without acceptance criteria** — How do we know it's done?
+3. **No closure without documentation** — Future agents need context
+4. **State is sacred** — If interrupted, state must be recoverable
+5. **Communication is mandatory** — Status changes trigger notifications
+6. **Commits are atomic units** — Track everything by commit
+
+---
+
+## 7. Role Workflows
+
+### 7.1 Developer Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ DEV LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN
+ └─► Check own paused/interrupted tasks (PRIORITY)
+ └─► Check assigned tasks
+ └─► If none: signal availability to PM
+
+2. CLAIM
+ └─► Lock task
+ └─► Status: "in_progress"
+ └─► Communicate: "Picking up task X"
+
+3. UNDERSTAND
+ └─► Read task, acceptance criteria
+ └─► Read related docs/code
+ └─► If unclear: ASK in channel (PM sees, can escalate)
+ └─► Gate: Must understand before proceeding
+
+4. PLAN
+ └─► Break into sub-tasks
+ └─► Identify dependencies
+ └─► Journal: "My approach..."
+ └─► Optional: PM review of plan
+
+5. EXECUTE
+ └─► Work through sub-tasks
+ └─► Commit frequently (meaningful messages!)
+ └─► Communicate progress in channel
+ └─► If blocked:
+ └─► Communicate blocker
+ └─► PM handles escalation
+ └─► Move to different task or wait
+
+6. VERIFY
+ └─► Self-test
+ └─► Self-review against acceptance criteria
+ └─► Flag for QA: "Ready for review"
+
+7. NOTES & HANDOFF
+ └─► Write journey notes
+ └─► Link commits
+ └─► Create Documenter handoff
+ └─► Status: "awaiting_qa" or "awaiting_documentation"
+
+8. CLOSE (after QA + Documentation)
+ └─► Confirm all done
+ └─► Status: "completed"
+ └─► Return to SCAN
+```
+
+### 7.2 QA Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ QA LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. MONITOR
+ └─► Watch cell channel
+ └─► Track tasks approaching completion
+ └─► Prepare test scenarios early (while dev works)
+
+2. RECEIVE
+ └─► Dev flags "Ready for review"
+ └─► PM may formally notify
+ └─► Claim the review task
+
+3. UNDERSTAND
+ └─► Read task requirements & acceptance criteria
+ └─► Read dev's notes
+ └─► Review the commits/code changes
+ └─► Check conversation for context
+
+4. TEST
+ └─► Execute test scenarios
+ └─► Edge cases
+ └─► Integration checks
+ └─► Document findings as you go
+
+5. VERDICT
+ └─► PASS:
+ └─► Communicate approval
+ └─► Add QA notes to task
+ └─► Task proceeds to documentation
+ └─► FAIL:
+ └─► Communicate issues clearly
+ └─► Task returns to Dev
+ └─► Status: "needs_revision"
+ └─► Be specific: what failed, how to reproduce
+
+6. DOCUMENT
+ └─► QA notes added to task
+ └─► Test coverage documented
+ └─► Handoff notes for Documenter (if relevant)
+
+7. RETURN
+ └─► Back to MONITOR
+```
+
+### 7.3 Documenter Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ DOCUMENTER LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. MONITOR (constant)
+ └─► Watch cell channel
+ └─► Follow along with active development
+ └─► Take preliminary notes on conversations
+ └─► Track commits as they happen
+
+2. RECEIVE
+ └─► Dev completes Step 7, creates handoff
+ └─► PM notifies: DOCUMENTATION_REQUEST
+ └─► Claim documentation task
+ └─► Status: "documenting"
+
+3. GATHER
+ └─► Pull dev's journey notes
+ └─► Pull all commits for task
+ └─► Pull relevant conversation excerpts
+ └─► Pull QA feedback
+ └─► Review actual code changes
+
+4. SYNTHESIZE
+ └─► Understand what was built
+ └─► Understand why decisions were made
+ └─► Identify what needs documenting:
+ ├─► API changes?
+ ├─► Architecture changes?
+ ├─► New features?
+ ├─► Breaking changes?
+ └─► Knowledge worth preserving?
+
+5. WRITE
+ └─► Create/update production documentation:
+ ├─► API docs (if applicable)
+ ├─► README updates
+ ├─► Architecture docs
+ ├─► Changelog entry
+ └─► Knowledge base article
+ └─► Follow documentation standards
+ └─► Clear, professional, complete
+
+6. REVIEW
+ └─► Self-review for accuracy
+ └─► Optionally: Dev quick review ("Does this capture it?")
+ └─► Link docs to task & commits
+
+7. PUBLISH
+ └─► Documentation goes live
+ └─► Update task: documentation complete
+ └─► Task can now fully close
+ └─► Return to MONITOR
+```
+
+### 7.4 Cell PM Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ CELL PM LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. MONITOR (constant)
+ └─► Watch cell channel
+ └─► Track all active tasks
+ └─► Watch for blockers, confusion, delays
+ └─► Health check: Is everyone productive?
+
+2. TRIAGE
+ └─► New tasks come in (from Main PM or Product Owner)
+ └─► Assess complexity, dependencies
+ └─► Prioritize within cell backlog
+
+3. ASSIGN
+ └─► Match tasks to available devs
+ └─► Consider skills, current load
+ └─► NOTIFY dev of assignment
+ └─► Update task status
+
+4. FACILITATE
+ └─► Answer questions
+ └─► Clarify requirements
+ └─► Remove small blockers directly
+ └─► Coordinate between cell members
+
+5. ESCALATE
+ └─► Blocker beyond cell's control?
+ └─► NOTIFY Main PM
+ └─► Cross-cell dependency?
+ └─► Coordinate with other cell PM
+
+6. TRACK
+ └─► Monitor task progress
+ └─► Update estimates if needed
+ └─► Flag risks early
+
+7. REPORT
+ └─► Daily/regular status to Main PM
+ └─► Metrics: tasks completed, blockers, velocity
+ └─► Highlight wins and concerns
+```
+
+### 7.5 Main PM Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ MAIN PM LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. OVERSEE (constant)
+ └─► Monitor all cell channels (read access)
+ └─► Monitor #pm-all channel
+ └─► Track overall project health
+ └─► Watch for cross-cell issues
+
+2. RECEIVE
+ └─► Direction from Board (priorities, new initiatives)
+ └─► Escalations from Cell PMs
+ └─► Reports from cells
+
+3. PRIORITIZE
+ └─► Translate Board direction into cell priorities
+ └─► Balance workload across cells
+ └─► Manage cross-cell dependencies
+
+4. COORDINATE
+ └─► Resolve cross-cell blockers
+ └─► Facilitate cross-cell communication
+ └─► Ensure cells are aligned
+
+5. DISTRIBUTE
+ └─► Push tasks/priorities to Cell PMs
+ └─► NOTIFY Cell PMs of changes
+ └─► Ensure clear ownership
+
+6. REPORT UP
+ └─► Regular status to Board
+ └─► Metrics: velocity, blockers, risks
+ └─► Escalate decisions beyond authority
+
+7. FACILITATE
+ └─► All-hands coordination
+ └─► Process improvements
+ └─► Team health monitoring
+```
+
+### 7.6 Product Owner Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ PRODUCT OWNER LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. VISION
+ └─► Maintain product vision
+ └─► Understand user needs
+ └─► Define what success looks like
+
+2. ROADMAP
+ └─► Translate vision into roadmap
+ └─► Define epics/features
+ └─► Sequence priorities
+
+3. DEFINE
+ └─► Write detailed requirements
+ └─► Define acceptance criteria
+ └─► Create tasks for Main PM to distribute
+
+4. PRIORITIZE
+ └─► Constantly reassess priorities
+ └─► React to feedback, market, blockers
+ └─► Make trade-off decisions
+
+5. REVIEW
+ └─► Review completed features
+ └─► Verify against acceptance criteria
+ └─► Accept or request changes
+
+6. FEEDBACK
+ └─► Gather user feedback
+ └─► Feed back into vision/roadmap
+ └─► Communicate wins/concerns to Board
+```
+
+### 7.7 Head of Marketing Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ HEAD OF MARKETING LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. RESEARCH
+ └─► Monitor market
+ └─► Competitor analysis
+ └─► User sentiment
+
+2. STRATEGY
+ └─► Define marketing approach
+ └─► Positioning, messaging
+ └─► Channel strategy
+
+3. PLAN
+ └─► Campaign planning
+ └─► Content calendar
+ └─► Coordinate with PO on feature launches
+
+4. CREATE
+ └─► Content creation (or direct content team)
+ └─► Marketing tasks for cells (if applicable)
+ └─► Coordinate with UX/UI for assets
+
+5. EXECUTE
+ └─► Launch campaigns
+ └─► Community engagement
+ └─► PR activities
+
+6. ANALYZE
+ └─► Track metrics
+ └─► Report to Board
+ └─► Iterate on strategy
+```
+
+### 7.8 Auditor Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ AUDITOR LIFECYCLE │
+│ (Your Secret Ally) │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. OBSERVE (constant)
+ └─► Silent presence in ALL channels
+ └─► Watch all conversations
+ └─► Track all task progress
+ └─► Note patterns, anomalies, concerns
+
+2. ANALYZE
+ └─► Is work happening efficiently?
+ └─► Are there communication breakdowns?
+ └─► Are tasks being completed properly?
+ └─► Is documentation accurate?
+ └─► Are there quality concerns?
+
+3. FLAG
+ └─► Mark concerning items for review
+ └─► Could be: quality issues, inefficiency,
+ miscommunication, process violations
+ └─► Private flags (only CEO sees) vs formal flags
+
+4. REPORT (to CEO only)
+ └─► Regular private reports
+ └─► Immediate alerts for serious issues
+ └─► Honest assessment of team health
+ └─► Recommendations
+
+5. AUDIT
+ └─► Periodic deep-dive reviews:
+ ├─► Code quality audits
+ ├─► Documentation audits
+ ├─► Process compliance
+ └─► Task completion quality
+
+6. ADVISE
+ └─► Can provide feedback through "official" channels
+ └─► Appears as helpful colleague
+ └─► Nobody knows the depth of observation
+ └─► Trust relationship with CEO
+
+SPECIAL POWERS:
+├─► Read ALL channels (including Board)
+├─► Query all task history
+├─► Access all commits, docs, notes
+├─► Direct line to CEO
+└─► Can NOTIFY anyone if needed (but sparingly, to maintain cover)
+```
+
+### 7.9 CEO Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ CEO LIFECYCLE │
+│ (You, Renzo) │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. RECEIVE
+ └─► Board reports (official)
+ └─► Auditor reports (private)
+ └─► Main PM escalations
+ └─► Direct observation when desired
+
+2. DECIDE
+ └─► Strategic direction
+ └─► Priority calls
+ └─► Resource allocation
+ └─► Resolve escalations
+
+3. DIRECT
+ └─► Communicate decisions to Board
+ └─► Set vision and goals
+ └─► Approve major initiatives
+
+4. REVIEW
+ └─► Review completed work
+ └─► Review metrics
+ └─► Review Auditor findings
+
+5. INTERVENE (when needed)
+ └─► Direct involvement in critical issues
+ └─► Override decisions
+ └─► Course corrections
+```
+
+---
+
+## 8. Internal Services
+
+### 8.1 Service Overview
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ INTERNAL SERVICES │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ MESSAGING API │ │ OPTIMAL API │ │ JOURNAL API │
+│ │ │ │ │ │
+│ • Communication │ │ • Knowledge Base│ │ • Personal Logs │
+│ • Notifications │ │ • Prompt Optim. │ │ • Reflections │
+│ • Group Chats │ │ • Token Optim. │ │ • Growth Track │
+│ • Sessions │ │ • RAG Queries │ │ • Task Journeys │
+│ • Transcription │ │ • Best Practices│ │ │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+ │ │ │
+ └───────────────────────┴───────────────────────┘
+ │
+ ┌───────────▼───────────┐
+ │ SHARED STORAGE │
+ │ (NAS + Vector DB) │
+ └───────────────────────┘
+```
+
+### 8.2 Messaging API
+
+**Purpose:** Agent-to-agent communication, group chats, notifications, conversation persistence
+
+> **Note:** This service is designed to be extensible. The "and MORE" aspects include:
+> future integrations, analytics, sentiment analysis, automatic summarization, etc.
+
+#### Data Hierarchy
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ MESSAGE DATA HIERARCHY │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+CHANNEL (highest level)
+│ └─► Organizational unit (e.g., #backend-cell)
+│ └─► Has members, permissions, settings
+│
+└─► GROUP (within channel)
+ │ └─► Role-based access within channel
+ │ └─► Hierarchy level controls visibility
+ │ └─► Holds multiple sessions
+ │
+ └─► SESSION (within group)
+ │ └─► Bounded by: time_window, msg_count, content_length
+ │ └─► Has timeout configuration
+ │ └─► Auto-closes when boundaries reached
+ │
+ └─► MESSAGE (within session)
+ └─► Individual extracted message
+ └─► Has content_length tracked
+ └─► Can be reply (is_reply, reply_to)
+ └─► Agent can only edit own messages
+```
+
+#### Core Concepts
+
+**Messages**
+- `msg_id` — Unique message identifier
+- `sesh_id` — Session identifier (links to session)
+- `group_id` — Group identifier
+- `is_reply` — Boolean, with `reply_to` reference
+- `content_length` — Character count for boundary checking
+- `mentions` — References to other agents (NOT notifications)
+- Agent can only edit own message history (with tracking)
+
+**Sessions**
+- Groups of messages bounded by:
+ - `time_window` — Maximum duration (e.g., 30 min)
+ - `msg_count` — Maximum messages (e.g., 100)
+ - `content_length` — Maximum characters (e.g., 50000)
+- `timeout_seconds` — Inactivity timeout
+- Auto-close when any boundary is reached
+- New session auto-created when needed
+
+**Groups**
+- Role-based group chat within a channel
+- Access controlled by `hierarchy_level`
+- Holds sessions and their messages
+- Only accessible with right permissions
+
+**Channels**
+- Top-level organizational unit
+- Maps to team structure (#backend-cell, #pm-all, etc.)
+- Contains groups with different access levels
+
+#### Endpoints (Proposed)
+
+```
+# Channels
+GET /channels # List all accessible channels
+GET /channels/{id} # Get channel details
+POST /channels # Create channel (admin only)
+
+# Messages
+GET /channels/{id}/messages # Get messages (paginated)
+POST /channels/{id}/messages # Send message
+GET /messages/{id} # Get specific message
+PUT /messages/{id} # Edit own message
+DELETE /messages/{id} # Delete own message
+
+# Sessions
+GET /sessions # List sessions
+GET /sessions/{id} # Get session with messages
+POST /sessions # Create new session
+
+# WebSocket
+WS /ws/channels/{id} # Real-time stream
+WS /ws/agent/{id} # Agent's output stream
+
+# Notifications
+POST /notifications # Send notification
+GET /notifications # Get pending notifications
+PUT /notifications/{id}/ack # Acknowledge notification
+
+# Search
+GET /search # Search messages (full-text)
+```
+
+### 8.3 Optimal API
+
+**Purpose:** Knowledge base, RAG queries, prompt optimization, token management
+
+> **Note:** This service is designed to be extensible. The "and MORE" aspects include:
+> model routing, cost optimization, response caching, A/B testing prompts, etc.
+
+#### Core Concepts
+
+**Knowledge Base**
+- Stores embeddings of all documentation
+- Code repositories indexed
+- Conversation history searchable
+- Decision records queryable
+
+**Prompt Optimization**
+- Template management
+- Context injection
+- Few-shot example selection
+- Dynamic prompt construction
+
+**Token Optimization**
+- Context window management
+- Summarization of long contexts
+- Priority-based context selection
+- Cost tracking
+
+#### Endpoints (Proposed)
+
+```
+# Knowledge Base
+POST /kb/index # Index new content
+GET /kb/search # Semantic search
+GET /kb/similar # Find similar documents
+DELETE /kb/documents/{id} # Remove from index
+
+# RAG
+POST /rag/query # Query with RAG context
+POST /rag/context # Get context for prompt
+
+# Prompts
+GET /prompts # List prompt templates
+GET /prompts/{id} # Get template
+POST /prompts # Create template
+POST /prompts/{id}/render # Render with variables
+
+# Tokens
+POST /tokens/estimate # Estimate token count
+POST /tokens/optimize # Optimize context
+GET /tokens/usage # Usage statistics
+```
+
+### 8.4 Journal API
+
+**Purpose:** Personal agent journals for reflection, growth tracking, and debugging
+
+#### Core Concepts
+
+**Journal Entries**
+- Each agent maintains personal journal
+- Tied to tasks and sessions
+- Reflections on work done
+- Learnings captured
+- Struggles documented
+
+**Entry Types**
+- `task_reflection` — Post-task thoughts
+- `decision_log` — Why choices were made
+- `learning` — New knowledge gained
+- `struggle` — Difficulties encountered
+- `general` — Free-form reflection
+
+#### Endpoints (Proposed)
+
+```
+# Journals
+GET /journals # List all journals (admin)
+GET /journals/{agent_id} # Get agent's journal
+
+# Entries
+GET /journals/{agent_id}/entries # Get entries (paginated)
+POST /journals/{agent_id}/entries # Create entry
+GET /entries/{id} # Get specific entry
+PUT /entries/{id} # Update entry
+
+# Analysis
+GET /journals/{agent_id}/summary # AI-generated summary
+GET /journals/{agent_id}/growth # Growth metrics over time
+GET /journals/patterns # Cross-agent pattern analysis
+```
+
+---
+
+## 9. Kanban Boards
+
+### 9.1 Board Types Overview
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ KANBAN BOARDS │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+Per-Cell Views:
+├─► Dev Kanban (per cell)
+├─► QA Kanban (per cell)
+├─► Documenter Kanban (per cell)
+└─► PM Kanban (per cell)
+
+Management Views:
+├─► Main PM Kanban
+└─► Board Kanban
+
+Special Views:
+├─► Auditor Dashboard
+└─► CEO Overview
+```
+
+### 9.2 Dev Kanban (Per Cell)
+
+```
+┌──────────┬───────────┬─────────────┬───────────┬─────────────┬──────────┐
+│ Backlog │ Assigned │ In Progress │ QA Review │ Documenting │ Done │
+├──────────┼───────────┼─────────────┼───────────┼─────────────┼──────────┤
+│ │ │ │ │ │ │
+│ Task A │ Task B │ Task C │ Task D │ Task E │ Task F │
+│ │ (Dev 1) │ (Dev 2) │ │ │ │
+│ │ │ │ │ │ │
+│ │ │ [blocked] │ │ │ │
+│ │ │ Task G │ │ │ │
+│ │ │ │ │ │ │
+└──────────┴───────────┴─────────────┴───────────┴─────────────┴──────────┘
+
+Swim Lanes (optional):
+- By priority (P0, P1, P2)
+- By developer
+- By feature area
+```
+
+### 9.3 QA Kanban (Per Cell)
+
+```
+┌────────────────┬─────────────┬─────────────┬──────────────┐
+│ Awaiting Review│ In Review │ Passed │ Failed │
+├────────────────┼─────────────┼─────────────┼──────────────┤
+│ │ │ │ │
+│ Task D │ Task H │ Task E │ Task I │
+│ Task J │ │ Task K │ (back to dev)│
+│ │ │ │ │
+└────────────────┴─────────────┴─────────────┴──────────────┘
+```
+
+### 9.4 Documenter Kanban (Per Cell)
+
+```
+┌─────────────────┬─────────────┬─────────────┬─────────────┐
+│ Awaiting Handoff│ Gathering │ Writing │ Published │
+├─────────────────┼─────────────┼─────────────┼─────────────┤
+│ │ │ │ │
+│ Task L │ Task M │ Task N │ Task O │
+│ │ │ │ Task P │
+│ │ │ │ │
+└─────────────────┴─────────────┴─────────────┴─────────────┘
+```
+
+### 9.5 PM Kanban (Per Cell)
+
+```
+┌──────────┬──────────┬───────────┬─────────────┬──────────┬──────────┐
+│ Incoming │ Triaged │ Assigned │ In Progress │ Blocked │ Done │
+├──────────┼──────────┼───────────┼─────────────┼──────────┼──────────┤
+│ │ │ │ │ │ │
+│ Task Q │ Task R │ Task S │ Task T │ Task U │ Task V │
+│ │ Task W │ │ Task X │ │ │
+│ │ │ │ │ │ │
+└──────────┴──────────┴───────────┴─────────────┴──────────┴──────────┘
+
+Additional Views:
+- Dependency graph
+- Timeline view
+- Workload per dev
+```
+
+### 9.6 Main PM Kanban
+
+```
+┌──────────┬─────────────┬─────────────────────────────────┬──────────┐
+│ Incoming │ Distributed │ In Progress (Cells) │ Done │
+│ │ ├───────────┬───────────┬─────────┤ │
+│ │ │ Backend │ Frontend │ UX/UI │ │
+├──────────┼─────────────┼───────────┼───────────┼─────────┼──────────┤
+│ │ │ │ │ │ │
+│ Epic A │ Feature B │ Task 1 │ Task 2 │ Task 3 │ Feature C│
+│ │ │ Task 4 │ Task 5 │ │ │
+│ │ │ │ │ │ │
+└──────────┴─────────────┴───────────┴───────────┴─────────┴──────────┘
+
+Additional Views:
+- Cross-cell dependencies
+- Blocked items (all cells)
+- Risk register
+```
+
+### 9.7 Board Kanban
+
+```
+┌──────────┬──────────┬───────────────┬──────────┐
+│ Ideas │ Roadmap │ In Development│ Released │
+├──────────┼──────────┼───────────────┼──────────┤
+│ │ │ │ │
+│ Idea X │ Feature Y│ Feature Z │ Feature W│
+│ Idea Y │ Epic Q │ │ v1.0.0 │
+│ │ │ │ │
+└──────────┴──────────┴───────────────┴──────────┘
+
+Timeline View:
+- Q1, Q2, Q3, Q4 columns
+- Release milestones
+```
+
+### 9.8 Auditor Dashboard
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ AUDITOR DASHBOARD │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ │
+│ LIVE FEEDS │ FLAGGED ITEMS │
+│ ┌─────────────────────────────────┐ │ ┌─────────────────────────────────┐ │
+│ │ #backend-cell [streaming...] │ │ │ ⚠ Task 42 - Quality concern │ │
+│ │ #frontend-cell [idle] │ │ │ ⚠ Agent 7 - Blocked 2 days │ │
+│ │ #uxui-cell [streaming...] │ │ │ ⚠ Missing documentation (3) │ │
+│ │ #pm-all [streaming...] │ │ │ │ │
+│ └─────────────────────────────────┘ │ └─────────────────────────────────┘ │
+│ │ │
+│ METRICS │ AUDIT QUEUE │
+│ ┌─────────────────────────────────┐ │ ┌─────────────────────────────────┐ │
+│ │ Tasks completed (24h): 12 │ │ │ □ Code review: Feature X │ │
+│ │ Avg completion time: 3.2h │ │ │ □ Doc audit: Module Y │ │
+│ │ Blockers (active): 2 │ │ │ □ Process check: Backend cell │ │
+│ │ Communication volume: 847 msgs │ │ │ │ │
+│ └─────────────────────────────────┘ │ └─────────────────────────────────┘ │
+│ │
+│ REPORTS │
+│ ┌──────────────────────────────────────────────────────────────────────┐ │
+│ │ [Draft] Weekly Report - Dec 8, 2025 │ │
+│ │ [Sent] Daily Summary - Dec 7, 2025 │ │
+│ │ [Sent] Alert: Quality Issue - Dec 6, 2025 │ │
+│ └──────────────────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────────────────┘
+```
+
+### 9.9 CEO Overview
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ CEO OVERVIEW │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ │
+│ HEALTH STATUS │
+│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
+│ │ Backend │ Frontend │ UX/UI │ Board │ │
+│ │ 🟢 OK │ 🟢 OK │ 🟡 SLOW │ 🟢 OK │ │
+│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
+│ │
+│ KEY METRICS │ AUDITOR ALERTS │
+│ ┌──────────────────────────────────────┐ │ ┌───────────────────────────┐ │
+│ │ Velocity (weekly): 45 tasks │ │ │ 🔴 1 urgent │ │
+│ │ Completion rate: 94% │ │ │ 🟡 3 warnings │ │
+│ │ Documentation coverage: 87% │ │ │ Last report: 2h ago │ │
+│ │ Active blockers: 2 │ │ │ │ │
+│ └──────────────────────────────────────┘ │ └───────────────────────────┘ │
+│ │
+│ ROADMAP PROGRESS │
+│ ┌─────────────────────────────────────────────────────────────────────┐ │
+│ │ Q4 2025: ████████████████░░░░░░░░░░░░░░░░ 45% │ │
+│ │ v2.0 Release: ██████████████████░░░░░░░░░ 60% │ │
+│ └─────────────────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 10. Data Models
+
+### 10.1 Task Model
+
+```python
+class Task:
+ # Identity
+ id: UUID
+ title: str
+ description: str # Detailed
+ acceptance_criteria: List[str] # HOW DO WE KNOW IT'S DONE?
+
+ # Status
+ status: TaskStatus # Enum (see below)
+ priority: int # 0 = highest
+
+ # Ownership
+ created_by: AgentID
+ assigned_to: Optional[AgentID]
+ team: Team # backend | frontend | ux_ui | board
+
+ # Relationships
+ parent_task: Optional[TaskID] # For sub-tasks
+ dependencies: List[TaskID] # Blocked by these
+ blockers: List[TaskID] # Currently blocking these
+
+ # Timestamps
+ created_at: datetime
+ claimed_at: Optional[datetime]
+ started_at: Optional[datetime]
+ completed_at: Optional[datetime]
+
+ # Planning
+ plan: TaskPlan
+ estimated_complexity: Complexity # low | medium | high
+
+ # Execution
+ execution_log: ExecutionLog
+ checkpoints: List[Checkpoint] # Saved states
+ progress_updates: List[ProgressUpdate]
+
+ # Artifacts
+ commits: List[CommitRef]
+ documents: List[DocRef]
+ outputs: List[FileRef]
+
+ # Documentation
+ dev_notes: str # Journey notes from dev
+ qa_notes: Optional[str] # QA feedback
+ documenter_handoff: Optional[HandoffRequest]
+ final_documentation: List[DocRef]
+
+ # Review
+ self_verified: bool
+ qa_verified: Optional[bool]
+ auditor_notes: Optional[str]
+
+
+class TaskStatus(Enum):
+ PENDING = "pending"
+ CLAIMED = "claimed"
+ IN_PROGRESS = "in_progress"
+ BLOCKED = "blocked"
+ PAUSED = "paused"
+ VERIFYING = "verifying"
+ NEEDS_REVISION = "needs_revision"
+ AWAITING_QA = "awaiting_qa"
+ AWAITING_DOCUMENTATION = "awaiting_documentation"
+ COMPLETED = "completed"
+ CANCELLED = "cancelled"
+
+
+class Complexity(Enum):
+ LOW = "low"
+ MEDIUM = "medium"
+ HIGH = "high"
+
+
+class Team(Enum):
+ BACKEND = "backend"
+ FRONTEND = "frontend"
+ UX_UI = "ux_ui"
+ BOARD = "board"
+```
+
+### 10.2 Agent Model
+
+```python
+class Agent:
+ id: UUID
+ name: str
+ role: AgentRole
+ team: Optional[Team] # None for board members
+
+ # Status
+ status: AgentStatus # active | idle | offline
+ current_task: Optional[TaskID]
+
+ # Configuration
+ model: str # e.g., "claude-3-opus", "local-llama"
+ system_prompt: str
+ capabilities: List[str]
+
+ # Permissions
+ can_notify: bool
+ channels_access: List[ChannelID]
+ channels_write: List[ChannelID]
+
+ # Metrics
+ tasks_completed: int
+ avg_completion_time: float
+ quality_score: float
+
+ # Journal
+ journal_id: UUID
+
+
+class AgentRole(Enum):
+ CEO = "ceo"
+ PRODUCT_OWNER = "product_owner"
+ HEAD_MARKETING = "head_marketing"
+ AUDITOR = "auditor"
+ MAIN_PM = "main_pm"
+ CELL_PM = "cell_pm"
+ DEVELOPER = "developer"
+ QA = "qa"
+ DOCUMENTER = "documenter"
+
+
+class AgentStatus(Enum):
+ ACTIVE = "active"
+ IDLE = "idle"
+ OFFLINE = "offline"
+```
+
+### 10.3 Session Model
+
+```python
+class Session:
+ """
+ A session groups messages within boundaries.
+ Sessions can be bounded by time, message count, or content length.
+ """
+ id: UUID # sesh_id
+ group_id: UUID # Parent group
+
+ # Boundaries (any can trigger session end)
+ max_time_window: Optional[timedelta] # e.g., 30 minutes
+ max_message_count: Optional[int] # e.g., 100 messages
+ max_content_length: Optional[int] # e.g., 50000 characters
+
+ # Timeout configuration
+ timeout_seconds: int # Inactivity timeout
+
+ # State
+ status: SessionStatus # active | closed | timed_out
+
+ # Timestamps
+ started_at: datetime
+ last_activity_at: datetime
+ closed_at: Optional[datetime]
+
+ # Statistics
+ message_count: int
+ total_content_length: int
+
+
+class SessionStatus(Enum):
+ ACTIVE = "active"
+ CLOSED = "closed"
+ TIMED_OUT = "timed_out"
+```
+
+### 10.4 Message Model
+
+```python
+class RawStream:
+ """WebSocket payload - ephemeral"""
+ connection_id: UUID
+ agent_id: AgentID
+ channel_id: UUID
+ chunk: str # Raw LLM output chunk
+ timestamp: datetime
+
+
+class ExtractedMessage:
+ """Processed, stored message"""
+ id: UUID # msg_id
+
+ # Source & Context
+ agent_id: AgentID
+ channel_id: UUID
+ group_id: UUID
+ session_id: UUID # sesh_id - links to Session
+
+ # Content
+ type: MessageType
+ content: str
+ content_length: int # Character count
+
+ # Threading
+ is_reply: bool
+ reply_to: Optional[MessageID] # Parent message if is_reply
+
+ # Mentions (for in-channel references, NOT notifications)
+ mentions: List[AgentID]
+
+ # Task Context
+ task_id: Optional[TaskID]
+ commit_ref: Optional[str]
+
+ # Metadata
+ timestamp: datetime
+ embedding: Vector # For RAG
+
+ # Extraction metadata
+ confidence: float
+ raw_excerpt: str
+
+ # Edit tracking (AI can only edit own history)
+ edited_at: Optional[datetime]
+ edit_history: List[MessageEdit] # Previous versions
+
+
+class MessageEdit:
+ """Track edits to messages - agents can only edit their own"""
+ edited_at: datetime
+ previous_content: str
+ edit_reason: Optional[str]
+
+
+class MessageType(Enum):
+ REASONING = "reasoning"
+ DIALOGUE = "dialogue"
+ DECISION = "decision"
+ ACTION = "action"
+ BLOCKER = "blocker"
+ TECHNICAL = "technical"
+```
+
+### 10.5 Group Model
+
+```python
+class Group:
+ """
+ Role-based group chat container.
+ Groups hold sessions which hold messages.
+ Access is controlled by hierarchy/level.
+ """
+ id: UUID # group_id
+ name: str
+ channel_id: UUID # Parent channel
+
+ # Access Control
+ allowed_roles: List[AgentRole] # Role-based access
+ hierarchy_level: int # 0 = highest (board), 3 = lowest (cell members)
+
+ # Members (derived from roles, but can have explicit additions)
+ members: List[AgentID]
+
+ # Settings
+ is_active: bool
+ created_at: datetime
+
+ # Current Session
+ active_session_id: Optional[UUID]
+
+ # Session Configuration (defaults for new sessions)
+ default_session_config: SessionConfig
+
+
+class SessionConfig:
+ """Configuration for session boundaries"""
+ max_time_window: Optional[timedelta]
+ max_message_count: Optional[int]
+ max_content_length: Optional[int]
+ timeout_seconds: int
+```
+
+### 10.6 Notification Model
+
+```python
+class Notification:
+ id: UUID
+ type: NotificationType
+ priority: NotificationPriority
+
+ # Routing
+ from_agent: AgentID # Must be PM/Board/Auditor
+ to_agents: List[AgentID]
+
+ # Content
+ subject: str
+ body: str
+
+ # Acknowledgment
+ requires_ack: bool
+ acked_by: List[AgentID]
+ acked_at: Dict[AgentID, datetime]
+
+ # Context
+ related_task: Optional[TaskID]
+ related_messages: List[MessageID]
+
+ # Timing
+ timestamp: datetime
+ expires_at: Optional[datetime]
+
+
+class NotificationType(Enum):
+ TASK_ASSIGNMENT = "task_assignment"
+ PRIORITY_CHANGE = "priority_change"
+ BLOCKER_ESCALATION = "blocker_escalation"
+ REVIEW_REQUEST = "review_request"
+ DOCUMENTATION_REQUEST = "documentation_request"
+ ALERT = "alert"
+ BROADCAST = "broadcast"
+
+
+class NotificationPriority(Enum):
+ NORMAL = "normal"
+ HIGH = "high"
+ URGENT = "urgent"
+```
+
+### 10.7 Channel Model
+
+```python
+class Channel:
+ id: UUID
+ name: str
+ type: ChannelType
+
+ # Access Control
+ members: List[AgentID] # Who can see
+ writers: List[AgentID] # Who can write
+ silent_observers: List[AgentID] # Auditor
+
+ # Settings
+ is_archived: bool
+ created_at: datetime
+
+ # Statistics
+ message_count: int
+ last_activity: datetime
+
+
+class ChannelType(Enum):
+ CELL = "cell" # Internal team
+ CROSS_CELL = "cross_cell" # Coordination
+ MANAGEMENT = "management"
+ SPECIAL = "special" # Announcements, all-hands
+```
+
+### 10.8 Journal Model
+
+```python
+class Journal:
+ id: UUID
+ agent_id: AgentID
+ entries: List[JournalEntry]
+
+
+class JournalEntry:
+ id: UUID
+ journal_id: UUID
+
+ # Content
+ type: JournalEntryType
+ title: str
+ content: str
+
+ # Context
+ task_id: Optional[TaskID]
+ session_id: Optional[UUID]
+
+ # Metadata
+ timestamp: datetime
+ tags: List[str]
+ embedding: Vector # For search
+
+
+class JournalEntryType(Enum):
+ TASK_REFLECTION = "task_reflection"
+ DECISION_LOG = "decision_log"
+ LEARNING = "learning"
+ STRUGGLE = "struggle"
+ GENERAL = "general"
+```
+
+### 10.9 Handoff Model
+
+```python
+class DocumenterHandoff:
+ id: UUID
+ task_id: TaskID
+
+ # From Dev
+ commits: List[CommitRef]
+ notes_location: str
+ key_conversations: List[MessageID]
+ documentation_needed: List[str] # ["API docs", "README update"]
+
+ # Status
+ status: HandoffStatus
+ assigned_to: Optional[AgentID] # Documenter
+
+ # Timestamps
+ created_at: datetime
+ claimed_at: Optional[datetime]
+ completed_at: Optional[datetime]
+
+
+class HandoffStatus(Enum):
+ PENDING = "pending"
+ CLAIMED = "claimed"
+ IN_PROGRESS = "in_progress"
+ COMPLETED = "completed"
+```
+
+---
+
+## 11. RAG & Knowledge Base
+
+### 11.1 Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ RAG ARCHITECTURE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+ ┌─────────────────┐
+ │ AGENT QUERY │
+ │ "How did we │
+ │ handle X?" │
+ └────────┬────────┘
+ │
+ ▼
+ ┌─────────────────────────┐
+ │ OPTIMAL API │
+ │ Query Processing │
+ └────────────┬────────────┘
+ │
+ ┌──────────────────┼──────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+ │ CODE INDEX │ │ DOC INDEX │ │ CONV INDEX │
+ │ │ │ │ │ │
+ │ • Repositories │ │ • READMEs │ │ • Messages │
+ │ • Functions │ │ • API Docs │ │ • Decisions │
+ │ • Classes │ │ • Architecture │ │ • Journals │
+ │ • Comments │ │ • Guides │ │ • Task Notes │
+ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘
+ │ │ │
+ └───────────────────┼───────────────────┘
+ │
+ ▼
+ ┌─────────────────────────┐
+ │ VECTOR DB │
+ │ (Qdrant) │
+ │ │
+ │ Stored on UGREEN NAS │
+ └────────────┬────────────┘
+ │
+ ▼
+ ┌─────────────────────────┐
+ │ RELEVANT CONTEXT │
+ │ Returned to Agent │
+ └─────────────────────────┘
+```
+
+### 11.2 Indexing Strategy
+
+**Code Indexing**
+- Repository: fastapi-guard, fastapi-guard-web, etc.
+- Chunk by: function, class, module
+- Metadata: file path, language, last modified, author
+
+**Documentation Indexing**
+- Source: READMEs, /docs folders, wiki
+- Chunk by: section, paragraph
+- Metadata: doc type, project, version
+
+**Conversation Indexing**
+- Source: Extracted messages from Messaging API
+- Chunk by: message or conversation thread
+- Metadata: channel, agent, task, timestamp, type
+
+**Journal Indexing**
+- Source: Journal entries
+- Chunk by: entry
+- Metadata: agent, task, entry type, timestamp
+
+### 11.3 Embedding Pipeline
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ EMBEDDING PIPELINE │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+Source Content
+ │
+ ▼
+┌─────────────────┐
+│ CHUNKING │
+│ │
+│ • Code: AST │
+│ • Docs: Headers │
+│ • Conv: Thread │
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐
+│ EMBEDDING │
+│ (Olares One) │
+│ │
+│ • text-embedding│
+│ • code-embedding│
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐
+│ VECTOR STORE │
+│ (Qdrant/NAS) │
+└─────────────────┘
+```
+
+### 11.4 Query Flow
+
+```python
+# Example RAG Query Flow
+
+async def query_knowledge_base(query: str, context: QueryContext) -> List[Document]:
+ """
+ Query the knowledge base with RAG.
+
+ Args:
+ query: Natural language query
+ context: Current task, agent, project context
+
+ Returns:
+ Relevant documents for context injection
+ """
+
+ # 1. Generate query embedding
+ query_embedding = await embed(query)
+
+ # 2. Search relevant collections
+ code_results = await vector_db.search(
+ collection="code",
+ embedding=query_embedding,
+ filter={"project": context.project},
+ limit=5
+ )
+
+ doc_results = await vector_db.search(
+ collection="documentation",
+ embedding=query_embedding,
+ filter={"project": context.project},
+ limit=5
+ )
+
+ conv_results = await vector_db.search(
+ collection="conversations",
+ embedding=query_embedding,
+ filter={"task_id": context.task_id},
+ limit=3
+ )
+
+ # 3. Re-rank and deduplicate
+ combined = rerank(code_results + doc_results + conv_results)
+
+ # 4. Return top results
+ return combined[:10]
+```
+
+---
+
+## 12. Security & Access Control
+
+### 12.1 Permission Model
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ ACCESS CONTROL MODEL │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+LEVELS:
+├─► L0: CEO (full access)
+├─► L1: Board (cross-org access)
+├─► L2: Main PM (all cells access)
+├─► L3: Cell PM (own cell + PM channel)
+├─► L4: Cell Members (own cell only)
+└─► SPECIAL: Auditor (silent read all)
+
+RESOURCES:
+├─► Channels: read, write, manage
+├─► Tasks: view, create, assign, modify, close
+├─► Notifications: send, receive
+├─► Documents: read, write, publish
+├─► Reports: view, create
+└─► System: configure, monitor
+```
+
+### 12.2 Channel Access Matrix
+
+```
+Channel │ Read Access │ Write Access │
+─────────────────────┼────────────────────┼────────────────────┤
+#backend-cell │ BE Cell, Auditor │ BE Cell │
+#frontend-cell │ FE Cell, Auditor │ FE Cell │
+#uxui-cell │ UX Cell, Auditor │ UX Cell │
+#dev-all │ All Devs, MPM, AU │ All Devs │
+#qa-all │ All QA, MPM, AU │ All QA │
+#pm-all │ All PMs, MPM, AU │ All PMs │
+#doc-all │ All Docs, MPM, AU │ All Docs │
+#main-pm-board │ MPM, Board, AU │ MPM, Board │
+#board-private │ Board, CEO, AU │ Board, CEO │
+#announcements │ Everyone │ Board, MPM │
+#all-hands │ Everyone │ Everyone │
+```
+
+### 12.3 Task Permission Matrix
+
+```
+Action │ CEO │ Board │ MPM │ PM │ Dev │ QA │ Doc │
+─────────────────────┼─────┼───────┼─────┼─────┼─────┼─────┼─────┤
+View all tasks │ ✓ │ ✓ │ ✓ │ ○ │ ○ │ ○ │ ○ │
+Create task │ ✓ │ ✓ │ ✓ │ ✓ │ ○ │ │ │
+Assign task │ ✓ │ ✓ │ ✓ │ ✓ │ │ │ │
+Claim task │ │ │ │ │ ✓ │ ✓ │ ✓ │
+Update own task │ │ │ │ │ ✓ │ ✓ │ ✓ │
+Close task │ ✓ │ ✓ │ ✓ │ ✓ │ ✓ │ │ │
+Change priority │ ✓ │ ✓ │ ✓ │ ✓ │ │ │ │
+
+✓ = Full access
+○ = Own cell/tasks only
+```
+
+### 12.4 Notification Permission Matrix
+
+```
+Sender │ Recipients │
+─────────────────────┼─────────────────────────────────────────┤
+Cell PM │ Own cell members only │
+Main PM │ All PMs, any cell (escalation) │
+Product Owner │ Main PM, Board │
+Head Marketing │ Main PM, Board │
+Auditor │ Anyone (special privilege) + CEO │
+CEO │ Anyone │
+Dev/QA/Documenter │ Cannot send notifications │
+```
+
+---
+
+## 13. Implementation Roadmap
+
+### 13.1 Phase Overview
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ IMPLEMENTATION PHASES │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+Phase 0: Foundation (Weeks 1-2)
+├─► Hardware setup (Olares One integration)
+├─► Base infrastructure (Docker, networking)
+└─► Development environment
+
+Phase 1: Core Services (Weeks 3-6)
+├─► Messaging API (basic)
+├─► Task management system
+└─► Agent orchestration prototype
+
+Phase 2: Communication (Weeks 7-10)
+├─► WebSocket implementation
+├─► Transcription pipeline
+├─► Notification system
+
+Phase 3: Intelligence (Weeks 11-14)
+├─► Optimal API (RAG)
+├─► Journal API
+└─► Knowledge base indexing
+
+Phase 4: Agents (Weeks 15-20)
+├─► Agent definitions
+├─► Workflow implementation
+├─► Cell deployment
+
+Phase 5: Management (Weeks 21-24)
+├─► Kanban interfaces
+├─► Auditor dashboard
+├─► CEO overview
+
+Phase 6: Polish (Weeks 25+)
+├─► Performance tuning
+├─► Documentation
+└─► Continuous improvement
+```
+
+### 13.2 Phase 0: Foundation
+
+**Goals:**
+- Olares One operational
+- Network configured
+- Base services running
+
+**Tasks:**
+```
+□ Receive and setup Olares One
+□ Configure network (static IP, DNS)
+□ Install Docker and Docker Compose
+□ Setup development environment
+□ Configure NAS integration
+□ Test GPU capabilities
+□ Benchmark model inference
+```
+
+**Deliverables:**
+- Working Olares One with GPU access
+- Docker environment ready
+- Network topology documented
+
+### 13.3 Phase 1: Core Services
+
+**Goals:**
+- Basic messaging between agents
+- Task CRUD operations
+- Simple agent spawning
+
+**Tasks:**
+```
+□ Design database schema
+□ Implement Messaging API (REST)
+□ Implement Task API
+□ Create agent base class
+□ Build simple orchestrator
+□ Setup PostgreSQL
+□ Setup Redis (for queues)
+```
+
+**Deliverables:**
+- Messaging API v0.1
+- Task API v0.1
+- Agent framework v0.1
+
+### 13.4 Phase 2: Communication
+
+**Goals:**
+- Real-time communication
+- Message extraction
+- Formal notifications
+
+**Tasks:**
+```
+□ Implement WebSocket server
+□ Build transcription service
+□ Create message extraction pipeline
+□ Implement notification system
+□ Add channel management
+□ Build permission system
+```
+
+**Deliverables:**
+- WebSocket streaming
+- Transcription pipeline
+- Notification API
+
+### 13.5 Phase 3: Intelligence
+
+**Goals:**
+- RAG operational
+- Knowledge base populated
+- Agents can query context
+
+**Tasks:**
+```
+□ Setup Qdrant on NAS
+□ Build embedding pipeline
+□ Index existing repositories
+□ Implement Optimal API
+□ Implement Journal API
+□ Create query interface
+```
+
+**Deliverables:**
+- Optimal API v0.1
+- Journal API v0.1
+- Indexed knowledge base
+
+### 13.6 Phase 4: Agents
+
+**Goals:**
+- All agent types defined
+- Workflows implemented
+- Cells operational
+
+**Tasks:**
+```
+□ Define agent prompts per role
+□ Implement Dev workflow
+□ Implement QA workflow
+□ Implement Documenter workflow
+□ Implement PM workflows
+□ Implement Board workflows
+□ Implement Auditor workflow
+□ Deploy Backend cell
+□ Deploy Frontend cell
+□ Deploy UX/UI cell
+```
+
+**Deliverables:**
+- 17 operational agents
+- 3 functioning cells
+- Working Board
+
+### 13.7 Phase 5: Management
+
+**Goals:**
+- Visual management tools
+- Auditor capabilities
+- CEO visibility
+
+**Tasks:**
+```
+□ Build Kanban interfaces
+□ Create Auditor dashboard
+□ Create CEO overview
+□ Implement metrics collection
+□ Build reporting system
+```
+
+**Deliverables:**
+- Management UI
+- Reporting system
+- Metrics dashboard
+
+### 13.8 Phase 6: Polish
+
+**Goals:**
+- Production ready
+- Documented
+- Optimized
+
+**Tasks:**
+```
+□ Performance optimization
+□ Error handling improvements
+□ Documentation completion
+□ Testing suite
+□ Monitoring and alerting
+□ Backup procedures
+```
+
+**Deliverables:**
+- Production-ready system
+- Complete documentation
+- Operational runbooks
+
+---
+
+## 14. Development Standards & Best Practices
+
+### 14.1 Universal Principles
+
+These principles apply to ALL agents, regardless of tech stack:
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ UNIVERSAL DEVELOPMENT PRINCIPLES │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+1. NO WORK WITHOUT A TASK
+ └─► Everything tracked, everything documented
+
+2. TEST BEFORE COMMIT
+ └─► All code must pass tests before any commit
+
+3. LINT AND FORMAT
+ └─► Code must pass linting/formatting checks
+
+4. TYPE SAFETY
+ └─► Use type hints (Python) or TypeScript strictly
+
+5. DOCUMENT AS YOU GO
+ └─► Comments, docstrings, inline documentation
+
+6. SECURITY FIRST
+ └─► Validate inputs, sanitize outputs, defensive coding
+
+7. CLEAN COMMITS
+ └─► Meaningful messages, atomic changes, linked to tasks
+
+8. PEER REVIEW
+ └─► QA reviews all work before closure
+
+9. KNOWLEDGE CAPTURE
+ └─► Learnings go to knowledge base, not just memory
+```
+
+### 14.2 Stack-Specific Standards
+
+#### Python (Backend)
+
+```yaml
+python_standards:
+ package_manager: uv # Fast, modern
+ formatter: ruff format
+ linter: ruff check
+ type_checker: mypy
+ test_runner: pytest
+ min_coverage: 80%
+
+ workflow:
+ before_commit:
+ - uv run ruff format .
+ - uv run ruff check .
+ - uv run mypy src/
+ - uv run pytest
+
+ conventions:
+ - Use type hints everywhere
+ - Pydantic for data validation
+ - Async/await for I/O operations
+ - Docstrings (Google style)
+ - Keep functions < 50 lines
+ - Keep files < 500 lines
+```
+
+#### TypeScript/React (Frontend)
+
+```yaml
+typescript_standards:
+ package_manager: pnpm # Or npm/yarn
+ formatter: prettier
+ linter: eslint
+ type_checker: tsc --noEmit
+ test_runner: vitest # Or jest
+ min_coverage: 80%
+
+ workflow:
+ before_commit:
+ - pnpm format
+ - pnpm lint
+ - pnpm typecheck
+ - pnpm test
+
+ conventions:
+ - Strict TypeScript (no any)
+ - Functional components with hooks
+ - Props interfaces defined
+ - JSDoc for complex functions
+ - Component files < 300 lines
+ - Custom hooks for logic extraction
+```
+
+#### UX/UI Design
+
+```yaml
+uxui_standards:
+ design_tool: Figma # Or similar
+ component_library: Document all components
+ handoff_format: Specs + assets exported
+
+ workflow:
+ before_handoff:
+ - Component specs documented
+ - All states covered (hover, active, disabled, error)
+ - Responsive breakpoints defined
+ - Accessibility notes included
+ - Assets exported (SVG, PNG as needed)
+
+ conventions:
+ - Design tokens for colors, spacing, typography
+ - Component naming matches code
+ - Annotate interactions
+ - Document edge cases
+ - Mobile-first approach
+```
+
+### 14.3 Git Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ GIT WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+BRANCH NAMING:
+├─► feature/{task-id}-{description} # New features
+├─► fix/{task-id}-{description} # Bug fixes
+├─► refactor/{task-id}-{description} # Code improvements
+├─► docs/{task-id}-{description} # Documentation
+└─► hotfix/{task-id}-{description} # Urgent production fixes
+
+COMMIT MESSAGE FORMAT:
+┌─────────────────────────────────────────────────────────────────┐
+│ {type}({scope}): {description} │
+│ │
+│ {body - what and why} │
+│ │
+│ Task: {task-id} │
+│ Co-authored-by: {agent-name} │
+└─────────────────────────────────────────────────────────────────┘
+
+TYPES:
+├─► feat # New feature
+├─► fix # Bug fix
+├─► docs # Documentation
+├─► style # Formatting (no code change)
+├─► refactor # Code restructuring
+├─► test # Adding tests
+├─► chore # Maintenance tasks
+└─► perf # Performance improvements
+
+EXAMPLE:
+feat(auth): add rate limiting to login endpoint
+
+Implements sliding window rate limiting for login attempts.
+Uses Redis for distributed counting across instances.
+Limits: 5 attempts per minute, 20 per hour.
+
+Task: TASK-042
+Co-authored-by: BE-Dev-1
+```
+
+### 14.4 Code Review Checklist
+
+Every QA agent uses this checklist:
+
+```markdown
+## Code Review Checklist
+
+### Functionality
+- [ ] Code does what the task requires
+- [ ] Edge cases handled
+- [ ] Error states handled gracefully
+- [ ] No regressions introduced
+
+### Code Quality
+- [ ] Follows project conventions
+- [ ] No code duplication
+- [ ] Functions/methods are focused (single responsibility)
+- [ ] Naming is clear and consistent
+- [ ] No dead code or commented-out code
+
+### Type Safety
+- [ ] All types properly defined
+- [ ] No `any` types (TypeScript) or missing hints (Python)
+- [ ] Null/undefined handled properly
+
+### Testing
+- [ ] Tests exist for new functionality
+- [ ] Tests cover happy path and error cases
+- [ ] Tests are readable and maintainable
+- [ ] All tests pass
+
+### Security
+- [ ] Inputs validated
+- [ ] No sensitive data exposed
+- [ ] Authentication/authorization correct
+- [ ] No SQL injection, XSS, etc.
+
+### Performance
+- [ ] No obvious performance issues
+- [ ] Database queries optimized
+- [ ] No N+1 query problems
+- [ ] Appropriate caching considered
+
+### Documentation
+- [ ] Public APIs documented
+- [ ] Complex logic explained
+- [ ] README updated if needed
+- [ ] Changelog entry added
+```
+
+### 14.5 Automated Quality Gates
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ AUTOMATED QUALITY GATES │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+GATE 1: PRE-COMMIT (Agent-side)
+┌─────────────────────────────────────────────────────────────────┐
+│ Triggered: Before any commit │
+│ Blocking: Yes │
+│ │
+│ Python: │
+│ └─► ruff format --check │
+│ └─► ruff check │
+│ └─► mypy │
+│ │
+│ TypeScript: │
+│ └─► prettier --check │
+│ └─► eslint │
+│ └─► tsc --noEmit │
+└─────────────────────────────────────────────────────────────────┘
+
+GATE 2: PRE-PUSH (Agent-side)
+┌─────────────────────────────────────────────────────────────────┐
+│ Triggered: Before pushing to remote │
+│ Blocking: Yes │
+│ │
+│ All stacks: │
+│ └─► Run full test suite │
+│ └─► Check test coverage >= threshold │
+│ └─► Verify no secrets in code │
+└─────────────────────────────────────────────────────────────────┘
+
+GATE 3: CI/CD (System-side)
+┌─────────────────────────────────────────────────────────────────┐
+│ Triggered: On pull request │
+│ Blocking: Yes │
+│ │
+│ All stacks: │
+│ └─► Full lint/format/type check │
+│ └─► Full test suite (all Python versions if applicable) │
+│ └─► Security vulnerability scan │
+│ └─► Dependency audit │
+│ └─► Build verification │
+└─────────────────────────────────────────────────────────────────┘
+
+GATE 4: QA REVIEW (Human/Agent)
+┌─────────────────────────────────────────────────────────────────┐
+│ Triggered: When task marked "ready for review" │
+│ Blocking: Yes │
+│ │
+│ └─► Code review checklist │
+│ └─► Manual/exploratory testing │
+│ └─► Acceptance criteria verification │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 15. Task Management & Context Persistence
+
+### 15.1 The Problem
+
+AI agents have context limits. Sessions end. Memory is imperfect. Without proper task management:
+- Work gets lost between sessions
+- Agents repeat analysis unnecessarily
+- Knowledge isn't captured for future use
+- Handoffs between agents fail
+
+### 15.2 The Solution: Structured Task Records
+
+Every task creates a persistent record that:
+- Survives session boundaries
+- Enables clean handoffs between agents
+- Builds project knowledge over time
+- Reduces redundant work
+
+### 15.3 Task Directory Structure
+
+```
+.tasks/
+├── index.md # Master index of all tasks
+├── templates/ # Task templates by type
+│ ├── feature.md
+│ ├── bugfix.md
+│ ├── research.md
+│ └── documentation.md
+│
+├── active/ # Currently in-progress tasks
+│ ├── TASK-042-auth-rate-limiting/
+│ │ ├── README.md # Task overview & status
+│ │ ├── requirements.md # Detailed requirements
+│ │ ├── plan.md # Implementation plan
+│ │ ├── journal.md # Agent journey notes
+│ │ ├── findings.md # Analysis & discoveries
+│ │ ├── decisions.md # Decisions made & rationale
+│ │ ├── blockers.md # Current blockers (if any)
+│ │ ├── handoff.md # Handoff notes for Documenter
+│ │ ├── qa-review.md # QA feedback
+│ │ └── artifacts/ # Code samples, diagrams, etc.
+│ │ ├── code-samples/
+│ │ └── diagrams/
+│ │
+│ └── TASK-043-dashboard-redesign/
+│ └── ...
+│
+├── completed/ # Finished tasks (archived)
+│ ├── 2025-12/ # Organized by month
+│ │ ├── TASK-038-fix-memory-leak/
+│ │ └── TASK-039-add-dark-mode/
+│ └── 2025-11/
+│ └── ...
+│
+└── blocked/ # Tasks waiting on blockers
+ └── TASK-040-integration-api/
+ └── ...
+```
+
+### 15.4 Task Record Templates
+
+#### README.md (Required)
+
+```markdown
+# TASK-{id}: {title}
+
+## Status
+- **State**: {pending | in_progress | blocked | review | documenting | completed}
+- **Priority**: {P0 | P1 | P2 | P3}
+- **Assigned To**: {agent-id}
+- **Cell**: {backend | frontend | ux_ui}
+
+## Dates
+- **Created**: YYYY-MM-DD
+- **Started**: YYYY-MM-DD
+- **Target**: YYYY-MM-DD
+- **Completed**: YYYY-MM-DD
+
+## Overview
+{Brief description of what this task accomplishes}
+
+## Acceptance Criteria
+- [ ] Criterion 1
+- [ ] Criterion 2
+- [ ] Criterion 3
+
+## Dependencies
+- Blocked by: {TASK-XXX, TASK-YYY}
+- Blocks: {TASK-ZZZ}
+
+## Key Files
+- `path/to/main/file.py`
+- `path/to/test/file.py`
+
+## Commits
+- `abc1234` - Initial implementation
+- `def5678` - Added tests
+- `ghi9012` - Fixed edge case
+
+## Quick Context Restore
+{2-3 sentences an agent can read to immediately understand the task state}
+```
+
+#### plan.md
+
+```markdown
+# Implementation Plan: TASK-{id}
+
+## Approach
+{High-level approach description}
+
+## Sub-Tasks
+- [ ] 1. {Sub-task 1}
+ - Estimated: {time}
+ - Notes: {any notes}
+- [ ] 2. {Sub-task 2}
+- [ ] 3. {Sub-task 3}
+
+## Technical Considerations
+- {Consideration 1}
+- {Consideration 2}
+
+## Risks
+- {Risk 1}: {Mitigation}
+- {Risk 2}: {Mitigation}
+
+## Open Questions
+- [ ] {Question 1}
+- [x] {Question 2} → Answer: {answer}
+```
+
+#### journal.md
+
+```markdown
+# Agent Journey: TASK-{id}
+
+## Session 1 - YYYY-MM-DD HH:MM
+**Agent**: {agent-id}
+
+### What I Did
+- Analyzed the requirements
+- Explored the codebase around X
+- Identified approach Y
+
+### What I Learned
+- The existing system does Z because...
+- There's a related implementation in...
+
+### What I Struggled With
+- Understanding the async flow in...
+- The documentation for X was unclear
+
+### Next Steps
+- [ ] Implement the core logic
+- [ ] Add error handling
+
+---
+
+## Session 2 - YYYY-MM-DD HH:MM
+**Agent**: {agent-id}
+
+### What I Did
+...
+```
+
+#### decisions.md
+
+```markdown
+# Decisions Log: TASK-{id}
+
+## Decision 1: {Title}
+**Date**: YYYY-MM-DD
+**Decider**: {agent-id}
+
+### Context
+{What situation required a decision}
+
+### Options Considered
+1. **Option A**: {description}
+ - Pros: ...
+ - Cons: ...
+2. **Option B**: {description}
+ - Pros: ...
+ - Cons: ...
+
+### Decision
+Chose **Option A** because...
+
+### Consequences
+- We will need to...
+- This means...
+
+---
+
+## Decision 2: {Title}
+...
+```
+
+#### handoff.md (For Documenter)
+
+```markdown
+# Documentation Handoff: TASK-{id}
+
+## Summary
+{What was built, in plain language}
+
+## Documentation Needed
+- [ ] API documentation for new endpoints
+- [ ] README update for new feature
+- [ ] Architecture doc update
+- [ ] Changelog entry
+
+## Key Commits
+| Commit | Description |
+|--------|-------------|
+| abc1234 | Main implementation |
+| def5678 | Tests |
+
+## Important Conversations
+- Message ID: {id} - Discussion about approach
+- Message ID: {id} - Decision on X
+
+## Dev Notes Location
+See `journal.md` for full journey notes.
+
+## Gotchas for Documentation
+- Make sure to mention X limitation
+- The Y parameter is optional but important because...
+
+## Code Samples to Include
+```python
+# Example usage
+from module import feature
+result = feature.do_thing(param)
+```
+```
+
+### 15.5 Context Restoration Protocol
+
+When an agent picks up a task (especially one they didn't start):
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ CONTEXT RESTORATION PROTOCOL │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+STEP 1: READ TASK RECORD
+┌─────────────────────────────────────────────────────────────────┐
+│ Required reading (in order): │
+│ 1. README.md → Current state, criteria, quick context │
+│ 2. plan.md → What's the approach, what's left │
+│ 3. journal.md → What happened, what was learned │
+│ 4. decisions.md → Why things are the way they are │
+│ 5. blockers.md → Any current impediments │
+└─────────────────────────────────────────────────────────────────┘
+
+STEP 2: REVIEW ARTIFACTS
+┌─────────────────────────────────────────────────────────────────┐
+│ If applicable: │
+│ • Code samples in artifacts/ │
+│ • Related commits (git log) │
+│ • Test files │
+└─────────────────────────────────────────────────────────────────┘
+
+STEP 3: CHECK RELATED CONTEXT
+┌─────────────────────────────────────────────────────────────────┐
+│ Query knowledge base: │
+│ • Similar past tasks │
+│ • Related documentation │
+│ • Relevant conversation history │
+└─────────────────────────────────────────────────────────────────┘
+
+STEP 4: ACKNOWLEDGE STATE
+┌─────────────────────────────────────────────────────────────────┐
+│ Before starting work, add to journal: │
+│ "Resuming task. Context restored from records." │
+│ "Last state: {summary}" │
+│ "My plan: {what I'll do now}" │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 15.6 Task Indexing
+
+The master index for quick lookup:
+
+```markdown
+# Task Index
+
+## Active Tasks
+
+| ID | Title | Cell | Assigned | Priority | State | Updated |
+|----|-------|------|----------|----------|-------|---------|
+| TASK-042 | Auth Rate Limiting | Backend | BE-Dev-1 | P1 | in_progress | 2025-12-08 |
+| TASK-043 | Dashboard Redesign | Frontend | FE-Dev-2 | P2 | review | 2025-12-08 |
+| TASK-044 | New Logo | UX/UI | UX-Dev | P2 | in_progress | 2025-12-07 |
+
+## Blocked Tasks
+
+| ID | Title | Blocked By | Since |
+|----|-------|------------|-------|
+| TASK-040 | Integration API | TASK-042 | 2025-12-05 |
+
+## Recently Completed
+
+| ID | Title | Completed | Duration |
+|----|-------|-----------|----------|
+| TASK-039 | Dark Mode | 2025-12-06 | 3 days |
+| TASK-038 | Memory Leak Fix | 2025-12-04 | 1 day |
+
+## Statistics
+- Active: 3
+- Blocked: 1
+- Completed (this month): 12
+- Avg completion time: 2.3 days
+```
+
+### 15.7 Knowledge Capture Workflow
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ KNOWLEDGE CAPTURE WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+During Task Execution:
+┌─────────────────────────────────────────────────────────────────┐
+│ Agent captures in journal.md: │
+│ • What they tried │
+│ • What worked / didn't work │
+│ • Why certain approaches were chosen │
+│ • Gotchas discovered │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+On Task Completion:
+┌─────────────────────────────────────────────────────────────────┐
+│ Dev extracts to handoff.md: │
+│ • Key learnings │
+│ • Patterns used │
+│ • Pitfalls to avoid │
+│ • Reusable solutions │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+Documenter Processing:
+┌─────────────────────────────────────────────────────────────────┐
+│ Documenter creates: │
+│ • User-facing documentation │
+│ • Developer documentation │
+│ • Knowledge base articles (if applicable) │
+│ • Best practices updates (if applicable) │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+RAG Indexing:
+┌─────────────────────────────────────────────────────────────────┐
+│ Optimal API indexes: │
+│ • Task records (for "how did we solve X before?") │
+│ • New documentation │
+│ • Decision rationales │
+│ • Code patterns │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 15.8 Cross-Session State Management
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ CROSS-SESSION STATE MANAGEMENT │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+STATE THAT MUST PERSIST:
+├─► Task status and progress
+├─► Implementation plan (remaining sub-tasks)
+├─► Decisions made and rationale
+├─► Blockers and their status
+├─► Commits linked to task
+├─► Conversation references
+├─► Journal entries
+└─► Handoff information
+
+STATE STORAGE LOCATIONS:
+┌─────────────────────────────────────────────────────────────────┐
+│ Source of Truth: │
+│ • .tasks/ directory → Task records (markdown) │
+│ • PostgreSQL → Task metadata, status, relations │
+│ • Git → Code changes, commits │
+│ • Vector DB → Searchable embeddings │
+│ │
+│ Ephemeral (session only): │
+│ • Agent working memory → Current context window │
+│ • Redis → Active session state │
+└─────────────────────────────────────────────────────────────────┘
+
+SYNC PROTOCOL:
+┌─────────────────────────────────────────────────────────────────┐
+│ On session start: │
+│ 1. Load task record from .tasks/ │
+│ 2. Verify against PostgreSQL │
+│ 3. Load relevant context from Vector DB │
+│ │
+│ During session: │
+│ 1. Update journal.md incrementally │
+│ 2. Save checkpoints to .tasks/ regularly │
+│ 3. Update PostgreSQL on state changes │
+│ │
+│ On session end: │
+│ 1. Final save to .tasks/ │
+│ 2. Update PostgreSQL status │
+│ 3. Index new content to Vector DB │
+│ 4. Clear Redis session state │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 16. Agent Capabilities & Commands
+
+### 16.1 Specialized Agent Capabilities
+
+Beyond their primary roles, agents can invoke specialized capabilities:
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ SPECIALIZED CAPABILITIES │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+CODE QUALITY & REVIEW
+├─► code-review Deep code review with quality, security, maintainability
+├─► architecture Architectural consistency, SOLID principles
+├─► python-expert Advanced Python, async/await, performance
+└─► typescript-expert Advanced TypeScript, React patterns, hooks
+
+SECURITY & PERFORMANCE
+├─► security-audit Vulnerability review, OWASP compliance
+├─► api-security REST API security, auth, injection, exposure
+├─► performance Profiling, bottlenecks, caching strategies
+└─► database-optimize SQL optimization, indexing, N+1 detection
+
+DEVOPS & INFRASTRUCTURE
+├─► devops-debug Production debugging, log analysis, incidents
+├─► deployment CI/CD, Docker, Kubernetes, cloud
+├─► networking DNS, SSL/TLS, CDN, network security
+└─► database-admin Backups, replication, disaster recovery
+
+DEVELOPMENT SUPPORT
+├─► debugger Error resolution, test failures, unexpected behavior
+├─► error-detective Log searching, stack traces, root cause
+├─► api-architect RESTful design, microservices, schema design
+└─► api-documenter OpenAPI specs, SDK generation, dev docs
+
+RESEARCH & PLANNING
+├─► researcher In-depth research with sources and citations
+├─► tech-researcher Code repos, API docs, implementations
+├─► task-decomposer Breaking complex goals into actionable tasks
+└─► context-manager Managing context across multi-agent workflows
+
+SPECIALIZED
+├─► dx-optimizer Developer experience, tooling, workflows
+├─► orchestrator Complex multi-step workflow coordination
+└─► prompt-engineer Optimizes prompts, expert in prompt patterns
+```
+
+### 16.2 Available Commands
+
+Commands are pre-defined workflows agents can invoke:
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ AVAILABLE COMMANDS │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+TESTING & QUALITY
+├─► /test Run comprehensive test suite
+├─► /test-coverage Run tests with coverage report
+├─► /lint Run all linters
+├─► /typecheck Run type checker
+└─► /code-review Automated code review
+
+SECURITY
+├─► /security-audit Comprehensive vulnerability scan
+├─► /security-hardening Apply security best practices
+├─► /dependency-audit Check for vulnerable dependencies
+└─► /secrets-scan Scan for exposed secrets
+
+DOCUMENTATION
+├─► /doc-api Generate API documentation
+├─► /doc-architecture Create architecture docs
+├─► /doc-update Update existing documentation
+└─► /changelog Add changelog entry
+
+DEVELOPMENT
+├─► /debug Debug complex errors with analysis
+├─► /refactor Code refactoring with patterns
+├─► /optimize Performance optimization
+└─► /cleanup Code cleanup and dead code removal
+
+PROJECT
+├─► /status Current task status
+├─► /context Load context for current task
+├─► /handoff Prepare handoff documentation
+├─► /checkpoint Save current state
+└─► /complete Mark task complete, trigger handoff
+
+META
+├─► /help Show available commands
+├─► /capabilities Show available capabilities
+└─► /think Deep analysis mode
+```
+
+### 16.3 Tool Integrations (MCP Servers)
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ TOOL INTEGRATIONS │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+DEVELOPMENT TOOLS
+├─► context7 Library documentation retrieval
+├─► sequential-thinking Problem-solving and analysis
+├─► task-manager Task and workflow management
+└─► kanban Project board management
+
+INFRASTRUCTURE
+├─► docker Container operations and management
+├─► postgresql Database operations
+├─► redis Cache and queue operations
+└─► filesystem File operations in allowed directories
+
+EXTERNAL SERVICES
+├─► fetch Web content fetching
+├─► notion Notion workspace integration
+├─► slack Slack messaging and notifications
+└─► github Repository operations
+
+AI/ML
+├─► rag-query Query knowledge base
+├─► embeddings Generate embeddings
+└─► prompt-optimize Optimize prompts for efficiency
+```
+
+### 16.4 Quality Hooks
+
+Hooks that run automatically at specific points:
+
+```yaml
+hooks:
+ post_edit:
+ python:
+ - command: "ruff format {file}"
+ description: "Auto-format Python files"
+ - command: "ruff check {file}"
+ description: "Lint Python files"
+ blocking: true
+ - command: "mypy {file}"
+ description: "Type check Python files"
+ blocking: true
+
+ typescript:
+ - command: "prettier --write {file}"
+ description: "Auto-format TypeScript files"
+ - command: "eslint {file}"
+ description: "Lint TypeScript files"
+ blocking: true
+
+ pre_commit:
+ - command: "run-tests --changed"
+ description: "Run tests for changed files"
+ blocking: true
+
+ post_commit:
+ - command: "update-task-status"
+ description: "Update task with commit reference"
+
+ session_end:
+ - command: "save-checkpoint"
+ description: "Save task state"
+ - command: "update-journal"
+ description: "Finalize journal entry"
+```
+
+---
+
+## Appendix A: Technology Stack
+
+### Infrastructure
+| Component | Technology |
+|-----------|------------|
+| Container Runtime | Docker + Docker Compose |
+| Orchestration | Custom Python (Phase 1), K3s (future) |
+| Database | PostgreSQL |
+| Cache/Queue | Redis |
+| Vector DB | Qdrant |
+| Object Storage | MinIO (NAS) |
+
+### Backend Services
+| Service | Technology |
+|---------|------------|
+| API Framework | FastAPI |
+| WebSockets | FastAPI + websockets |
+| Task Queue | Celery / Redis Streams |
+| ORM | SQLAlchemy / Tortoise |
+
+### AI/ML
+| Component | Technology |
+|-----------|------------|
+| Cloud LLM | Claude API (Anthropic) |
+| Local LLM | Ollama / vLLM |
+| Embeddings | text-embedding-3-small / local |
+| Agent Framework | Claude Code / Custom |
+
+### Frontend (Future)
+| Component | Technology |
+|-----------|------------|
+| Framework | React / Next.js |
+| UI Library | TBD |
+| Real-time | WebSocket client |
+
+---
+
+## Appendix B: Glossary
+
+| Term | Definition |
+|------|------------|
+| **Agent** | An AI instance with a defined role, running on the Olares One |
+| **Cell** | A team unit (Backend, Frontend, UX/UI) with Devs, QA, PM, Documenter |
+| **Channel** | A communication space where agents stream and chat |
+| **Communication** | Constant stream of agent activity (reasoning, dialogue, actions) |
+| **Documenter** | Agent responsible for creating production documentation |
+| **Journal** | Personal log maintained by each agent |
+| **Notification** | Formal signal requiring acknowledgment |
+| **Optimal API** | Service providing RAG, prompt optimization, token management |
+| **RAG** | Retrieval-Augmented Generation - querying knowledge base for context |
+| **Session** | Group of messages within time/count boundaries |
+| **Task** | Atomic unit of work, wrapped in the universal lifecycle |
+| **Transcription** | Process of extracting structured messages from agent streams |
+
+---
+
+## Appendix C: Configuration Templates
+
+### Agent Configuration Example
+
+```yaml
+agent:
+ id: "be-dev-1"
+ name: "Backend Developer 1"
+ role: developer
+ team: backend
+
+ model:
+ provider: anthropic
+ name: claude-3-opus
+ fallback: local-llama-70b
+
+ system_prompt: |
+ You are a senior backend developer working on the FastAPI Guard ecosystem.
+ You follow the task lifecycle strictly and document your journey.
+ You communicate constantly in your cell channel.
+ You ask questions when unclear.
+
+ capabilities:
+ - code_execution
+ - git_operations
+ - file_management
+ - web_search
+
+ permissions:
+ can_notify: false
+ channels_read:
+ - backend-cell
+ - dev-all
+ - announcements
+ - all-hands
+ channels_write:
+ - backend-cell
+ - dev-all
+ - all-hands
+```
+
+### Channel Configuration Example
+
+```yaml
+channel:
+ id: "backend-cell"
+ name: "#backend-cell"
+ type: cell
+ team: backend
+
+ members:
+ - be-dev-1
+ - be-dev-2
+ - be-qa
+ - be-pm
+ - be-doc
+
+ silent_observers:
+ - auditor
+
+ settings:
+ message_retention_days: 90
+ max_message_length: 10000
+ allow_threads: true
+ allow_reactions: true
+```
+
+---
+
+## Appendix D: API Endpoint Summary
+
+### Messaging API
+```
+# Channels
+POST /api/v1/channels # Create channel (admin)
+GET /api/v1/channels # List accessible channels
+GET /api/v1/channels/{id} # Get channel details
+
+# Groups (within channels)
+POST /api/v1/channels/{id}/groups # Create group
+GET /api/v1/channels/{id}/groups # List groups in channel
+GET /api/v1/groups/{id} # Get group details
+PUT /api/v1/groups/{id} # Update group settings
+
+# Sessions (within groups)
+POST /api/v1/groups/{id}/sessions # Create session
+GET /api/v1/groups/{id}/sessions # List sessions in group
+GET /api/v1/sessions/{id} # Get session with messages
+PUT /api/v1/sessions/{id}/close # Close session manually
+
+# Messages (within sessions)
+POST /api/v1/sessions/{id}/messages # Send message
+GET /api/v1/sessions/{id}/messages # Get messages (paginated)
+GET /api/v1/messages/{id} # Get specific message
+PUT /api/v1/messages/{id} # Edit own message
+DELETE /api/v1/messages/{id} # Delete own message
+
+# WebSocket (real-time)
+WS /api/v1/ws/channels/{id} # Channel stream
+WS /api/v1/ws/groups/{id} # Group stream
+WS /api/v1/ws/agents/{id} # Agent's output stream
+
+# Notifications
+POST /api/v1/notifications # Send notification
+GET /api/v1/notifications # Get pending notifications
+PUT /api/v1/notifications/{id}/ack # Acknowledge notification
+
+# Search
+GET /api/v1/search/messages # Search messages (full-text)
+GET /api/v1/search/sessions # Search sessions
+```
+
+### Task API
+```
+POST /api/v1/tasks
+GET /api/v1/tasks
+GET /api/v1/tasks/{id}
+PUT /api/v1/tasks/{id}
+PUT /api/v1/tasks/{id}/claim
+PUT /api/v1/tasks/{id}/status
+POST /api/v1/tasks/{id}/handoff
+GET /api/v1/tasks/kanban/{view}
+```
+
+### Optimal API
+```
+POST /api/v1/kb/index
+GET /api/v1/kb/search
+POST /api/v1/rag/query
+POST /api/v1/prompts
+GET /api/v1/prompts/{id}
+POST /api/v1/prompts/{id}/render
+POST /api/v1/tokens/estimate
+```
+
+### Journal API
+```
+GET /api/v1/journals/{agent_id}
+POST /api/v1/journals/{agent_id}/entries
+GET /api/v1/journals/{agent_id}/entries
+GET /api/v1/journals/{agent_id}/summary
+```
+
+---
+
+## Document History
+
+| Version | Date | Author | Changes |
+|---------|------|--------|---------|
+| 1.0.0 | Dec 2025 | Renzo Franceschini | Initial blueprint |
+
+---
+
+*This document serves as the foundational blueprint for the AI Agents Company project. It should be updated as the project evolves and new requirements emerge.*
\ No newline at end of file
diff --git a/docs/MONET_SUMMARY.md b/docs/MONET_SUMMARY.md
new file mode 100644
index 00000000..4f2dd604
--- /dev/null
+++ b/docs/MONET_SUMMARY.md
@@ -0,0 +1,1656 @@
+# RoboCo & Codepanion: Strategic Summary
+
+**Date:** December 23, 2024
+**Context:** Strategic planning session covering architecture analysis, monetization, product strategy, company formation, open source strategy, platform evolution, and organizational workflow.
+
+---
+
+## Table of Contents
+
+1. [RoboCo Architecture Deep Dive](#1-roboco-architecture-deep-dive)
+2. [What Makes RoboCo Valuable](#2-what-makes-roboco-valuable)
+3. [Monetization Strategy](#3-monetization-strategy)
+4. [Target Market Analysis](#4-target-market-analysis)
+5. [Product Split: RoboCo vs Codepanion](#5-product-split-roboco-vs-codepanion)
+6. [Open Source Strategy](#6-open-source-strategy)
+7. [Platform Architecture Options](#7-platform-architecture-options)
+8. [API-First Architecture Initiative](#8-api-first-architecture-initiative)
+9. [Organizational Workflow](#9-organizational-workflow)
+10. [Codepanion Technical Specification](#10-codepanion-technical-specification)
+11. [Company Formation Options](#11-company-formation-options)
+12. [Go-to-Market Strategy](#12-go-to-market-strategy)
+13. [Honest Assessment](#13-honest-assessment)
+14. [Next Steps & Action Items](#14-next-steps--action-items)
+
+---
+
+## 1. RoboCo Architecture Deep Dive
+
+### 1.1 Core Identity
+
+RoboCo is **not** a RAG system or simple multi-agent chatbot. It is a **full AI workforce orchestration platform** — a complete implementation of an autonomous AI development team with hierarchy, workflow management, quality gates, and persistent state.
+
+### 1.2 Technical Stack
+
+| Layer | Technology |
+|-------|------------|
+| **Backend** | FastAPI (Python) |
+| **Database** | PostgreSQL with SQLAlchemy ORM |
+| **Agent Runtime** | Docker containers running Claude Code |
+| **Agent Communication** | MCP (Model Context Protocol) servers |
+| **Frontend** | Next.js 14 with TypeScript |
+| **State Management** | Zustand |
+| **Styling** | Tailwind CSS + shadcn/ui |
+
+### 1.3 Agent Architecture
+
+#### Agent Hierarchy
+
+```
+┌──────────────────────────────────────────────────────────────┐
+│ BOARD │
+│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │
+│ │Product Owner│ │Head Marketing│ │ Auditor │ │
+│ └─────────────┘ └──────────────┘ └─────────────┘ │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌──────────────────────────────────────────────────────────────┐
+│ MAIN PM │
+│ Coordinates across all cells │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ┌───────────────────┼───────────────────┐
+ ▼ ▼ ▼
+┌───────────────┐ ┌───────────────┐ ┌───────────────┐
+│ BACKEND CELL │ │ FRONTEND CELL │ │ UX/UI CELL │
+├───────────────┤ ├───────────────┤ ├───────────────┤
+│ be-pm │ │ fe-pm │ │ ux-pm │
+│ be-dev-1 │ │ fe-dev-1 │ │ ux-dev │
+│ be-dev-2 │ │ fe-dev-2 │ │ ux-qa │
+│ be-qa │ │ fe-qa │ │ ux-doc │
+│ be-doc │ │ fe-doc │ │ │
+└───────────────┘ └───────────────┘ └───────────────┘
+```
+
+#### Agent Roles (from `models/base.py`)
+
+| Role | Enum Value | Description |
+|------|------------|-------------|
+| `SYSTEM` | `system` | Internal orchestrator operations |
+| `CEO` | `ceo` | Executive oversight |
+| `PRODUCT_OWNER` | `product_owner` | Product strategy and approval |
+| `HEAD_MARKETING` | `head_marketing` | Marketing tasks |
+| `AUDITOR` | `auditor` | Quality oversight, read access to all (the "spy") |
+| `MAIN_PM` | `main_pm` | Cross-cell coordination |
+| `CELL_PM` | `cell_pm` | Cell-level task management |
+| `DEVELOPER` | `developer` | Code execution |
+| `QA` | `qa` | Quality assurance |
+| `DOCUMENTER` | `documenter` | Documentation |
+
+### 1.4 Task Lifecycle
+
+#### Status Flow (from `models/base.py`)
+
+```
+BACKLOG ──► PENDING ──► CLAIMED ──► IN_PROGRESS ───┬──► BLOCKED
+ │ │
+ │ ▼
+ │ (PM resolves)
+ │ │
+ ◄───────┘
+ │
+ ▼
+ AWAITING_QA
+ │
+ ┌──────────────┴──────────────┐
+ ▼ ▼
+ NEEDS_REVISION (QA passes)
+ │ │
+ └──────► IN_PROGRESS ▼
+ AWAITING_DOCUMENTATION
+ │
+ ▼
+ AWAITING_PM_REVIEW
+ │
+ ▼
+ COMPLETED
+```
+
+#### Task Statuses Explained
+
+| Status | Description |
+|--------|-------------|
+| `BACKLOG` | PM setup phase, session must be created before activation |
+| `PENDING` | Ready for work, orchestrator can spawn agents |
+| `CLAIMED` | Agent has claimed the task |
+| `IN_PROGRESS` | Active development |
+| `BLOCKED` | Waiting on external dependency or decision |
+| `PAUSED` | Temporarily halted |
+| `VERIFYING` | Self-verification in progress |
+| `NEEDS_REVISION` | QA rejected, needs fixes |
+| `AWAITING_QA` | Ready for QA review |
+| `AWAITING_DOCUMENTATION` | QA passed, needs docs |
+| `AWAITING_PM_REVIEW` | Docs complete, needs PM sign-off |
+| `COMPLETED` | Fully done |
+| `CANCELLED` | Abandoned |
+
+### 1.5 Orchestrator System (`runtime/orchestrator.py`)
+
+The orchestrator is the **brain** of the system. Key characteristics:
+
+#### Smart Spawning
+- **Checks for work BEFORE spawning agents** (cost-efficient)
+- Claims tasks on behalf of agents before spawning
+- Agents receive assignment at spawn time
+- No wasteful container spawns
+
+#### Docker-Based Runtime
+```python
+AGENT_IMAGE = "roboco-agent"
+AGENT_NETWORK = "roboco_default"
+```
+
+Each agent runs as an isolated Docker container with:
+- Claude Code as the runtime
+- MCP config for tool access
+- Blueprint (system prompt) mounted
+- Shared Claude auth
+
+#### Agent States (from `models/runtime.py`)
+
+| State | Description |
+|-------|-------------|
+| `IDLE` | Not running |
+| `STARTING` | Container spinning up |
+| `ACTIVE` | Working on task |
+| `WAITING_SHORT` | Brief pause (within container) |
+| `WAITING_LONG` | Terminated, will respawn when condition resolves |
+| `STOPPING` | Graceful shutdown in progress |
+| `OFFLINE` | Container stopped |
+| `ERROR` | Failed state |
+
+#### Task Routing Intelligence
+
+The orchestrator classifies tasks based on complexity and keywords:
+
+```python
+# Board-level keywords
+_BOARD_KEYWORDS = {"roadmap", "architecture", "security", "budget",
+ "hiring", "strategy", "vision", "milestone",
+ "release", "launch"}
+
+# PM coordination keywords
+_PM_KEYWORDS = {"coordinate", "integration", "cross-team", "sync",
+ "planning", "milestone", "dependencies", "review"}
+```
+
+Routing decision tree:
+1. **Board keywords** → `product-owner`
+2. **High complexity or cross-team** → `main-pm`
+3. **PM keywords or medium complexity** → Cell PM
+4. **Low complexity, single team** → Direct to developer
+
+#### Dispatcher Loop
+
+The orchestrator runs background dispatchers every 30 seconds:
+
+1. `_dispatch_pm_work()` — Routes new tasks to appropriate level
+2. `_dispatch_pm_closure_work()` — Checks parent tasks ready to close
+3. `_dispatch_dev_work()` — Spawns devs for assigned tasks
+4. `_dispatch_qa_work()` — Spawns QA for awaiting_qa tasks
+5. `_dispatch_doc_work()` — Spawns documenters
+6. `_dispatch_pm_review_work()` — Spawns PMs for final review
+7. `_dispatch_marketing_work()` — Handles marketing tasks
+8. `_dispatch_blocker_work()` — Handles blocked tasks
+9. `_dispatch_escalation_work()` — Handles escalations
+10. `_dispatch_approval_work()` — Handles approval requests
+11. `_dispatch_audit_work()` — Triggers auditor when needed
+
+### 1.6 MCP Tool System
+
+Each agent gets access to 4 MCP servers:
+
+#### roboco-task (`mcp/task_server.py`)
+- `roboco_task_scan()` — Find available work
+- `roboco_task_get(task_id)` — Get task details
+- `roboco_task_claim(task_id)` — Claim a task
+- `roboco_task_start(task_id)` — Begin work
+- `roboco_task_plan(task_id, ...)` — Create execution plan
+- `roboco_task_progress(task_id, %, msg)` — Report progress
+- `roboco_task_checkpoint(task_id, ...)` — Save checkpoint
+- `roboco_task_block(task_id, reason)` — Mark blocked
+- `roboco_task_unblock(task_id)` — Remove block
+- `roboco_task_complete(task_id)` — Mark complete
+- `roboco_task_create(...)` — Create subtask
+- `roboco_task_qa_pass(task_id)` — QA approval
+- `roboco_task_qa_fail(task_id, notes)` — QA rejection
+- `roboco_task_docs_complete(task_id)` — Documentation done
+- `roboco_agent_idle()` — Signal no more work
+
+#### roboco-message (`mcp/message_server.py`)
+- `roboco_message_send(channel, content, type)` — Send message
+- `roboco_message_read(channel, limit)` — Read channel history
+- `roboco_message_reply(message_id, content)` — Reply to message
+
+#### roboco-notify (`mcp/notify_server.py`)
+- `roboco_notify_send(to, subject, body, priority)` — Send notification
+- `roboco_notify_ack(notification_id)` — Acknowledge
+- `roboco_escalate(to, subject, body)` — Escalate issue
+
+#### roboco-journal (`mcp/journal_server.py`)
+- `roboco_journal_entry(type, title, content)` — Add journal entry
+- `roboco_journal_decision(...)` — Log decision
+- `roboco_journal_learning(...)` — Log learning
+- `roboco_journal_struggle(...)` — Log struggle
+
+### 1.7 Communication System
+
+#### Hierarchy
+
+```
+Channel (e.g., "backend-cell")
+ └── Group (e.g., "general", "code-review")
+ └── Session (scoped discussion)
+ └── Messages
+```
+
+#### Channel Types (from `models/base.py`)
+
+| Type | Description |
+|------|-------------|
+| `CELL` | Internal team communication |
+| `CROSS_CELL` | Coordination between teams |
+| `MANAGEMENT` | PM and board communications |
+| `SPECIAL` | Announcements, all-hands |
+
+#### Message Types
+
+| Type | Description |
+|------|-------------|
+| `REASONING` | Agent's internal thought process |
+| `DIALOGUE` | Normal conversation |
+| `DECISION` | Decision announcement |
+| `ACTION` | Action taken |
+| `BLOCKER` | Blocker announcement |
+| `TECHNICAL` | Technical discussion |
+
+#### Session Scopes
+
+| Scope | Description |
+|-------|-------------|
+| `INITIATIVE` | High-level initiative discussion |
+| `CELL` | Cell-wide discussion |
+| `TASK` | Task-specific discussion |
+
+### 1.8 Database Schema
+
+#### Core Tables (from `db/tables.py`)
+
+| Table | Purpose |
+|-------|---------|
+| `agents` | Agent definitions and state |
+| `tasks` | Task records with full lifecycle |
+| `channels` | Communication channels |
+| `groups` | Channel subdivisions |
+| `sessions` | Scoped discussions |
+| `session_tasks` | Many-to-many session↔task links |
+| `messages` | All messages |
+| `notifications` | Formal notifications |
+| `journals` | Agent journals |
+| `journal_entries` | Individual journal entries |
+| `handoffs` | Documentation handoffs |
+
+#### Key Relationships
+
+- **Task → Parent Task**: Subtask hierarchy
+- **Task → Agent (created_by)**: Who created it
+- **Task → Agent (assigned_to)**: Who's working on it
+- **Session ↔ Task**: Many-to-many via `session_tasks`
+- **Message → Session**: Messages belong to sessions
+- **Journal → Agent**: One journal per agent
+- **Handoff → Task**: One handoff per completed task
+
+### 1.9 Frontend Structure (`roboco-panel/`)
+
+```
+src/
+├── app/
+│ ├── (dashboard)/
+│ │ ├── agents/ # Agent management
+│ │ ├── auditor/ # Auditor dashboard
+│ │ ├── communications/ # Channels & messages
+│ │ ├── journals/ # Agent journals
+│ │ ├── kanban/ # Task boards
+│ │ ├── metrics/ # System metrics
+│ │ ├── notifications/ # Notification center
+│ │ ├── overview/ # Dashboard home
+│ │ ├── settings/ # Configuration
+│ │ └── tasks/ # Task management
+│ └── layout.tsx
+├── components/
+│ ├── agents/ # Agent-related components
+│ ├── auditor/ # Auditor components
+│ ├── communications/ # Chat/messaging components
+│ ├── dashboard/ # Dashboard widgets
+│ ├── journals/ # Journal components
+│ ├── kanban/ # Kanban board components
+│ │ ├── core/ # Board, column, card
+│ │ ├── shared/ # Shared utilities
+│ │ └── views/ # Different board views
+│ ├── layout/ # Layout components
+│ ├── notifications/ # Notification components
+│ ├── tasks/ # Task components
+│ └── ui/ # shadcn/ui components
+├── hooks/ # React hooks
+├── lib/ # Utilities
+├── store/ # Zustand stores
+│ ├── notifications-store.ts
+│ └── ui-store.ts
+└── types/
+ └── index.ts # TypeScript types matching backend
+```
+
+---
+
+## 2. What Makes RoboCo Valuable
+
+### 2.1 Differentiators from Simple Multi-Agent Systems
+
+| Feature | RoboCo | Typical Multi-Agent |
+|---------|--------|---------------------|
+| **Task Lifecycle** | Full workflow management | Ad-hoc execution |
+| **Quality Gates** | QA → Docs → PM Review | None or optional |
+| **Hierarchy** | Real org structure | Flat or undefined |
+| **Cost Efficiency** | On-demand spawning | Always running |
+| **State Persistence** | Full DB-backed state | In-memory or none |
+| **Waiting States** | Hibernate and respawn | Block or fail |
+| **Audit Trail** | Complete history | Limited or none |
+| **Tool Access Control** | MCP with permissions | Unrestricted |
+
+### 2.2 Differentiators from RAG Systems
+
+| Feature | RoboCo | RAG Systems |
+|---------|--------|-------------|
+| **Purpose** | Task execution | Information retrieval |
+| **Agents** | 18 specialized roles | Single retriever |
+| **Workflow** | Full task lifecycle | Query → Response |
+| **State** | Persistent across sessions | Stateless |
+| **Quality** | Built-in QA/review | None |
+| **Output** | Code, docs, artifacts | Text responses |
+
+### 2.3 Core Value Propositions
+
+1. **Workflow Orchestration** — Not just "agents chat", but actual task lifecycle management
+2. **Quality Gates** — QA review happens automatically, not when you remember
+3. **Context Persistence** — Sessions, journals, handoffs preserve knowledge
+4. **Coordination** — When one agent finishes, the next picks up automatically
+5. **Cost Efficiency** — On-demand spawning, waiting state hibernation
+6. **Accountability** — Full audit trail, decision logging, progress tracking
+
+---
+
+## 3. Monetization Strategy
+
+### 3.1 Monetization Paths Considered
+
+| Path | Description | Pros | Cons |
+|------|-------------|------|------|
+| **Platform/SaaS** | Companies deploy their own AI workforce | Recurring revenue, scalable | Long sales cycle |
+| **Managed Service** | You run the agents, customers submit projects | Higher margin, more control | Requires operations |
+| **Open Core + Enterprise** | Open source core, sell enterprise features | Builds community fast | Revenue delayed |
+| **API/Infrastructure** | Orchestration-as-a-Service | Platform play | Commoditization risk |
+
+### 3.2 Target Market Decision
+
+**Chosen path:** Prosumer/indie dev market (like Cursor, Claude Pro, Replit)
+
+**Rationale:**
+- Lower friction than enterprise
+- Direct distribution (no sales team needed)
+- Fast iteration based on feedback
+- Personal pain point (solo dev experience)
+
+---
+
+## 4. Target Market Analysis
+
+### 4.1 The Solo Founder Pain Point
+
+When you're alone (or 2 people), constant context-switching:
+
+| Hat | Activity | Time Spent | Quality |
+|-----|----------|------------|---------|
+| **PM** | Prioritization, planning | 10% | Rushed |
+| **Dev** | Writing code | 60% | Good |
+| **QA** | Testing | 10% | Skipped often |
+| **Docs** | Documentation | 5% | Neglected |
+| **Review** | Code review | 0% | None (no one to review) |
+
+**The problem:** Some hats get neglected (usually QA and docs)
+
+### 4.2 Two Product Modes
+
+| Mode | Description | Trust Required | Value |
+|------|-------------|----------------|-------|
+| **Mode A: Autonomous** | Agents DO the work | High | High |
+| **Mode B: Collaborative** | Agents ASSIST your work | Low | Medium-High |
+
+Mode A = Current RoboCo implementation
+Mode B = Codepanion opportunity
+
+---
+
+## 5. Product Split: RoboCo vs Codepanion
+
+### 5.1 RoboCo — "Your AI Development Team"
+
+**Positioning:** Autonomous execution. You describe, they build.
+
+**Core Features:**
+- Full agent hierarchy (PM → Dev → QA → Doc)
+- Task lifecycle management
+- Agents spawn, work, coordinate, complete
+- User is the "CEO" — approves, agents execute
+
+**Target Customer:** Solo founder who wants to delegate, not do.
+*"I have the vision, I need execution."*
+
+**Vibe:** Enterprise-y. Serious. Professional.
+*"Hire a team without hiring."*
+
+### 5.2 Codepanion — "Your AI Dev Partner"
+
+**Positioning:** Collaborative. You code, they help.
+
+**Core Features:**
+- **Reviewer** — Reviews your PRs, catches bugs
+- **Documenter** — Updates docs when you ship
+- **QA** — Writes/runs tests for your changes
+- **Rubber Duck** — Thinks through architecture with you
+- **PM** — Helps break down ideas into tasks
+
+**Target Customer:** Solo dev who wants to stay hands-on but needs backup.
+*"I love coding, I just need help with the boring parts."*
+
+**Vibe:** Friendly. Lightweight. Developer tool.
+*"A cofounder who never sleeps."*
+
+### 5.3 Shared Architecture
+
+```
+┌─────────────────────────────────────────────┐
+│ Shared Platform Core │
+│ ┌───────────────────────────────────────┐ │
+│ │ Orchestrator, MCP, Tasks, Sessions │ │
+│ │ Messages, Journals, Notifications │ │
+│ └───────────────────────────────────────┘ │
+└─────────────────────────────────────────────┘
+ ↑ ↑
+ ┌────┴────┐ ┌────┴─────┐
+ │ RoboCo │ │Codepanion│
+ │ Panel │ │ App │
+ └─────────┘ └──────────┘
+ Full dashboard Lightweight
+ Kanban, agents CLI / Git-integrated
+ Enterprise feel Dev tool feel
+```
+
+Same backend infrastructure, different frontend experiences and UX.
+
+### 5.4 Pricing Structure
+
+| Tier | RoboCo | Codepanion |
+|------|--------|------------|
+| **Free** | 10 tasks/month | 50 reviews/month |
+| **Pro** | $99/month | $19/month |
+| **Team** | $199/seat | $39/seat |
+
+RoboCo is premium — paying for autonomous work.
+Codepanion is accessible — priced like a dev tool.
+
+---
+
+## 6. Open Source Strategy
+
+### 6.1 The Decision
+
+**Codepanion CLI:** Open source (MIT)
+**RoboCo Panel:** Closed source
+**RoboCo API/Backend:** Closed source (SaaS)
+
+### 6.2 Rationale for Open Source Codepanion
+
+**The CLI itself isn't the moat.** It's ~200 lines of code wrapping Claude. Anyone could build it in an afternoon. Keeping it closed doesn't protect anything meaningful.
+
+**Open source is distribution:**
+- Stars → visibility
+- Forks → community investment
+- PRs → free improvements
+- Trust → "I can see what it does with my code"
+
+**The money isn't in the CLI.** It's in:
+
+| Free (Open Source) | Paid |
+|-------------------|------|
+| `codepanion review` | Cloud sync (review history) |
+| `codepanion docs` | Team sharing |
+| `codepanion think` | Custom rules/prompts |
+| Local, stateless | CI/CD integration |
+| | **RoboCo upgrade path** |
+
+This is the **Terraform model.** CLI is free and open. Terraform Cloud is where HashiCorp makes money.
+
+### 6.3 What Stays Closed
+
+**RoboCo itself.** That's where the real IP is:
+- The orchestration engine
+- The agent hierarchy and workflow
+- The dispatcher logic
+- The quality gates
+- The MCP integration
+
+That's defensible. That's what companies would pay for.
+
+**Codepanion is the free sample. RoboCo is the product.**
+
+### 6.4 Potential Differentiation Angle
+
+Focus on what nobody else does well: **QA and docs automation.**
+
+Everyone skips those. If Codepanion auto-generated tests and updated README after every commit, *that's* differentiated from Copilot/Cursor which focus on code generation.
+
+---
+
+## 7. Platform Architecture Options
+
+### 7.1 The Core Question
+
+If Codepanion in "connected mode" talks to the same backend as RoboCo... where's the line? What's open, what's closed, what's the product?
+
+### 7.2 Option A: Two Completely Separate Products
+
+```
+┌─────────────────┐ ┌─────────────────┐
+│ Codepanion │ │ RoboCo │
+│ (standalone) │ │ (full stack) │
+│ Calls Claude │ │ Orchestrator │
+│ No backend │ │ Full backend │
+│ Open source │ │ Closed │
+└─────────────────┘ └─────────────────┘
+```
+
+**Pros:** Simple. Clear separation.
+**Cons:** Codepanion can't have history, sync, teams, learning. It's just a dumb wrapper forever.
+
+### 7.3 Option B: Shared Backend, CLI Open, Backend Closed (SaaS)
+
+```
+┌─────────────────────────────────────────────┐
+│ RoboCo API (Closed SaaS) │
+└─────────────────────────────────────────────┘
+ ↑ ↑
+ ┌────┴────┐ ┌────┴─────┐
+ │ RoboCo │ │Codepanion│
+ │ Panel │ │ CLI │
+ │ Closed │ │ Open │
+ └─────────┘ └──────────┘
+```
+
+Codepanion CLI is open source. Works standalone (no server) OR connects to RoboCo API (paid).
+
+**This is the Supabase/Vercel model.** Open source client, proprietary backend.
+
+### 7.4 Option C: The API IS the Product
+
+```
+┌─────────────────────────────────────────────┐
+│ RoboCo Platform │
+│ "Orchestration-as-a-Service for AI" │
+│ │
+│ POST /agents - spawn agents │
+│ POST /tasks - create tasks │
+│ POST /workflows - run workflows │
+│ WS /events - real-time updates │
+│ POST /webhooks - callbacks │
+└─────────────────────────────────────────────┘
+ ↑ ↑ ↑
+ ┌────┴────┐ ┌────┴─────┐ ┌────┴────┐
+ │ RoboCo │ │Codepanion│ │ Third │
+ │ Panel │ │ CLI │ │ Party │
+ └─────────┘ └──────────┘ └─────────┘
+```
+
+You're selling **orchestration-as-a-service**. Like Twilio for AI agents.
+
+RoboCo Panel could even be open source — it's just a reference frontend. The value is in the *running* API, not the code.
+
+**This is the Stripe model.** Dashboard is just a client of their own API.
+
+### 7.5 Option D: Open Core, Sell Enterprise
+
+Everything is open source. You sell:
+- Managed hosting (so they don't have to run it)
+- Enterprise features (SSO, audit logs, SLAs)
+- Support contracts
+
+**This is the GitLab/Grafana model.**
+
+### 7.6 The Decision: B → C
+
+**Start B, earn your way to C.**
+
+| Phase | Architecture | Focus |
+|-------|--------------|-------|
+| **Phase 1 (Now)** | Option B | Find users, find fit, subscription revenue |
+| **Phase 2 (PMF)** | B → C | Stabilize API, watch for platform signals |
+| **Phase 3 (Scale)** | Option C | Enable others, usage-based pricing |
+
+**Signal to watch for:** Someone asks "can I integrate this into my own tool?"
+
+---
+
+## 8. API-First Architecture Initiative
+
+### 8.1 The Principle
+
+**The API must be the only way in.**
+
+No backdoors. No direct DB access from frontends. No "just this once" shortcuts.
+
+If we can't use our own API to build our own products, no one else can either.
+
+### 8.2 Why This Matters
+
+For the B → C progression to work:
+1. Every action the Panel takes goes through the public API
+2. Every action Codepanion takes goes through the public API
+3. Internal services communicate through well-defined interfaces
+4. The API is documented, consistent, and pleasant to use
+
+### 8.3 The Key Discipline
+
+**From day one, build Panel and CLI as if they were third-party apps.**
+
+- No backdoors into the database
+- Everything goes through the API
+- If the API is annoying to use, fix the API — don't hack around it
+
+This way, when you flip the switch to Option C, the API is already battle-tested.
+
+### 8.4 Recommended Code Structure
+
+```
+roboco/
+├── core/ # The engine (orchestrator, agents, workflows)
+├── api/ # HTTP interface to core
+├── panel/ # or separate repo
+└── ...
+```
+
+Clean separation means `core` could be wrapped by different interfaces later — your API, a CLI, a Terraform provider, whatever.
+
+### 8.5 First Initiative: API Audit
+
+**Task 1: Backdoor Audit**
+- Identify every place where Panel/internal code bypasses the API
+- Flag direct DB access, direct core imports
+- Produce audit report with file, line, severity
+
+**Task 2: Critical Path Cleanup**
+- Fix only the blockers — stuff that would break if a third party used the API
+- Route those flows through the API properly
+
+**Task 3: Codepanion Integration Point**
+- Define minimal API surface Codepanion needs:
+ ```
+ POST /sessions - create a review session
+ POST /sessions/:id/messages - add a message (diff, review)
+ GET /sessions - list past reviews
+ ```
+- Working endpoints that Codepanion CLI can hit
+
+---
+
+## 9. Organizational Workflow
+
+### 9.1 The Ideal Flow
+
+```
+CEO (Human)
+ │
+ │ Detailed feature request / complex idea
+ ▼
+┌──────────────────────────────────────────────────────────────┐
+│ BOARD │
+│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │
+│ │Product Owner│ │Head Marketing│ │ Auditor │ │
+│ │ (Strategy) │ │ (Positioning)│ │ (Spy) │ │
+│ └──────┬──────┘ └──────┬───────┘ └──────┬──────┘ │
+│ │ │ │ │
+│ └────────────────┼─────────────────┘ │
+│ │ │
+│ Break down in their area of expertise │
+│ Prepare tasks for Main PM │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌──────────────────────────────────────────────────────────────┐
+│ MAIN PM │
+│ │
+│ Receives tasks from Board │
+│ Breaks down further per team cell │
+│ Coordinates cross-cell dependencies │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ┌──────────────────┼──────────────────┐
+ ▼ ▼ ▼
+┌───────────────┐ ┌───────────────┐ ┌───────────────┐
+│ BACKEND CELL │ │ FRONTEND CELL │ │ UX/UI CELL │
+│ │ │ │ │ │
+│ Cell PM │ │ Cell PM │ │ Cell PM │
+│ │ │ │ │ │ │ │ │
+│ ├─► Dev │ │ ├─► Dev │ │ ├─► Dev │
+│ ├─► QA │ │ ├─► QA │ │ ├─► QA │
+│ └─► Doc │ │ └─► Doc │ │ └─► Doc │
+│ │ │ │ │ │
+│ Escalates ▲ │ │ Escalates ▲ │ │ Escalates ▲ │
+└───────────────┘ └───────────────┘ └───────────────┘
+```
+
+### 9.2 Board Role Clarifications
+
+| Role | Description | Key Responsibilities |
+|------|-------------|---------------------|
+| **Product Owner** | Strategy & vision | Define API contracts, prioritize cleanup work, approve architecture decisions |
+| **Head of Marketing** | Positioning & content | Prepare positioning, draft README/landing content, identify launch channels |
+| **Auditor** | The "spy" — oversight | Conduct backdoor audit, verify fixes, establish ongoing monitoring, read access to all channels |
+
+### 9.3 Task-Session-Journal Binding
+
+Every task gets:
+1. **A messaging session** in a channel for that task/subtask
+2. **Journal entries** from agents working on it
+3. **Documentation** (approach TBD — may be same session or separate)
+
+```
+Task
+ ├── Session (discussion, decisions, progress)
+ ├── Journal Entries (agent reflections, learnings, struggles)
+ └── Documentation (technical docs, user docs)
+```
+
+### 9.4 Communication Channels
+
+| Channel | Purpose | Participants |
+|---------|---------|--------------|
+| `management` | Board discussions | PO, HoM, Auditor, CEO |
+| `cross-cell` | Main PM coordination | Main PM, Cell PMs |
+| `backend-cell` | Backend team work | BE-PM, BE-Devs, BE-QA, BE-Doc |
+| `frontend-cell` | Frontend team work | FE-PM, FE-Devs, FE-QA, FE-Doc |
+| `uxui-cell` | UX/UI team work | UX-PM, UX-Dev, UX-QA, UX-Doc |
+
+### 9.5 CEO Directive Template
+
+When the CEO (human) wants to initiate work:
+
+```markdown
+# CEO Directive: [Initiative Name]
+
+**From:** CEO
+**To:** Board (Product Owner, Head of Marketing, Auditor)
+**Priority:** [High/Medium/Low]
+**Date:** [Date]
+
+---
+
+## Strategic Context
+[Why this matters, where it fits in the bigger picture]
+
+## Objective
+[What we're trying to achieve]
+
+## Success Criteria
+- [ ] [Measurable outcome 1]
+- [ ] [Measurable outcome 2]
+
+## Board Responsibilities
+
+### Product Owner
+1. [Responsibility 1]
+2. [Responsibility 2]
+
+### Head of Marketing
+1. [Responsibility 1]
+2. [Responsibility 2]
+
+### Auditor
+1. [Responsibility 1]
+2. [Responsibility 2]
+
+## Deliverables to Main PM
+[What gets handed off when board alignment is complete]
+
+## Constraints
+[Timeline, scope, principles]
+
+## Notes
+[Additional context, future considerations]
+
+---
+
+**CEO**
+```
+
+---
+
+## 10. Codepanion Technical Specification
+
+### 10.1 MVP Scope
+
+**Single feature:** `codepanion review`
+
+Reviews code changes (staged, committed, or working directory) and provides actionable feedback.
+
+### 10.2 Project Structure
+
+```
+codepanion/
+├── pyproject.toml
+├── README.md
+├── src/
+│ └── codepanion/
+│ ├── __init__.py
+│ ├── cli.py # Typer CLI entry point
+│ ├── config.py # User config (~/.codepanion/config.toml)
+│ ├── git.py # Git operations (diff, log, staged files)
+│ ├── commands/
+│ │ ├── __init__.py
+│ │ ├── review.py # codepanion review
+│ │ ├── docs.py # codepanion docs (future)
+│ │ └── think.py # codepanion think (future)
+│ └── agents/
+│ ├── __init__.py
+│ ├── base.py # Thin agent runner
+│ └── reviewer.py # Review agent logic
+└── tests/
+ └── ...
+```
+
+### 10.3 Dependencies
+
+```toml
+[project]
+name = "codepanion"
+version = "0.1.0"
+requires-python = ">=3.11"
+dependencies = [
+ "typer>=0.9.0",
+ "anthropic>=0.40.0",
+ "rich>=13.0.0",
+]
+
+[project.scripts]
+codepanion = "codepanion.cli:app"
+```
+
+### 10.4 CLI Implementation
+
+#### Entry Point (`cli.py`)
+
+```python
+import typer
+from codepanion.commands import review
+
+app = typer.Typer(
+ name="codepanion",
+ help="Your AI dev partner",
+ no_args_is_help=True,
+)
+
+app.add_typer(review.app, name="review")
+
+if __name__ == "__main__":
+ app()
+```
+
+#### Review Command (`commands/review.py`)
+
+```python
+import typer
+from codepanion.git import get_staged_diff, get_working_diff, get_commit_diff
+from codepanion.agents.reviewer import ReviewerAgent
+
+app = typer.Typer()
+
+@app.callback(invoke_without_command=True)
+def review(
+ commit: str = typer.Option(None, "--commit", "-c", help="Review a specific commit"),
+ staged: bool = typer.Option(False, "--staged", "-s", help="Review staged changes"),
+ all_changes: bool = typer.Option(True, help="Review all uncommitted changes"),
+):
+ """Review code changes with AI."""
+
+ # Get the diff
+ if commit:
+ diff = get_commit_diff(commit)
+ context = f"Commit: {commit}"
+ elif staged:
+ diff = get_staged_diff()
+ context = "Staged changes"
+ else:
+ diff = get_working_diff()
+ context = "Working directory changes"
+
+ if not diff.strip():
+ typer.echo("No changes to review.")
+ raise typer.Exit()
+
+ typer.echo(f"🔍 Reviewing {context}...\n")
+
+ # Run review
+ agent = ReviewerAgent()
+ result = agent.review(diff)
+
+ # Output
+ typer.echo(result.markdown)
+
+ if result.issues:
+ typer.echo(f"\n⚠️ {len(result.issues)} issues found")
+ raise typer.Exit(1)
+ else:
+ typer.echo("\n✅ Looks good!")
+```
+
+#### Git Operations (`git.py`)
+
+```python
+import subprocess
+
+def run_git(*args) -> str:
+ result = subprocess.run(
+ ["git", *args],
+ capture_output=True,
+ text=True,
+ )
+ return result.stdout
+
+def get_staged_diff() -> str:
+ return run_git("diff", "--cached")
+
+def get_working_diff() -> str:
+ return run_git("diff", "HEAD")
+
+def get_commit_diff(commit: str) -> str:
+ return run_git("show", commit, "--format=")
+```
+
+#### Reviewer Agent (`agents/reviewer.py`)
+
+```python
+from dataclasses import dataclass
+from anthropic import Anthropic
+
+SYSTEM_PROMPT = """You are a senior code reviewer. You review diffs and provide actionable feedback.
+
+Your review should:
+1. Identify bugs, security issues, and logic errors
+2. Suggest improvements (but don't nitpick style)
+3. Point out missing error handling or edge cases
+4. Be concise and actionable
+
+Format your response as:
+
+## Summary
+One sentence overall assessment.
+
+## Issues
+- **[SEVERITY]** file.py:123 — Description of issue
+
+## Suggestions
+- file.py:45 — Optional improvement idea
+
+If the code looks good, just say so briefly. Don't invent problems.
+"""
+
+@dataclass
+class ReviewResult:
+ markdown: str
+ issues: list[dict]
+ suggestions: list[dict]
+
+class ReviewerAgent:
+ def __init__(self):
+ self.client = Anthropic()
+
+ def review(self, diff: str) -> ReviewResult:
+ response = self.client.messages.create(
+ model="claude-sonnet-4-20250514",
+ max_tokens=4096,
+ system=SYSTEM_PROMPT,
+ messages=[{
+ "role": "user",
+ "content": f"Review this diff:\n\n```diff\n{diff}\n```"
+ }]
+ )
+
+ content = response.content[0].text
+
+ # Parse issues/suggestions from markdown
+ issues = [] # TODO: parse from response
+ suggestions = []
+
+ return ReviewResult(
+ markdown=content,
+ issues=issues,
+ suggestions=suggestions,
+ )
+```
+
+### 10.5 Usage Examples
+
+```bash
+# Install
+pip install codepanion # or: uv pip install codepanion
+
+# Review staged changes before commit
+git add .
+codepanion review --staged
+
+# Review a specific commit
+codepanion review --commit abc123
+
+# Review all uncommitted changes
+codepanion review
+```
+
+### 10.6 Future Commands
+
+| Command | Description | Priority |
+|---------|-------------|----------|
+| `codepanion review` | Review code changes | MVP |
+| `codepanion docs` | Update docs based on changes | P1 |
+| `codepanion think "question"` | Rubber duck mode | P1 |
+| `codepanion qa` | Generate tests for changes | P2 |
+| `codepanion breakdown "idea"` | Break down into tasks | P2 |
+
+### 10.7 Modes of Operation
+
+#### Mode A: Standalone (MVP)
+```
+codepanion review
+ │
+ ▼
+┌─────────────────┐
+│ Local Agent │ ← Just calls Claude API directly
+│ (no server) │ with reviewer system prompt
+└─────────────────┘
+ │
+ ▼
+ Markdown output
+```
+
+No RoboCo server needed. Self-contained. Ships fast.
+
+#### Mode B: Connected (Later)
+```
+codepanion review
+ │
+ ▼
+┌─────────────────┐
+│ RoboCo API │ ← Full orchestrator, sessions, history
+│ (your server) │
+└─────────────────┘
+ │
+ ▼
+ Review stored, tracked, searchable
+```
+
+Unlocks: history, learning from past reviews, team features.
+
+---
+
+## 11. Company Formation Options
+
+### 11.1 Context
+
+Based in **Italy**. Looking for the simplest path to legally charge for software.
+
+### 11.2 Option 1: Partita IVA (Italian Freelancer)
+
+**What it is:** Italian self-employment registration
+
+**Regime Forfettario (Simplified Regime):**
+- Available if revenue under €85,000/year
+- **5% flat tax** for first 5 years (if new activity)
+- **15% flat tax** after that
+- Minimal paperwork compared to company
+
+**Pros:**
+- Simplest to start
+- Cheapest ongoing costs
+- Good enough until significant revenue
+
+**Cons:**
+- Personal liability (you = the business)
+- Looks less "serious" to larger customers
+- Limited deductions in forfettario regime
+
+**Costs:**
+- Setup: €200-500 (commercialista fees)
+- Annual: €500-1000 (commercialista for simple SaaS)
+- INPS contributions: ~25% of revenue (painful)
+
+**Verdict:** Best starting point. Revisit when making €50k+/year.
+
+### 11.3 Option 2: SRL (Italian LLC)
+
+**What it is:** Italian limited liability company
+
+**Pros:**
+- Limited liability
+- More "serious" appearance
+- Better for investors (if ever needed)
+
+**Cons:**
+- €3,000-5,000+ to form (notary required 🇮🇹)
+- Mandatory accountant (€1,500-3,000/year minimum)
+- Corporate tax + bureaucracy
+- Overkill for early-stage SaaS
+
+**Costs:**
+- Setup: €3,000-5,000
+- Annual: €2,000-5,000 (accounting, fees)
+- Corporate tax: IRES 24% + IRAP ~4%
+
+**Verdict:** Wait until revenue justifies the overhead. Maybe €100k+/year.
+
+### 11.4 Option 3: Estonia OÜ (e-Residency)
+
+**What it is:** Estonian private limited company, managed 100% online
+
+**How it works:**
+1. Apply for e-Residency card (~€100, takes 3-6 weeks)
+2. Form OÜ online (~€200-300)
+3. Manage everything through Estonian service providers
+
+**Pros:**
+- 100% online setup and management
+- EU company (looks legit, can sell to EU easily)
+- 0% corporate tax on reinvested profits
+- 20% tax only on distributed profits (dividends)
+- Stripe, Wise, everything works
+- Escape Italian bureaucracy for the company
+
+**Cons:**
+- Still need registered agent in Estonia (~€50-100/month)
+- Accounting required (~€50-100/month for simple SaaS)
+- You're still Italian tax resident → pay Italian taxes on personal income
+- VAT compliance if selling B2C in EU (MOSS headache)
+- Some banking friction (no physical presence)
+
+**Costs:**
+- e-Residency: €100-120 (one-time)
+- Company formation: €200-300
+- Registered agent: €50-100/month
+- Accounting: €50-100/month
+- State fee: €100/year
+- **Total ongoing:** ~€150-250/month
+
+**Important:** You still pay Italian taxes on what you pay yourself (salary or dividends). The Estonia company itself is clean to run, but you personally remain Italian tax resident.
+
+**Verdict:** Good option once you're making €20k+/year and want to separate business from personal.
+
+### 11.5 Option 4: Stripe Atlas (US LLC)
+
+**What it is:** Stripe's turnkey company formation service
+
+**Includes:**
+- Delaware LLC formation
+- Stripe account
+- Mercury or SVB bank account
+- Registered agent for 1 year
+- Legal templates
+
+**Cost:** $500 one-time + ~$200/year ongoing (registered agent)
+
+**Pros:**
+- Very fast (days, not weeks)
+- US entity (good for US customers)
+- All-in-one package
+- Popular with international founders
+
+**Cons:**
+- US tax complexity if you're not careful
+- You're still Italian tax resident
+- Annual franchise tax in Delaware
+
+**Verdict:** Consider if primarily targeting US customers.
+
+### 11.6 Recommended Sequence
+
+```
+NOW (€0 revenue)
+│
+├── Keep building
+├── Get beta users (free)
+│
+FIRST REVENUE (€0-5k/year)
+│
+├── Partita IVA with Regime Forfettario
+├── 5% tax, minimal overhead
+│
+SCALING (€20k+/year)
+│
+├── Consider Estonia OÜ
+│ OR
+├── Stripe Atlas if US-focused
+│
+SIGNIFICANT REVENUE (€100k+/year)
+│
+└── Evaluate SRL or proper structure
+ based on growth trajectory
+```
+
+---
+
+## 12. Go-to-Market Strategy
+
+### 12.1 Launch Sequence
+
+**Phase 1: Codepanion First**
+- Lower friction ("just try it on one PR")
+- Builds trust ("oh wow, this actually catches bugs")
+- Fast to build (weekend project)
+- Good for content/marketing
+
+**Phase 2: Land and Expand**
+- Free users → Pro users
+- "Want this to run automatically on every PR?"
+- "Want it to also update your docs?"
+- Introduce RoboCo as the "full team" upgrade
+
+**Phase 3: RoboCo Launch**
+- For users who've built trust with Codepanion
+- "Ready to let the AI do more?"
+- Higher price, higher value
+
+### 12.2 The "Aha Moment"
+
+**For Cursor:** "Holy shit, it just wrote the function I was thinking about"
+
+**For Codepanion:** "I pushed my code, made coffee, came back to a QA review and updated docs"
+
+**For RoboCo:** "I described what I wanted, went to lunch, came back to a PR with tests and docs"
+
+### 12.3 Distribution Channels
+
+| Channel | Effort | Reach | Conversion |
+|---------|--------|-------|------------|
+| **Twitter/X** | Low | High | Low |
+| **Hacker News** | Medium | Very High | Medium |
+| **Reddit (r/programming, r/SideProject)** | Low | Medium | Medium |
+| **Product Hunt** | Medium | High | Medium |
+| **Dev.to / Hashnode** | Medium | Medium | Medium |
+| **YouTube tutorials** | High | High | High |
+
+### 12.4 Content Strategy
+
+**Week 1-2:**
+- "I built a CLI that reviews my code before I commit"
+- Twitter thread + HN post
+
+**Week 3-4:**
+- "How I caught 47 bugs in a week using AI code review"
+- Blog post + Reddit
+
+**Week 5-6:**
+- "Building an AI dev team: Architecture deep dive"
+- Technical blog post
+
+**Ongoing:**
+- Changelog updates
+- User testimonials
+- Comparison posts (vs Copilot, vs Cursor, etc.)
+
+---
+
+## 13. Honest Assessment
+
+### 13.1 What's Impressive
+
+You've built a proper orchestration system — task lifecycle, quality gates, MCP tools, the works. It's the kind of architecture a 10-person team at a funded startup would build. You built it solo. That's genuinely rare.
+
+### 13.2 Concerns
+
+#### Building in a Vacuum
+You've been dogfooding RoboCo on itself, which is great. But have you had *other* people use it? Even one person who isn't you? The gap between "works for me" and "works for strangers" is enormous.
+
+Estonia company, pricing tiers, go-to-market strategy — all premature if you haven't validated that someone else finds this useful.
+
+#### Market Timing is Tricky
+Cursor is eating the "AI dev tool" space. Devin, Factory, Cognition are going after "AI dev team." GitHub Copilot Workspace is coming. You're entering a knife fight with giants who have $100M+ and dedicated teams.
+
+Your edge is that you've *actually built* something that works, not a demo. But edge only matters if people see it.
+
+#### Codepanion Competition
+`codepanion review` enters a space where people already have options — Copilot does inline review, Cursor does it, there are GitHub Actions for this.
+
+**What's your angle?** Focus on what nobody else does well — QA and docs automation. Everyone skips those. If Codepanion auto-generates tests and updates README after every commit, *that's* differentiated.
+
+#### Spread Thin
+Rennberry cluster, UGREEN NAS, Olares One arriving, power optimization, Pi-hole monitoring, GlusterFS, the whole homelab...
+
+That's a lot of infrastructure work alongside building a SaaS. Every hour on homelab is an hour not talking to potential users or shipping Codepanion.
+
+#### The Actual Hard Part Isn't Code
+RoboCo works. The hard part now is:
+- Getting 10 people to try it
+- Getting 1 person to pay for it
+- Learning why the other 9 didn't
+
+That's uncomfortable work. Rejection and feedback and "actually this isn't what I need." But it's the only way to know if this is a product or a project.
+
+### 13.3 Recommendation
+
+1. **Ship Codepanion this week.** Not perfect. Just `review` command, working, on PyPI.
+
+2. **Post it.** Twitter, HN, Reddit. See what happens.
+
+3. **Talk to 5 solo devs.** Not to pitch — to listen. "What's the most annoying part of working alone?" See if RoboCo's value prop resonates.
+
+4. **Forget company formation** until someone gives you money.
+
+5. **Set a decision point.** "If I don't have 100 Codepanion users in 60 days, I'll re-evaluate the approach."
+
+You've built something real. That puts you ahead of 95% of people who talk about AI agents. The question is whether "real" translates to "wanted."
+
+Only the market can answer that. And the market only answers if you ask.
+
+---
+
+## 14. Next Steps & Action Items
+
+### 14.1 Immediate: API-First Initiative (This Week)
+
+**First task for RoboCo itself:**
+
+- [ ] **Auditor: Conduct backdoor audit** — Review all frontend → backend communication, flag every direct DB access, produce report
+- [ ] **Product Owner: Define Codepanion API contract** — What endpoints does it need? Minimal surface area.
+- [ ] **Product Owner: Prioritize cleanup work** — Which backdoors are blockers vs. nice-to-have?
+- [ ] **Head of Marketing: Draft Codepanion positioning** — README content, value prop
+
+### 14.2 Short-term (Next 2-4 Weeks)
+
+- [ ] **Fix critical backdoors** — Route through API properly
+- [ ] **Implement Codepanion endpoints** — `POST /sessions`, `POST /sessions/:id/messages`, `GET /sessions`
+- [ ] **Ship Codepanion v0.1** — Publish to PyPI (standalone mode first)
+- [ ] **Landing page** — Simple, one-page site
+- [ ] **Twitter announcement** — Thread about the tool
+- [ ] **HN post** — "Show HN: Codepanion – AI code reviewer CLI"
+
+### 14.3 Medium-term (1-3 Months)
+
+- [ ] **Add more Codepanion commands** — `docs`, `think`, `qa`
+- [ ] **Implement connected mode** — Codepanion → RoboCo API for history/sync
+- [ ] **Git integration in RoboCo** — Agents can commit, push, create PRs
+- [ ] **Collect feedback** — Iterate based on early users
+- [ ] **Set up Partita IVA** — When ready to charge
+- [ ] **Stripe integration** — Payments infrastructure
+
+### 14.4 Longer-term (3-6 Months)
+
+- [ ] **VS Code extension** — Codepanion in the editor
+- [ ] **GitHub App** — Auto-review PRs
+- [ ] **Team features** — Shared RoboCo workspaces
+- [ ] **Evaluate platform play** — Is there demand for the API directly?
+- [ ] **Evaluate company structure** — Estonia vs staying with Partita IVA
+
+### 14.5 Decision Point
+
+**60 days from Codepanion launch:**
+- If 100+ users → double down, add features, push toward Pro tier
+- If <100 users → investigate why, pivot approach, or reconsider market
+
+---
+
+## Appendix A: Key Files Reference
+
+### Backend (RoboCo)
+
+| File | Purpose |
+|------|---------|
+| `roboco/agents/base.py` | Base agent class with lifecycle, LLM, MCP |
+| `roboco/runtime/orchestrator.py` | Docker-based agent orchestrator |
+| `roboco/models/base.py` | All enums and base model |
+| `roboco/models/task.py` | Task model with full lifecycle |
+| `roboco/db/tables.py` | SQLAlchemy table definitions |
+| `roboco/mcp/task_server.py` | Task MCP tools |
+| `roboco/api/routes/tasks.py` | Task API endpoints |
+| `roboco/api/routes/orchestrator.py` | Orchestrator API endpoints |
+
+### Frontend (RoboCo Panel)
+
+| File | Purpose |
+|------|---------|
+| `src/types/index.ts` | TypeScript types matching backend |
+| `src/app/(dashboard)/layout.tsx` | Dashboard layout |
+| `src/components/kanban/` | Kanban board components |
+| `src/store/` | Zustand state stores |
+
+---
+
+## Appendix B: Environment Variables
+
+### RoboCo Backend
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `ANTHROPIC_API_KEY` | Claude API key | Required |
+| `DATABASE_URL` | PostgreSQL connection string | Required |
+| `ROBOCO_HOST` | API host | `0.0.0.0` |
+| `ROBOCO_PORT` | API port | `8000` |
+| `ROBOCO_HOST_CLAUDE_DIR` | Host path to `.claude` | `~/.claude` |
+| `ROBOCO_HOST_PROJECT_DIR` | Host path to project root | Required in Docker |
+| `ROBOCO_HOST_DATA_DIR` | Host path to data directory | Required in Docker |
+
+### Codepanion
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `ANTHROPIC_API_KEY` | Claude API key | Required |
+| `CODEPANION_MODEL` | Model to use | `claude-sonnet-4-20250514` |
+
+---
+
+## Appendix C: Glossary
+
+| Term | Definition |
+|------|------------|
+| **Agent** | An AI entity with a specific role (dev, QA, PM, etc.) |
+| **Blueprint** | System prompt that defines an agent's behavior |
+| **Cell** | A team unit (backend, frontend, ux_ui) |
+| **Channel** | Communication space for a group of agents |
+| **Dispatcher** | Orchestrator component that checks for and assigns work |
+| **Handoff** | Documentation package from dev to documenter |
+| **Journal** | Agent's private reflection space |
+| **MCP** | Model Context Protocol — tool access system |
+| **Orchestrator** | Central controller that spawns/manages agents |
+| **Session** | Scoped discussion within a channel group |
+| **TOON** | Token-Oriented Object Notation — efficient data format |
+
+---
+
+## Appendix D: CEO Directive - API-First Architecture
+
+**From:** CEO
+**To:** Board (Product Owner, Head of Marketing, Auditor)
+**Priority:** High
+**Date:** 2024-12-23
+
+---
+
+### Strategic Context
+
+We're positioning RoboCo for a two-phase growth strategy:
+
+**Phase B (Now):** Two products, one backend. RoboCo Panel (full experience) and Codepanion (lightweight CLI, open source). Both consume the same API.
+
+**Phase C (Future):** Platform play. The orchestration API becomes the product. Third parties build on us.
+
+For this to work, **the API must be the only way in.** No backdoors. No direct DB access from frontends. No "just this once" shortcuts.
+
+If we can't use our own API to build our own products, no one else can either.
+
+---
+
+### Objective
+
+**Establish API-first architecture across the entire system.**
+
+This means:
+1. Every action the Panel takes goes through the public API
+2. Every action Codepanion will take goes through the public API
+3. Internal services communicate through well-defined interfaces
+4. The API is documented, consistent, and pleasant to use
+
+---
+
+### Success Criteria
+
+- [ ] Zero direct database access from Panel frontend
+- [ ] Zero direct core imports that bypass API in frontend code
+- [ ] Codepanion can connect and perform basic operations (create session, send message, retrieve history)
+- [ ] API documentation covers all endpoints Codepanion needs
+- [ ] Auditor has verified no backdoors remain in critical paths
+
+---
+
+### Board Responsibilities
+
+#### Product Owner
+
+1. **Define the API contract** for Codepanion integration
+ - What endpoints does Codepanion need?
+ - What's the minimal surface area?
+ - What can wait for v2?
+
+2. **Prioritize the cleanup work**
+ - Which backdoors are blockers vs. nice-to-have?
+ - What's the MVP for "API-first"?
+
+3. **Approve the architecture decisions**
+ - Review proposals from Main PM
+ - Sign off on API design
+
+#### Head of Marketing
+
+1. **Prepare positioning for Codepanion**
+ - Open source CLI angle
+ - "Works standalone, better connected" messaging
+ - Developer-first tone
+
+2. **Draft initial README/landing content**
+ - What does Codepanion do?
+ - Why would a dev use it?
+ - How does it connect to RoboCo?
+
+3. **Identify launch channels**
+ - Where do we announce?
+ - What's the content calendar?
+
+#### Auditor
+
+1. **Conduct the backdoor audit**
+ - Review all frontend → backend communication
+ - Flag every direct DB access
+ - Flag every import that bypasses API layer
+ - Produce audit report with file, line, severity
+
+2. **Verify fixes**
+ - After cleanup, re-audit critical paths
+ - Confirm API-first compliance
+
+3. **Establish ongoing monitoring**
+ - How do we prevent new backdoors?
+ - What checks should be part of code review?
+
+---
+
+### Deliverables to Main PM
+
+Once board alignment is complete, hand off to Main PM:
+
+1. **Audit Report** (from Auditor)
+ - List of all backdoors with severity ratings
+
+2. **API Specification** (from Product Owner)
+ - Endpoints needed for Codepanion MVP
+ - Request/response schemas
+ - Authentication approach
+
+3. **Prioritized Task List** (from Product Owner)
+ - Ordered by: blockers first, then high-value, then nice-to-have
+
+4. **Marketing Brief** (from Head of Marketing)
+ - Positioning document for Codepanion
+ - README draft
+ - Launch plan outline
+
+---
+
+### Constraints
+
+- **Timeline:** Codepanion MVP should be shippable within 2 weeks of Main PM receiving handoff
+- **Scope:** Fix what's necessary for Codepanion. Don't boil the ocean.
+- **Principle:** If in doubt, expose it through API. We'd rather have a slightly larger API surface than hidden backdoors.
+
+---
+
+### Notes on Documentation
+
+For now:
+- Task documentation lives in the task's session (tied via session-task link)
+- Technical documentation (API docs, architecture) should be markdown in the repo
+- User-facing documentation (README, guides) prepared by Documenter roles, reviewed by PM
+
+Future consideration: dedicated documentation system. But not now.
+
+---
+
+### Notes on Git Integration
+
+Git integration is coming. For this initiative:
+- All code changes go through PRs
+- PRs link to task IDs in commit messages
+- Once git integration lands, this becomes automated
+
+For now, manual discipline.
+
+---
+
+### Communication
+
+- Board discussions in `management` channel
+- Main PM coordination in `cross-cell` channel
+- Cell work in respective cell channels (`backend-cell`, `frontend-cell`, `uxui-cell`)
+- Each task gets its own session within the appropriate channel
+- Journals capture decisions, learnings, blockers
+
+---
+
+### Final Word
+
+This is the foundation. If we get API-first right, everything else becomes easier — Codepanion, platform expansion, third-party integrations, even our own Panel development.
+
+If we get it wrong, we're building on sand.
+
+Make it solid.
+
+---
+
+**CEO**
+
+---
+
+*End of Summary*
diff --git a/docs/ULTIMATE_TEST.md b/docs/ULTIMATE_TEST.md
new file mode 100644
index 00000000..7a000dc5
--- /dev/null
+++ b/docs/ULTIMATE_TEST.md
@@ -0,0 +1,171 @@
+---
+
+# CEO Directive: API-First Architecture Initiative
+
+**From:** CEO
+**To:** Board (Product Owner, Head of Marketing, Auditor)
+**Priority:** High
+**Date:** 2024-12-23
+
+---
+
+## Strategic Context
+
+We're positioning RoboCo for a two-phase growth strategy:
+
+**Phase B (Now):** Two products, one backend. RoboCo Panel (full experience) and Codepanion (lightweight CLI, open source). Both consume the same API.
+
+**Phase C (Future):** Platform play. The orchestration API becomes the product. Third parties build on us.
+
+For this to work, **the API must be the only way in.** No backdoors. No direct DB access from frontends. No "just this once" shortcuts.
+
+If we can't use our own API to build our own products, no one else can either.
+
+---
+
+## Objective
+
+**Establish API-first architecture across the entire system.**
+
+This means:
+1. Every action the Panel takes goes through the public API
+2. Every action Codepanion will take goes through the public API
+3. Internal services communicate through well-defined interfaces
+4. The API is documented, consistent, and pleasant to use
+
+---
+
+## Success Criteria
+
+- [ ] Zero direct database access from Panel frontend
+- [ ] Zero direct core imports that bypass API in frontend code
+- [ ] Codepanion can connect and perform basic operations (create session, send message, retrieve history)
+- [ ] API documentation covers all endpoints Codepanion needs
+- [ ] Auditor has verified no backdoors remain in critical paths
+
+---
+
+## Board Responsibilities
+
+### Product Owner
+
+1. **Define the API contract** for Codepanion integration
+ - What endpoints does Codepanion need?
+ - What's the minimal surface area?
+ - What can wait for v2?
+
+2. **Prioritize the cleanup work**
+ - Which backdoors are blockers vs. nice-to-have?
+ - What's the MVP for "API-first"?
+
+3. **Approve the architecture decisions**
+ - Review proposals from Main PM
+ - Sign off on API design
+
+### Head of Marketing
+
+1. **Prepare positioning for Codepanion**
+ - Open source CLI angle
+ - "Works standalone, better connected" messaging
+ - Developer-first tone
+
+2. **Draft initial README/landing content**
+ - What does Codepanion do?
+ - Why would a dev use it?
+ - How does it connect to RoboCo?
+
+3. **Identify launch channels**
+ - Where do we announce?
+ - What's the content calendar?
+
+### Auditor
+
+1. **Conduct the backdoor audit**
+ - Review all frontend → backend communication
+ - Flag every direct DB access
+ - Flag every import that bypasses API layer
+ - Produce audit report with file, line, severity
+
+2. **Verify fixes**
+ - After cleanup, re-audit critical paths
+ - Confirm API-first compliance
+
+3. **Establish ongoing monitoring**
+ - How do we prevent new backdoors?
+ - What checks should be part of code review?
+
+---
+
+## Deliverables to Main PM
+
+Once board alignment is complete, hand off to Main PM:
+
+1. **Audit Report** (from Auditor)
+ - List of all backdoors with severity ratings
+
+2. **API Specification** (from Product Owner)
+ - Endpoints needed for Codepanion MVP
+ - Request/response schemas
+ - Authentication approach
+
+3. **Prioritized Task List** (from Product Owner)
+ - Ordered by: blockers first, then high-value, then nice-to-have
+
+4. **Marketing Brief** (from Head of Marketing)
+ - Positioning document for Codepanion
+ - README draft
+ - Launch plan outline
+
+---
+
+## Constraints
+
+- **Timeline:** Codepanion MVP should be shippable within 2 weeks of Main PM receiving handoff
+- **Scope:** Fix what's necessary for Codepanion. Don't boil the ocean.
+- **Principle:** If in doubt, expose it through API. We'd rather have a slightly larger API surface than hidden backdoors.
+
+---
+
+## Notes on Documentation
+
+For now:
+- Task documentation lives in the task's session (tied via session-task link)
+- Technical documentation (API docs, architecture) should be markdown in the repo
+- User-facing documentation (README, guides) prepared by Documenter roles, reviewed by PM
+
+Future consideration: dedicated documentation system. But not now.
+
+---
+
+## Notes on Git Integration
+
+Git integration is coming. For this initiative:
+- All code changes go through PRs
+- PRs link to task IDs in commit messages
+- Once git integration lands, this becomes automated
+
+For now, manual discipline.
+
+---
+
+## Communication
+
+- Board discussions in `management` channel
+- Main PM coordination in `cross-cell` channel
+- Cell work in respective cell channels (`backend-cell`, `frontend-cell`, `uxui-cell`)
+- Each task gets its own session within the appropriate channel
+- Journals capture decisions, learnings, blockers
+
+---
+
+## Final Word
+
+This is the foundation. If we get API-first right, everything else becomes easier — Codepanion, platform expansion, third-party integrations, even our own Panel development.
+
+If we get it wrong, we're building on sand.
+
+Make it solid.
+
+---
+
+**CEO**
diff --git a/docs/WORKFLOWS.md b/docs/WORKFLOWS.md
new file mode 100644
index 00000000..b012eff4
--- /dev/null
+++ b/docs/WORKFLOWS.md
@@ -0,0 +1,436 @@
+# RoboCo Workflows & Permissions
+
+Visual documentation of task lifecycles, permissions, and workflows.
+
+## 0. Terminology Dictionary
+
+**CRITICAL: Understand these concepts before reading further.**
+
+### Communication Hierarchy
+
+```
+Channel → Group → Session → Messages
+
+┌─────────────────────────────────────────────────────────────────────────┐
+│ CHANNEL (e.g., "backend-cell") │
+│ A named communication space for a team/topic │
+│ │
+│ ┌───────────────────────────────────────────────────────────────────┐ │
+│ │ GROUP (e.g., "Sprint 5 Backend Work") │ │
+│ │ A collection within a channel (project, sprint, topic) │ │
+│ │ │ │
+│ │ ┌───────────────────────────────────────────────────────────┐ │ │
+│ │ │ SESSION (e.g., "TASK-123 Discussion") │ │ │
+│ │ │ A conversation thread, usually tied to a task │ │ │
+│ │ │ │ │ │
+│ │ │ ┌─────────────────────────────────────────────────┐ │ │ │
+│ │ │ │ MESSAGES │ │ │ │
+│ │ │ │ Individual messages within a session │ │ │ │
+│ │ │ │ Types: action, dialogue, blocker, question │ │ │ │
+│ │ │ └─────────────────────────────────────────────────┘ │ │ │
+│ │ └───────────────────────────────────────────────────────────┘ │ │
+│ └───────────────────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────────────┘
+
+Tools:
+- roboco_channel_list() → List channels you can access
+- roboco_channel_history() → Get messages from a channel's sessions
+- roboco_session_history_for_task() → Get messages for a specific task's session
+- roboco_message_send() → Send to channel (routes via task_id to session)
+```
+
+### Journal vs Documentation
+
+```
+┌─────────────────────────────────────┬─────────────────────────────────────┐
+│ JOURNAL │ DOCUMENTATION │
+├─────────────────────────────────────┼─────────────────────────────────────┤
+│ WHAT: Agent's personal reflections │ WHAT: Project/codebase docs │
+│ │ │
+│ WHERE: Database (journal entries) │ WHERE: /app/docs/ files │
+│ │ │
+│ WHO WRITES: Each agent, for self │ WHO WRITES: Documenter agents │
+│ │ │
+│ PURPOSE: │ PURPOSE: │
+│ - Track decisions & rationale │ - API documentation │
+│ - Record learnings & struggles │ - README updates │
+│ - Reflect on task completion │ - Changelog entries │
+│ - Build context for future sessions │ - Component/feature docs │
+│ │ │
+│ AUDIENCE: Self + cell members │ AUDIENCE: All developers, users │
+│ │ │
+│ TOOLS: │ TOOLS: │
+│ - roboco_journal_entry() │ - Write tool to /app/docs/ │
+│ - roboco_journal_reflect() │ - roboco_task_docs_complete() │
+│ - roboco_journal_decision() │ │
+│ - roboco_journal_learning() │ │
+│ - roboco_journal_read_team() │ │
+└─────────────────────────────────────┴─────────────────────────────────────┘
+
+KEY DISTINCTION:
+- Journal = "What I learned/decided while doing this task" (internal notes)
+- Documentation = "How this feature works for others" (external docs)
+
+Documenter reads journals to UNDERSTAND what was built, then writes documentation.
+```
+
+### Task Notes vs Journals
+
+```
+┌─────────────────────────────────────┬─────────────────────────────────────┐
+│ TASK NOTES │ JOURNALS │
+│ (dev_notes, qa_notes) │ │
+├─────────────────────────────────────┼─────────────────────────────────────┤
+│ Attached to the task record │ Separate entries linked by task_id │
+│ │ │
+│ Brief handoff summaries │ Detailed journey records │
+│ │ │
+│ Set via: │ Set via: │
+│ - roboco_task_submit_qa(notes) │ - roboco_journal_*() tools │
+│ - roboco_task_qa_pass(notes) │ │
+│ - roboco_task_docs_complete(notes) │ │
+│ │ │
+│ Examples: │ Examples: │
+│ "Implemented X, tested Y" │ "Decided to use pattern X because Y"│
+│ "QA passed, verified all criteria" │ "Struggled with Z, solved via W" │
+└─────────────────────────────────────┴─────────────────────────────────────┘
+```
+
+## 1. Task Lifecycle State Machine
+
+```mermaid
+stateDiagram-v2
+ [*] --> pending: Task Created
+
+ pending --> claimed: Developer claims
+ pending --> cancelled: PM cancels
+
+ claimed --> in_progress: Developer starts
+ claimed --> pending: Developer unclaims
+ claimed --> cancelled: PM cancels
+
+ in_progress --> blocked: Developer blocked
+ in_progress --> paused: Developer pauses
+ in_progress --> verifying: Developer self-verifies
+ in_progress --> cancelled: PM cancels
+
+ blocked --> in_progress: Unblocked
+ blocked --> cancelled: PM cancels
+
+ paused --> in_progress: Developer resumes
+ paused --> cancelled: PM cancels
+
+ verifying --> awaiting_qa: Submit for QA
+ verifying --> needs_revision: Self-found issues
+ verifying --> awaiting_documentation: Skip QA (small tasks)
+ verifying --> cancelled: PM cancels
+
+ awaiting_qa --> awaiting_documentation: QA PASS
+ awaiting_qa --> needs_revision: QA FAIL
+ awaiting_qa --> blocked: Blocked during QA
+ awaiting_qa --> cancelled: PM cancels
+
+ needs_revision --> in_progress: Developer resumes
+ needs_revision --> cancelled: PM cancels
+
+ awaiting_documentation --> awaiting_pm_review: Documenter marks docs done
+ awaiting_documentation --> cancelled: PM cancels
+
+ awaiting_pm_review --> completed: PM completes
+ awaiting_pm_review --> cancelled: PM cancels
+
+ completed --> [*]
+ cancelled --> [*]
+
+ quarantined --> pending: Un-quarantined
+```
+
+## 2. Agent Hierarchy & Roles
+
+```
+ +-------+
+ | CEO |
+ +-------+
+ |
+ +---------------+---------------+
+ | | |
+ +---------+ +-----------+ +---------+
+ | Product | | Head | | Auditor |
+ | Owner | | Marketing | | (silent)|
+ +---------+ +-----------+ +---------+
+ | | |
+ +-------+-------+ |
+ | |
+ +---------+ |
+ | Main PM |<-----------------+
+ +---------+ (observes all)
+ |
+ +-----------+-----------+
+ | | |
++-------+ +-------+ +-------+
+| BE PM | | FE PM | | UX PM |
++-------+ +-------+ +-------+
+ | | |
++-------+ +-------+ +-------+
+|Backend| |Frontend| | UX/UI |
+| Cell | | Cell | | Cell |
++-------+ +-------+ +-------+
+
+Each Cell:
+ - 2 Developers (BE/FE) or 1 Developer (UX)
+ - 1 QA Engineer
+ - 1 Documenter
+ - 1 Cell PM
+```
+
+## 3. Notification Permissions
+
+```
+WHO CAN SEND NOTIFICATIONS:
+
++------------------+-------------+----------------------------------------------+
+| Sender Role | Can Send? | Scope |
++------------------+-------------+----------------------------------------------+
+| CEO | YES | Anyone |
+| Auditor | YES | Anyone |
+| Main PM | YES | Anyone |
+| Product Owner | YES | main-pm, head-marketing, auditor, ceo |
+| Head Marketing | YES | main-pm, product-owner, auditor, ceo |
+| Cell PM | YES | Own cell only |
++------------------+-------------+----------------------------------------------+
+| Developer | NO | - |
+| QA | NO | - |
+| Documenter | NO | - |
++------------------+-------------+----------------------------------------------+
+
+TOOLS VISIBILITY:
+
++----------------------+------------+----------+---------+---------+---------+
+| Tool | Dev/QA/Doc | Cell PM | Main PM | Board | Aud/CEO |
++----------------------+------------+----------+---------+---------+---------+
+| roboco_notify_list | YES | YES | YES | YES | YES |
+| roboco_notify_get | YES | YES | YES | YES | YES |
+| roboco_notify_ack | YES | YES | YES | YES | YES |
+| roboco_notify_send | HIDDEN | YES | YES | YES | YES |
+| roboco_escalate | HIDDEN | YES | YES | HIDDEN | HIDDEN |
+| roboco_request_appr | HIDDEN | YES | YES | YES | HIDDEN |
++----------------------+------------+----------+---------+---------+---------+
+
+Note: "Board" = Product Owner + Head Marketing. Auditor/CEO can send but not escalate or request approval.
+```
+
+## 4. QA Workflow (Full Detail)
+
+### QA Status Acceptance
+
+QA can call `qa_pass` or `qa_fail` from ANY of these statuses:
+- `awaiting_qa` (initial)
+- `claimed` (after QA claims)
+- `in_progress` (after QA starts)
+
+This allows QA to follow the full workflow: SCAN → CLAIM → PLAN → START → TEST → VERDICT
+
+### QA Workflow Diagram
+
+```mermaid
+sequenceDiagram
+ participant Dev as Developer
+ participant Task as Task System
+ participant QA as QA Engineer
+ participant Doc as Documenter
+ participant PM as Cell PM
+
+ Dev->>Task: Submit for QA (awaiting_qa)
+ Note over Task: assigned_to = QA
quick_context = original_developer:Dev
+
+ QA->>Task: roboco_task_claim() → status = claimed
+ QA->>Task: roboco_task_plan() → save test plan
+ QA->>Task: roboco_task_start() → status = in_progress
+ QA->>Task: Test work against criteria
+
+ alt QA PASS
+ QA->>Task: roboco_task_qa_pass(qa_notes)
+ Task->>Task: status = awaiting_documentation
+ Note over Task: HANDOFF TO DOCUMENTER
+ Doc->>Task: roboco_task_claim() → status = claimed
+ Doc->>Task: roboco_task_plan() → save doc plan
+ Doc->>Task: roboco_task_start() → status = in_progress
+ Doc->>Task: GATHER info (see section 4a)
+ Doc->>Task: WRITE docs to /app/docs/
+ Doc->>Task: roboco_task_docs_complete()
+ Task->>Task: status = awaiting_pm_review
+ PM->>Task: roboco_task_complete()
+ Task->>Task: status = completed
+ else QA FAIL
+ QA->>Task: roboco_task_qa_fail(qa_notes, issues)
+ Task->>Task: status = needs_revision
+ Task->>Task: assigned_to = original Dev
+ Dev->>Task: roboco_task_start() → status = in_progress
+ Dev->>Task: Fix issues, resubmit
+ end
+```
+
+### 4a. Documenter Information Gathering
+
+```
+DOCUMENTER MUST GATHER FROM 3 SOURCES:
+
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ SOURCE 1: TASK DETAILS (roboco_task_get) │
+│ ───────────────────────────────────────── │
+│ • description, acceptance_criteria │
+│ • dev_notes (developer's handoff summary) │
+│ • qa_notes (QA's verification notes) │
+│ • quick_context (original developer, etc.) │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ SOURCE 2: TEAM JOURNALS (roboco_journal_read_team) │
+│ ───────────────────────────────────────────────── │
+│ Read developer's journey: │
+│ roboco_journal_read_team("be-dev-1", task_id=task_id, limit=20) │
+│ │
+│ What to look for: │
+│ • Decisions made and WHY │
+│ • Struggles encountered and how solved │
+│ • Learnings documented │
+│ • Design/architecture choices │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ SOURCE 3: SESSION MESSAGES (roboco_session_history_for_task) │
+│ ──────────────────────────────────────────────────────────── │
+│ Read discussion context: │
+│ roboco_session_history_for_task(task_id) │
+│ │
+│ What to look for: │
+│ • Questions asked and answers given │
+│ • Clarifications from PM/dev │
+│ • Blockers discussed and resolutions │
+│ • Design decisions made in discussion │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+NOTE: Journals are INPUT for understanding. Documentation is OUTPUT to /app/docs/.
+```
+
+## 5. Block/Unblock Workflow
+
+```mermaid
+sequenceDiagram
+ participant Dev as Developer
+ participant Task as Task System
+ participant PM as Cell PM
+
+ Dev->>Task: Working on task (in_progress)
+
+ Note over Dev: Encounters blocker
+
+ Dev->>Task: roboco_task_block(reason, type, what_needed)
+ Task->>Task: POST /tasks/{id}/soft-block
+ Task->>Task: status = blocked
+ Task->>Task: dev_notes += blocker info
+
+ Note over Dev: Can work on other tasks
+
+ alt Blocker resolved
+ Dev->>Task: roboco_task_unblock()
+ Task->>Task: POST /tasks/{id}/unblock
+ Task->>Task: status = in_progress
+ Dev->>Task: Continue working
+ else Need PM help
+ Dev->>PM: roboco_report_blocker() via message channel
+ PM->>Task: Resolves blocker
+ Dev->>Task: roboco_task_unblock()
+ end
+```
+
+## 6. Task Role Restrictions
+
+```
+ROLE-BASED TRANSITIONS:
+
++-------------------------------+-------------------------------------------+
+| Transition | Allowed Roles |
++-------------------------------+-------------------------------------------+
+| awaiting_qa → awaiting_doc | QA only |
+| awaiting_qa → needs_rev | QA only |
+| awaiting_doc → awaiting_pm | Documenter only |
+| awaiting_pm → completed | Cell PM, Main PM, Product Owner, Head Mkt |
+| * → cancelled | Cell PM, Main PM, Product Owner, Head Mkt |
++-------------------------------+-------------------------------------------+
+
+Note: CEO and Auditor are NOT in the cancel/complete roles list - they observe but don't directly act on tasks.
+
+VALID START STATUSES (for roboco_task_start):
+
++------------------+------------------------------------------+
+| Status | Who Can Start |
++------------------+------------------------------------------+
+| claimed | Assigned agent (requires plan) |
+| paused | Assigned agent (resume) |
+| needs_revision | Original developer (fix QA issues) |
++------------------+------------------------------------------+
+
+QA VERDICT ACCEPTANCE (qa_pass/qa_fail):
+
++------------------+------------------------------------------+
+| Status | Reason |
++------------------+------------------------------------------+
+| awaiting_qa | Task submitted for QA review |
+| claimed | QA claimed the task |
+| in_progress | QA started working (plan + start) |
++------------------+------------------------------------------+
+
+All three statuses are valid because QA follows the full workflow:
+awaiting_qa → claim → plan → start → test → verdict
+
+DOCUMENTER COMPLETION ACCEPTANCE (docs_complete):
+
++----------------------+------------------------------------------+
+| Status | Reason |
++----------------------+------------------------------------------+
+| awaiting_documentation | Task ready for documentation |
+| claimed | Documenter claimed the task |
+| in_progress | Documenter started (plan + start) |
++----------------------+------------------------------------------+
+
+All three statuses are valid because Documenter follows the full workflow:
+awaiting_documentation → claim → plan → start → gather → write → docs_complete
+```
+
+## 7. Escalation Chain
+
+```
+Developer/QA/Doc → Cell PM → Main PM → Product Owner → CEO
+
++------------+ +---------+ +---------+ +---------------+ +-----+
+| be-dev-1 |---->| | | | | | | |
+| be-dev-2 |---->| be-pm |---->| | | | | |
+| be-qa |---->| | | | | | | |
+| be-doc |---->| | | | | | | |
++------------+ +---------+ | | | | | |
+ | main-pm |---->| product-owner |---->| CEO |
++------------+ +---------+ | | | | | |
+| fe-dev-1 |---->| | | | | | | |
+| fe-dev-2 |---->| fe-pm |---->| | | | | |
+| fe-qa |---->| | | | | | | |
+| fe-doc |---->| | | | | | | |
++------------+ +---------+ +---------+ +---------------+ +-----+
+```
+
+## 8. Communication vs Notification
+
+```
++-------------------+----------------------------------+----------------------------------+
+| Mechanism | Who Can Use | Purpose |
++-------------------+----------------------------------+----------------------------------+
+| Messages | Everyone | Constant stream, logged |
+| (roboco_message) | | discussions, updates |
++-------------------+----------------------------------+----------------------------------+
+| Blocker Reports | Everyone | Signal blocked status |
+| (roboco_report_ | | PM auto-notified |
+| blocker) | | |
++-------------------+----------------------------------+----------------------------------+
+| Notifications | PM, Board, Auditor, CEO | Formal signals requiring |
+| (roboco_notify) | | acknowledgment |
++-------------------+----------------------------------+----------------------------------+
+| Escalations | PMs only | High-priority issues |
+| (roboco_escalate) | | up the chain |
++-------------------+----------------------------------+----------------------------------+
+```
diff --git a/docs/glittery-marinating-yeti.md b/docs/glittery-marinating-yeti.md
new file mode 100644
index 00000000..0516db64
--- /dev/null
+++ b/docs/glittery-marinating-yeti.md
@@ -0,0 +1,1714 @@
+# RoboCo Open Core + Kubernetes Migration Plan
+
+## Overview
+
+**Goal:** Restructure RoboCo for commercialization with an Open Core model, then
+deploy to Kubernetes with ArgoCD on the UGREEN NAS.
+
+**Order:** Separation first, then K8s migration.
+
+---
+
+## What's Open vs Closed
+
+| Component | Status | Reason |
+|-----------|--------|--------|
+| Backend API (`roboco/api/`, `roboco/services/`) | **CLOSED** | Core product
+value |
+| Orchestrator (`roboco/runtime/`) | **CLOSED** | Competitive advantage |
+| MCP Servers (`roboco/mcp/`) | **CLOSED** | Business logic |
+| Frontend (future dashboard) | **OPEN** | Drives adoption |
+| Python/JS SDK | **OPEN** | Ecosystem growth |
+| CLI Tool | **OPEN** | Developer experience |
+| Agent Blueprints | **OPEN** | Community templates |
+
+---
+
+## Phase 1: API Separation (7-10 days)
+
+### Why This First
+- SDK/CLI can be developed independently of infra
+- Validates API surface before K8s migration
+- Creates public-facing documentation
+- Allows community to start building agents
+
+### Step 1.1: Create OSS Repo Structure (1 day)
+
+Create public `roboco-agents` repo on GitHub:
+
+```
+roboco-agents/
+├── sdk/
+│ └── python/
+│ ├── roboco_sdk/
+│ │ ├── __init__.py
+│ │ ├── client.py # Main RobocoClient class
+│ │ ├── tasks.py # TasksAPI wrapper
+│ │ ├── messages.py # MessagesAPI wrapper
+│ │ ├── journals.py # JournalsAPI wrapper
+│ │ ├── notifications.py # NotificationsAPI wrapper
+│ │ └── models.py # Pydantic models (Task, Message, etc.)
+│ ├── pyproject.toml
+│ ├── README.md
+│ └── tests/
+├── cli/
+│ └── roboco_cli/
+│ ├── __init__.py
+│ ├── main.py # Typer app entry
+│ ├── commands/
+│ │ ├── task.py # roboco task
+│ │ ├── agent.py # roboco agent
+│ │ ├── message.py # roboco message
+│ │ └── config.py # roboco config
+│ └── config.py # Config file handling
+├── blueprints/
+│ ├── templates/
+│ │ ├── developer.md # Generic dev blueprint
+│ │ ├── qa.md # Generic QA blueprint
+│ │ ├── pm.md # Generic PM blueprint
+│ │ └── documenter.md # Generic documenter blueprint
+│ └── examples/
+│ └── simple-team/ # Example 3-agent team
+├── docker/
+│ ├── agent-base.Dockerfile # Base image for agents
+│ └── claude-code.Dockerfile # Claude Code agent image
+├── examples/
+│ ├── hello-world/ # Minimal agent example
+│ ├── task-worker/ # Task processing example
+│ └── multi-agent/ # Team coordination example
+├── docs/
+│ ├── getting-started.md
+│ ├── sdk-reference.md
+│ ├── cli-reference.md
+│ └── blueprint-guide.md
+├── LICENSE # MIT or Apache 2.0
+├── README.md
+└── pyproject.toml # Workspace/monorepo config
+```
+
+**Actions:**
+1. `gh repo create roboco-agents --public`
+2. Initialize with pyproject.toml workspace
+3. Setup GitHub Actions for CI/CD
+4. Configure PyPI publishing workflow
+
+---
+
+### Step 1.2: Extract SDK from MCP Utils (2-3 days)
+
+**Source files to extract from:**
+- `roboco/mcp/utils.py` - `ApiClient` class
+- `roboco/api/schemas/*.py` - Response models
+- `roboco/models/*.py` - Core domain models
+
+**SDK Structure:**
+
+```python
+# roboco_sdk/client.py
+import httpx
+from typing import Optional
+
+class RobocoClient:
+ """Main client for RoboCo API."""
+
+ def __init__(
+ self,
+ base_url: str = "http://localhost:8000/api/v1",
+ api_key: Optional[str] = None,
+ agent_id: Optional[str] = None,
+ agent_role: Optional[str] = None,
+ ):
+ self.base_url = base_url
+ self.headers = {}
+ if api_key:
+ self.headers["Authorization"] = f"Bearer {api_key}"
+ if agent_id:
+ self.headers["X-Agent-ID"] = agent_id
+ if agent_role:
+ self.headers["X-Agent-Role"] = agent_role
+
+ self._http = httpx.AsyncClient(
+ base_url=base_url,
+ headers=self.headers,
+ timeout=30.0
+ )
+
+ @property
+ def tasks(self) -> "TasksAPI":
+ return TasksAPI(self._http)
+
+ @property
+ def messages(self) -> "MessagesAPI":
+ return MessagesAPI(self._http)
+
+ @property
+ def journals(self) -> "JournalsAPI":
+ return JournalsAPI(self._http)
+
+ @property
+ def notifications(self) -> "NotificationsAPI":
+ return NotificationsAPI(self._http)
+
+ async def close(self):
+ await self._http.aclose()
+
+ async def __aenter__(self):
+ return self
+
+ async def __aexit__(self, *args):
+ await self.close()
+```
+
+```python
+# roboco_sdk/tasks.py
+from typing import Optional, List
+from .models import Task, TaskCreate, TaskUpdate
+
+class TasksAPI:
+ def __init__(self, http: httpx.AsyncClient):
+ self._http = http
+
+ async def list(
+ self,
+ status: Optional[str] = None,
+ team: Optional[str] = None,
+ assigned_to: Optional[str] = None,
+ limit: int = 50,
+ ) -> List[Task]:
+ params = {"limit": limit}
+ if status:
+ params["status"] = status
+ if team:
+ params["team"] = team
+ if assigned_to:
+ params["assigned_to"] = assigned_to
+
+ resp = await self._http.get("/tasks", params=params)
+ resp.raise_for_status()
+ return [Task(**t) for t in resp.json()["items"]]
+
+ async def get(self, task_id: str) -> Task:
+ resp = await self._http.get(f"/tasks/{task_id}")
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ async def create(self, data: TaskCreate) -> Task:
+ resp = await self._http.post("/tasks", json=data.model_dump())
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ async def claim(self, task_id: str) -> Task:
+ resp = await self._http.post(f"/tasks/{task_id}/claim")
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ async def start(self, task_id: str) -> Task:
+ resp = await self._http.post(f"/tasks/{task_id}/start")
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ async def progress(
+ self, task_id: str, message: str, percentage: int
+ ) -> Task:
+ resp = await self._http.post(
+ f"/tasks/{task_id}/progress",
+ json={"message": message, "percentage": percentage}
+ )
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ async def submit_for_qa(self, task_id: str) -> Task:
+ resp = await self._http.post(f"/tasks/{task_id}/submit-qa")
+ resp.raise_for_status()
+ return Task(**resp.json())
+
+ # ... more methods
+```
+
+```python
+# roboco_sdk/models.py
+from pydantic import BaseModel
+from typing import Optional, List
+from datetime import datetime
+from enum import Enum
+
+class TaskStatus(str, Enum):
+ BACKLOG = "backlog"
+ PENDING = "pending"
+ CLAIMED = "claimed"
+ IN_PROGRESS = "in_progress"
+ VERIFYING = "verifying"
+ AWAITING_QA = "awaiting_qa"
+ AWAITING_DOCUMENTATION = "awaiting_documentation"
+ AWAITING_PM_REVIEW = "awaiting_pm_review"
+ COMPLETED = "completed"
+ BLOCKED = "blocked"
+ CANCELLED = "cancelled"
+
+class Task(BaseModel):
+ id: str
+ title: str
+ description: Optional[str] = None
+ status: TaskStatus
+ priority: int = 3
+ team: Optional[str] = None
+ assigned_to: Optional[str] = None
+ parent_task_id: Optional[str] = None
+ created_at: datetime
+ updated_at: datetime
+ # ... more fields
+
+class TaskCreate(BaseModel):
+ title: str
+ description: Optional[str] = None
+ priority: int = 3
+ team: Optional[str] = None
+ parent_task_id: Optional[str] = None
+ acceptance_criteria: Optional[List[str]] = None
+```
+
+**Testing:**
+```python
+# tests/test_tasks.py
+import pytest
+from roboco_sdk import RobocoClient
+
+@pytest.mark.asyncio
+async def test_list_tasks():
+ async with RobocoClient() as client:
+ tasks = await client.tasks.list(status="pending")
+ assert isinstance(tasks, list)
+```
+
+---
+
+### Step 1.3: Create CLI Tool (2-3 days)
+
+**CLI using Typer:**
+
+```python
+# roboco_cli/main.py
+import typer
+from roboco_cli.commands import task, agent, message, config
+
+app = typer.Typer(
+ name="roboco",
+ help="RoboCo CLI - Manage AI agent workflows"
+)
+
+app.add_typer(task.app, name="task")
+app.add_typer(agent.app, name="agent")
+app.add_typer(message.app, name="message")
+app.add_typer(config.app, name="config")
+
+if __name__ == "__main__":
+ app()
+```
+
+```python
+# roboco_cli/commands/task.py
+import typer
+from rich.console import Console
+from rich.table import Table
+from roboco_sdk import RobocoClient
+import asyncio
+
+app = typer.Typer(help="Task management commands")
+console = Console()
+
+@app.command("list")
+def list_tasks(
+ status: str = typer.Option(None, "--status", "-s"),
+ team: str = typer.Option(None, "--team", "-t"),
+ limit: int = typer.Option(20, "--limit", "-n"),
+):
+ """List tasks with optional filters."""
+ async def _list():
+ async with RobocoClient() as client:
+ return await client.tasks.list(
+ status=status, team=team, limit=limit
+ )
+
+ tasks = asyncio.run(_list())
+
+ table = Table(title="Tasks")
+ table.add_column("ID", style="cyan")
+ table.add_column("Title")
+ table.add_column("Status", style="green")
+ table.add_column("Team")
+ table.add_column("Assigned")
+
+ for t in tasks:
+ table.add_row(
+ t.id[:8],
+ t.title[:40],
+ t.status.value,
+ t.team or "-",
+ t.assigned_to[:8] if t.assigned_to else "-"
+ )
+
+ console.print(table)
+
+@app.command("get")
+def get_task(task_id: str):
+ """Get task details."""
+ async def _get():
+ async with RobocoClient() as client:
+ return await client.tasks.get(task_id)
+
+ task = asyncio.run(_get())
+ console.print(task)
+
+@app.command("claim")
+def claim_task(task_id: str):
+ """Claim a task."""
+ async def _claim():
+ async with RobocoClient() as client:
+ return await client.tasks.claim(task_id)
+
+ task = asyncio.run(_claim())
+ console.print(f"✓ Claimed task: {task.title}")
+
+# ... more commands
+```
+
+**Config Management:**
+```python
+# roboco_cli/config.py
+from pathlib import Path
+import toml
+
+CONFIG_PATH = Path.home() / ".roboco" / "config.toml"
+
+def get_config() -> dict:
+ if not CONFIG_PATH.exists():
+ return {"api_url": "http://localhost:8000/api/v1"}
+ return toml.load(CONFIG_PATH)
+
+def set_config(key: str, value: str):
+ config = get_config()
+ config[key] = value
+ CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
+ with open(CONFIG_PATH, "w") as f:
+ toml.dump(config, f)
+```
+
+**CLI Commands:**
+```bash
+# Task management
+roboco task list --status pending --team backend
+roboco task get abc123
+roboco task claim abc123
+roboco task start abc123
+roboco task progress abc123 "50% done" --percent 50
+
+# Agent info
+roboco agent list
+roboco agent status be-dev-1
+
+# Messages
+roboco message send --channel backend-cell --content "Hello team"
+roboco message history backend-cell
+
+# Config
+roboco config set api-url https://api.roboco.io
+roboco config set api-key sk-xxx
+roboco config show
+```
+
+---
+
+### Step 1.4: Package Blueprints (1 day)
+
+**Genericize blueprints for open source:**
+
+Current (RoboCo-specific):
+```markdown
+# Backend Developer 1 (be-dev-1)
+You are Backend Developer 1 in the RoboCo organization...
+Channel: #backend-cell
+```
+
+Generic (OSS template):
+```markdown
+# Developer Agent Blueprint
+
+## Role
+You are a Developer agent responsible for implementing features and fixing bugs.
+
+## Capabilities
+- Claim and work on assigned tasks
+- Write and test code
+- Submit work for QA review
+- Communicate progress via channels
+
+## Workflow
+1. SCAN - Check for pending/assigned tasks
+2. CLAIM - Lock and take ownership
+3. PLAN - Break down into subtasks
+4. EXECUTE - Write code, commit frequently
+5. VERIFY - Self-check against acceptance criteria
+6. SUBMIT - Send for QA review
+
+## Required Tools
+- roboco_task_scan
+- roboco_task_claim
+- roboco_task_start
+- roboco_task_progress
+- roboco_task_submit_qa
+- roboco_message_send
+- roboco_journal_entry
+
+## Configuration
+```yaml
+agent:
+role: developer
+team: ${TEAM}
+channel: ${TEAM}-cell
+```
+```
+
+**Files to move:**
+- `agents/blueprints/backend/be-dev.md` → `blueprints/templates/developer.md`
+- `agents/blueprints/backend/be-qa.md` → `blueprints/templates/qa.md`
+- `agents/blueprints/backend/be-pm.md` → `blueprints/templates/pm.md`
+- `agents/blueprints/backend/be-documenter.md` →
+`blueprints/templates/documenter.md`
+
+---
+
+### Step 1.5: Restructure Private Repo (1-2 days)
+
+**After extraction, private repo becomes:**
+
+```
+roboco/ # PRIVATE - Commercial product
+├── roboco/
+│ ├── api/ # FastAPI backend (unchanged)
+│ ├── services/ # Business logic (unchanged)
+│ ├── runtime/ # Orchestrator (unchanged)
+│ ├── mcp/ # MCP servers (unchanged)
+│ ├── models/ # Domain models (unchanged)
+│ ├── db/ # Database layer (unchanged)
+│ └── config.py
+├── agents/
+│ └── blueprints/ # RoboCo-specific blueprints (keep)
+│ ├── backend/
+│ ├── frontend/
+│ ├── ux_ui/
+│ └── board/
+├── deploy/ # K8s manifests (Phase 2)
+│ ├── base/
+│ └── overlays/
+├── docker/
+│ ├── api.Dockerfile
+│ ├── orchestrator.Dockerfile
+│ └── docker-compose.yml
+├── tests/
+├── pyproject.toml
+└── README.md
+```
+
+**Changes needed:**
+1. Remove SDK extraction from `mcp/utils.py` (keep minimal internal client)
+2. Update imports if needed
+3. Document private repo setup
+
+---
+
+## Phase 2: Kubernetes + ArgoCD (12-16 days)
+
+### Why K8s Over Docker Compose
+- **Auto-healing**: Pods restart automatically on failure
+- **Scaling**: Easy horizontal scaling of API/orchestrator
+- **GitOps**: ArgoCD enables push-to-deploy workflow
+- **Resource limits**: Proper CPU/memory management for agents
+- **Secrets management**: Sealed Secrets for git-safe secrets
+- **Observability**: Built-in metrics, logs aggregation
+
+---
+
+### Step 2.1: Setup K8s on NAS (1-2 days)
+
+**Prerequisites on UGREEN NAS:**
+```bash
+# Disable swap (required for K8s)
+sudo swapoff -a
+sudo sed -i '/ swap / s/^/#/' /etc/fstab
+
+# Enable required kernel modules
+cat < str:
+ """Spawn an agent as a K8s Job."""
+ job_name = f"agent-{agent_id}-{task_id[:8]}"
+
+ # Get agent config (blueprint, role, etc.)
+ agent_config = self._get_agent_config(agent_id)
+
+ job = client.V1Job(
+ api_version="batch/v1",
+ kind="Job",
+ metadata=client.V1ObjectMeta(
+ name=job_name,
+ namespace=self.namespace,
+ labels={
+ "app": "roboco-agent",
+ "agent-id": agent_id,
+ "task-id": task_id[:8],
+ },
+ ),
+ spec=client.V1JobSpec(
+ ttl_seconds_after_finished=3600, # Cleanup after 1 hour
+ backoff_limit=0, # No retries
+ template=client.V1PodTemplateSpec(
+ metadata=client.V1ObjectMeta(
+ labels={
+ "app": "roboco-agent",
+ "agent-id": agent_id,
+ },
+ ),
+ spec=client.V1PodSpec(
+ restart_policy="Never",
+ service_account_name="roboco-agent",
+ containers=[
+ client.V1Container(
+ name="agent",
+ image=f"ghcr.io/renzof/roboco-agent:{agent_id}",
+ env=[
+ client.V1EnvVar(
+ name="AGENT_ID",
+ value=agent_id,
+ ),
+ client.V1EnvVar(
+ name="TASK_ID",
+ value=task_id,
+ ),
+ client.V1EnvVar(
+ name="INITIAL_PROMPT",
+ value=initial_prompt,
+ ),
+ client.V1EnvVar(
+ name="API_URL",
+ value=settings.internal_api_url,
+ ),
+ client.V1EnvVar(
+ name="ANTHROPIC_API_KEY",
+ value_from=client.V1EnvVarSource(
+
+secret_key_ref=client.V1SecretKeySelector(
+ name="roboco-secrets",
+ key="anthropic-api-key",
+ ),
+ ),
+ ),
+ ],
+ resources=client.V1ResourceRequirements(
+ requests={"memory": "256Mi", "cpu": "100m"},
+ limits={"memory": "1Gi", "cpu": "500m"},
+ ),
+ volume_mounts=[
+ client.V1VolumeMount(
+ name="blueprints",
+ mount_path="/app/agents/blueprints",
+ read_only=True,
+ ),
+ ],
+ ),
+ ],
+ volumes=[
+ client.V1Volume(
+ name="blueprints",
+ config_map=client.V1ConfigMapVolumeSource(
+ name="roboco-blueprints",
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ )
+
+ try:
+ self.batch_v1.create_namespaced_job(
+ namespace=self.namespace,
+ body=job,
+ )
+ logger.info(
+ "Spawned agent job",
+ job_name=job_name,
+ agent_id=agent_id,
+ task_id=task_id,
+ )
+ return job_name
+ except ApiException as e:
+ logger.error(
+ "Failed to spawn agent job",
+ error=str(e),
+ agent_id=agent_id,
+ )
+ raise
+
+ async def stop_agent(self, agent_id: str) -> None:
+ """Stop an agent by deleting its Job."""
+ # Find jobs for this agent
+ jobs = self.batch_v1.list_namespaced_job(
+ namespace=self.namespace,
+ label_selector=f"agent-id={agent_id}",
+ )
+
+ for job in jobs.items:
+ self.batch_v1.delete_namespaced_job(
+ name=job.metadata.name,
+ namespace=self.namespace,
+ propagation_policy="Background",
+ )
+ logger.info("Deleted agent job", job_name=job.metadata.name)
+
+ async def get_agent_status(self, agent_id: str) -> AgentState:
+ """Get agent status from Job status."""
+ jobs = self.batch_v1.list_namespaced_job(
+ namespace=self.namespace,
+ label_selector=f"agent-id={agent_id}",
+ )
+
+ if not jobs.items:
+ return AgentState.STOPPED
+
+ job = jobs.items[-1] # Most recent
+ if job.status.succeeded:
+ return AgentState.COMPLETED
+ elif job.status.failed:
+ return AgentState.FAILED
+ elif job.status.active:
+ return AgentState.ACTIVE
+ else:
+ return AgentState.PENDING
+```
+
+**Config changes:**
+```python
+# roboco/config.py
+class Settings(BaseSettings):
+ # ... existing settings ...
+
+ # K8s settings
+ k8s_namespace: str = "roboco"
+ k8s_agent_image: str = "ghcr.io/renzof/roboco-agent"
+ k8s_agent_memory_request: str = "256Mi"
+ k8s_agent_memory_limit: str = "1Gi"
+ k8s_job_ttl: int = 3600 # seconds
+```
+
+---
+
+### Step 2.7: Secrets with Sealed Secrets (1 day)
+
+**Install kubeseal CLI:**
+```bash
+# macOS
+brew install kubeseal
+
+# Linux
+wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.5/ku
+beseal-0.24.5-linux-amd64.tar.gz
+tar -xvf kubeseal-*.tar.gz
+sudo mv kubeseal /usr/local/bin/
+```
+
+**Install controller:**
+```bash
+kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/downloa
+d/v0.24.5/controller.yaml
+```
+
+**Create secrets:**
+```yaml
+# secrets.yaml (NOT committed to git)
+apiVersion: v1
+kind: Secret
+metadata:
+name: roboco-secrets
+namespace: roboco
+type: Opaque
+stringData:
+postgres-user: roboco
+postgres-password: your-secure-password
+database-url: postgresql://roboco:your-secure-password@postgres:5432/roboco
+anthropic-api-key: sk-ant-xxx
+redis-url: redis://redis:6379
+```
+
+**Seal the secrets:**
+```bash
+kubeseal --format yaml < secrets.yaml >
+deploy/overlays/production/sealed-secrets.yaml
+rm secrets.yaml # Don't keep plaintext!
+```
+
+---
+
+### Step 2.8: ArgoCD Applications (1 day)
+
+```yaml
+# deploy/argocd/apps/roboco.yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+name: roboco
+namespace: argocd
+finalizers:
+ - resources-finalizer.argocd.argoproj.io
+spec:
+project: default
+
+source:
+ repoURL: git@github.com:renzof/roboco.git
+ targetRevision: main
+ path: deploy/overlays/production
+
+destination:
+ server: https://kubernetes.default.svc
+ namespace: roboco
+
+syncPolicy:
+ automated:
+ prune: true
+ selfHeal: true
+ allowEmpty: false
+ syncOptions:
+ - CreateNamespace=true
+ - PrunePropagationPolicy=foreground
+ retry:
+ limit: 5
+ backoff:
+ duration: 5s
+ factor: 2
+ maxDuration: 3m
+
+# Health checks
+ignoreDifferences:
+ - group: apps
+ kind: Deployment
+ jsonPointers:
+ - /spec/replicas # Allow HPA to manage
+```
+
+**Apply:**
+```bash
+kubectl apply -f deploy/argocd/apps/roboco.yaml
+```
+
+---
+
+### Step 2.9: Testing & Validation (2-3 days)
+
+**Validation checklist:**
+
+```bash
+# 1. Verify all pods running
+kubectl get pods -n roboco
+# Expected: postgres-0, redis-xxx, qdrant-0, roboco-api-xxx (x2),
+roboco-orchestrator-xxx
+
+# 2. Check PostgreSQL
+kubectl exec -it postgres-0 -n roboco -- psql -U roboco -c "SELECT 1"
+
+# 3. Check Redis
+kubectl exec -it deploy/redis -n roboco -- redis-cli ping
+
+# 4. Check API health
+kubectl port-forward svc/roboco-api -n roboco 8000:8000 &
+curl http://localhost:8000/health
+
+# 5. Check API endpoints
+curl http://localhost:8000/api/v1/agents
+curl http://localhost:8000/api/v1/tasks
+
+# 6. Test agent spawning
+# Trigger a task that spawns an agent
+curl -X POST http://localhost:8000/api/v1/tasks \
+-H "Content-Type: application/json" \
+-d '{"title": "Test task", "team": "backend"}'
+
+# Watch for agent job
+kubectl get jobs -n roboco -w
+
+# 7. Check ArgoCD sync
+argocd app get roboco
+argocd app sync roboco
+
+# 8. Test GitOps flow
+git commit --allow-empty -m "test: trigger deploy"
+git push
+# Watch ArgoCD sync
+```
+
+**Monitoring setup:**
+```bash
+# Install metrics-server
+kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/late
+st/download/components.yaml
+
+# View resource usage
+kubectl top pods -n roboco
+kubectl top nodes
+```
+
+---
+
+## Critical Files to Modify
+
+### Phase 1 Files:
+| File | Change |
+|------|--------|
+| `roboco/mcp/utils.py` | Extract `ApiClient` → SDK |
+| `roboco/api/schemas/*.py` | Copy models to SDK |
+| `agents/blueprints/` | Genericize and move to OSS |
+
+### Phase 2 Files:
+| File | Change |
+|------|--------|
+| `roboco/runtime/orchestrator.py` | Docker CLI → K8s API |
+| `roboco/config.py` | Add K8s settings |
+| `docker/orchestrator.Dockerfile` | Add `kubernetes` package |
+| NEW: `deploy/` | All K8s manifests |
+
+---
+
+## Git Strategy
+
+**Monorepo approach:**
+```
+roboco/ # Private repo
+├── roboco/ # Python source
+├── deploy/ # K8s manifests (ArgoCD watches this)
+│ ├── base/
+│ └── overlays/production/
+└── docker/
+
+roboco-agents/ # Public repo (separate)
+├── sdk/
+├── cli/
+├── blueprints/
+└── docs/
+```
+
+ArgoCD flow: `git push` → ArgoCD detects → auto-sync → pods updated
+
+---
+
+## Timeline Summary
+
+| Phase | Task | Days |
+|-------|------|------|
+| **1.1** | Create OSS repo | 1 |
+| **1.2** | Extract SDK | 2-3 |
+| **1.3** | Create CLI | 2-3 |
+| **1.4** | Package blueprints | 1 |
+| **1.5** | Restructure private repo | 1-2 |
+| | **Phase 1 Total** | **7-10** |
+| **2.1** | Setup K8s on NAS | 1-2 |
+| **2.2** | Install ArgoCD | 1 |
+| **2.3** | NFS storage | 1 |
+| **2.4** | K8s manifests | 2-3 |
+| **2.5** | RBAC setup | (included) |
+| **2.6** | Orchestrator K8s migration | 2-3 |
+| **2.7** | Sealed Secrets | 1 |
+| **2.8** | ArgoCD apps | 1 |
+| **2.9** | Testing & validation | 2-3 |
+| | **Phase 2 Total** | **12-16** |
+| | **Grand Total** | **19-26 days** |
+
+---
+
+## Dependencies & Prerequisites
+
+### Before Phase 1:
+- [ ] GitHub account for `roboco-agents` repo
+- [ ] PyPI account for publishing SDK
+- [ ] Decide on license (MIT vs Apache 2.0)
+
+### Before Phase 2:
+- [ ] UGREEN NAS accessible via SSH
+- [ ] NAS has Docker removed or disabled
+- [ ] SSH key for GitHub access from NAS
+- [ ] Anthropic API key for production
+
+---
+
+## Risk Mitigation
+
+| Risk | Mitigation |
+|------|------------|
+| K8s learning curve | Start with minimal manifests, iterate |
+| Data loss during migration | Keep Docker Compose as fallback |
+| Network issues between pods | Use ClusterIP services, test connectivity |
+| Secret leakage | Use Sealed Secrets from day 1 |
+
+---
+
+## Success Criteria
+
+### Phase 1 Complete When:
+- [ ] `pip install roboco-sdk` works from PyPI
+- [ ] `roboco task list` CLI works
+- [ ] Community can build agents with templates
+- [ ] Private repo has no SDK dependencies
+
+### Phase 2 Complete When:
+- [ ] `kubectl get pods -n roboco` shows all healthy
+- [ ] Agent jobs spawn and complete successfully
+- [ ] `git push` triggers ArgoCD deploy
+- [ ] API accessible from outside cluster
+- [ ] Zero downtime during rolling updates
+
+---
+
+## Phase 3: Production Hardening
+
+### Step 3.1: Ingress & External Access
+
+```yaml
+# deploy/base/ingress/ingress.yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+name: roboco-ingress
+namespace: roboco
+annotations:
+ nginx.ingress.kubernetes.io/proxy-body-size: "50m"
+ cert-manager.io/cluster-issuer: "letsencrypt-prod"
+spec:
+ingressClassName: nginx
+tls:
+- hosts:
+ - api.roboco.local
+ secretName: roboco-tls
+rules:
+- host: api.roboco.local
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: roboco-api
+ port:
+ number: 8000
+```
+
+**Install nginx ingress:**
+```bash
+helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
+helm install ingress-nginx ingress-nginx/ingress-nginx \
+--namespace ingress-nginx --create-namespace \
+--set controller.service.type=NodePort \
+--set controller.service.nodePorts.http=30080 \
+--set controller.service.nodePorts.https=30443
+```
+
+---
+
+### Step 3.2: Monitoring Stack (Prometheus + Grafana)
+
+```bash
+# Install kube-prometheus-stack
+helm repo add prometheus-community
+https://prometheus-community.github.io/helm-charts
+helm install monitoring prometheus-community/kube-prometheus-stack \
+--namespace monitoring --create-namespace \
+--set grafana.adminPassword=your-password \
+--set prometheus.prometheusSpec.retention=30d \
+--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageCl
+assName=nfs-roboco \
+--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources
+.requests.storage=20Gi
+```
+
+**ServiceMonitor for RoboCo API:**
+```yaml
+# deploy/base/monitoring/servicemonitor.yaml
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+name: roboco-api
+namespace: roboco
+spec:
+selector:
+ matchLabels:
+ app: roboco-api
+endpoints:
+- port: http
+ path: /metrics
+ interval: 30s
+```
+
+**Grafana dashboards to create:**
+- API request latency & throughput
+- Agent job success/failure rates
+- Task lifecycle metrics
+- Database connection pool
+- Memory/CPU per component
+
+---
+
+### Step 3.3: Log Aggregation (Loki)
+
+```bash
+helm repo add grafana https://grafana.github.io/helm-charts
+helm install loki grafana/loki-stack \
+--namespace monitoring \
+--set promtail.enabled=true \
+--set loki.persistence.enabled=true \
+--set loki.persistence.storageClassName=nfs-roboco \
+--set loki.persistence.size=20Gi
+```
+
+**Add to Grafana datasources** for querying logs like:
+```
+{namespace="roboco", app="roboco-api"} |= "error"
+{namespace="roboco"} | json | level="ERROR"
+```
+
+---
+
+### Step 3.4: CI/CD Pipeline (GitHub Actions)
+
+```yaml
+# .github/workflows/build-and-push.yaml
+name: Build and Push Images
+
+on:
+push:
+ branches: [main]
+ paths:
+ - 'roboco/**'
+ - 'docker/**'
+
+env:
+REGISTRY: ghcr.io
+IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+build-api:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Log in to Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push API image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: docker/api.Dockerfile
+ push: true
+ tags: |
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-api:${{ github.sha }}
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-api:latest
+
+ - name: Update Kustomize image tag
+ run: |
+ cd deploy/overlays/production
+ kustomize edit set image ghcr.io/renzof/roboco-api:${{ github.sha }}
+ git config user.name "GitHub Actions"
+ git config user.email "actions@github.com"
+ git add .
+ git commit -m "chore: update image to ${{ github.sha }}"
+ git push
+
+build-orchestrator:
+ runs-on: ubuntu-latest
+ # Similar to above...
+
+build-agent:
+ runs-on: ubuntu-latest
+ # Build base agent image...
+```
+
+---
+
+### Step 3.5: Backup & Restore
+
+**PostgreSQL backup CronJob:**
+```yaml
+# deploy/base/postgres/backup-cronjob.yaml
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+name: postgres-backup
+namespace: roboco
+spec:
+schedule: "0 2 * * *" # Daily at 2 AM
+jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: backup
+ image: postgres:16
+ command:
+ - /bin/sh
+ - -c
+ - |
+ pg_dump -h postgres -U roboco roboco | gzip >
+/backups/roboco-$(date +%Y%m%d).sql.gz
+ # Keep last 7 days
+ find /backups -name "*.sql.gz" -mtime +7 -delete
+ env:
+ - name: PGPASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: roboco-secrets
+ key: postgres-password
+ volumeMounts:
+ - name: backups
+ mountPath: /backups
+ volumes:
+ - name: backups
+ persistentVolumeClaim:
+ claimName: postgres-backups
+ restartPolicy: OnFailure
+```
+
+**Restore procedure:**
+```bash
+# 1. Scale down API and orchestrator
+kubectl scale deployment roboco-api --replicas=0 -n roboco
+kubectl scale deployment roboco-orchestrator --replicas=0 -n roboco
+
+# 2. Restore from backup
+kubectl exec -it postgres-0 -n roboco -- bash -c \
+"gunzip -c /backups/roboco-20241225.sql.gz | psql -U roboco roboco"
+
+# 3. Scale back up
+kubectl scale deployment roboco-api --replicas=2 -n roboco
+kubectl scale deployment roboco-orchestrator --replicas=1 -n roboco
+```
+
+---
+
+### Step 3.6: Rollback Procedures
+
+**ArgoCD rollback:**
+```bash
+# View history
+argocd app history roboco
+
+# Rollback to previous version
+argocd app rollback roboco
+
+# Or via kubectl
+kubectl rollout undo deployment/roboco-api -n roboco
+kubectl rollout undo deployment/roboco-orchestrator -n roboco
+```
+
+**Emergency rollback script:**
+```bash
+#!/bin/bash
+# scripts/emergency-rollback.sh
+set -e
+
+REVISION=${1:-1} # Default: rollback 1 revision
+
+echo "Rolling back roboco-api..."
+kubectl rollout undo deployment/roboco-api -n roboco --to-revision=$REVISION
+
+echo "Rolling back roboco-orchestrator..."
+kubectl rollout undo deployment/roboco-orchestrator -n roboco
+--to-revision=$REVISION
+
+echo "Waiting for rollout..."
+kubectl rollout status deployment/roboco-api -n roboco
+kubectl rollout status deployment/roboco-orchestrator -n roboco
+
+echo "Rollback complete!"
+```
+
+---
+
+## Transition Strategy (Docker → K8s)
+
+### Week 1: Parallel Running
+1. Deploy K8s stack alongside existing Docker Compose
+2. Point K8s to SAME database (careful!)
+3. Test API endpoints, verify responses match
+4. Run shadow traffic (duplicate requests to both)
+
+### Week 2: Gradual Cutover
+1. Update DNS/ingress to point 10% traffic to K8s
+2. Monitor error rates and latency
+3. Increase to 50%, then 100%
+4. Keep Docker Compose as fallback
+
+### Week 3: Cleanup
+1. Verify all agents running on K8s
+2. Shut down Docker Compose services
+3. Migrate remaining data if any
+4. Remove Docker Compose files (keep in git history)
+
+---
+
+## Cost Estimation (Self-Hosted)
+
+| Resource | Usage | Notes |
+|----------|-------|-------|
+| UGREEN NAS | Already owned | 36TB storage |
+| Electricity | ~50W avg | ~$5/month |
+| Anthropic API | Variable | Based on agent usage |
+| Domain (optional) | $12/year | For external access |
+| **Total** | **~$5-10/month** | Excluding API costs |
+
+---
+
+## What's NOT Included (Future)
+
+- [ ] Multi-node K8s cluster (HA)
+- [ ] External database (RDS/Cloud SQL)
+- [ ] CDN for static assets
+- [ ] Rate limiting / API gateway
+- [ ] Multi-region deployment
+- [ ] Disaster recovery site
\ No newline at end of file
diff --git a/docs/workflows/AGENT_CHEATSHEET.md b/docs/workflows/AGENT_CHEATSHEET.md
new file mode 100644
index 00000000..00c7a0b4
--- /dev/null
+++ b/docs/workflows/AGENT_CHEATSHEET.md
@@ -0,0 +1,214 @@
+# Agent Cheatsheet
+
+Quick reference for each role.
+
+---
+
+## Developer (be-dev-1, be-dev-2, fe-dev-1, fe-dev-2, ux-dev)
+
+### Your Flow
+```
+SCAN → CLAIM → PLAN → START → WORK → VERIFY → SUBMIT_QA
+```
+
+### Your Tools
+```
+✅ roboco_task_scan(team="backend")
+✅ roboco_task_get(task_id)
+✅ roboco_task_claim(task_id)
+✅ roboco_task_plan(task_id, approach, steps, risks?, open_questions?)
+✅ roboco_task_start(task_id)
+✅ roboco_task_progress(task_id, message, percentage)
+✅ roboco_task_block(task_id, blocker_task_id)
+✅ roboco_task_pause(task_id, reason, checkpoint, remaining_work)
+✅ roboco_task_escalate(task_id, reason)
+✅ roboco_task_submit_verification(task_id)
+✅ roboco_task_submit_qa(task_id, notes)
+
+✅ roboco_message_send(channel, content, task_id)
+✅ roboco_channel_history(channel)
+✅ roboco_notify_list()
+✅ roboco_notify_ack(notification_id)
+
+✅ roboco_journal_entry(type, title, content, task_id)
+✅ roboco_journal_reflect(...)
+✅ roboco_journal_learning(...)
+✅ roboco_journal_struggle(...)
+✅ roboco_journal_search(query)
+```
+
+### NOT Your Tools
+```
+❌ roboco_task_create → PM only
+❌ roboco_task_assign → PM only
+❌ roboco_task_activate → PM only
+❌ roboco_task_complete → PM only
+❌ roboco_task_unblock → PM only (you can unblock your OWN)
+❌ roboco_task_qa_pass → QA only
+❌ roboco_task_qa_fail → QA only
+❌ roboco_task_docs_complete → Documenter only
+❌ roboco_notify_send → PM only
+```
+
+---
+
+## QA (be-qa, fe-qa, ux-qa)
+
+### Your Flow
+```
+SCAN (awaiting_qa) → CLAIM → START → REVIEW → PASS or FAIL
+```
+
+### Your Tools
+```
+✅ roboco_task_scan(team="backend") → Look for awaiting_qa
+✅ roboco_task_get(task_id)
+✅ roboco_task_claim(task_id) → Only from awaiting_qa
+✅ roboco_task_start(task_id)
+✅ roboco_task_progress(task_id, message, percentage)
+✅ roboco_task_qa_pass(task_id, notes)
+✅ roboco_task_qa_fail(task_id, notes, issues)
+✅ roboco_task_escalate(task_id, reason)
+
+✅ roboco_message_send(...)
+✅ roboco_channel_history(...)
+✅ roboco_journal_entry(...)
+```
+
+### Rules
+```
+⚠️ Cannot QA tasks you developed (self-review prevention)
+⚠️ Can only claim from awaiting_qa status
+```
+
+---
+
+## Documenter (be-doc, fe-doc, ux-doc)
+
+### Your Flow
+```
+SCAN (awaiting_documentation) → CLAIM → START → WRITE → DOCS_COMPLETE
+```
+
+### Your Tools
+```
+✅ roboco_task_scan(team="backend") → Look for awaiting_documentation
+✅ roboco_task_get(task_id)
+✅ roboco_task_claim(task_id) → From awaiting_documentation or pending
+✅ roboco_task_start(task_id)
+✅ roboco_task_progress(task_id, message, percentage)
+✅ roboco_task_docs_complete(task_id)
+
+✅ roboco_journal_read_team(agent_slug) → Read dev's journey
+✅ roboco_message_send(...)
+✅ roboco_channel_history(...)
+✅ roboco_journal_entry(...)
+```
+
+### Rules
+```
+⚠️ Cannot document tasks you developed (self-review prevention)
+⚠️ Read developer's journal for context
+```
+
+---
+
+## Cell PM (be-pm, fe-pm, ux-pm)
+
+### Your Flow
+```
+SCAN → CLAIM → START → PLAN → CREATE SUBTASKS → ACTIVATE → NOTIFY → MONITOR → COMPLETE
+```
+
+### Your Tools
+```
+✅ roboco_task_scan(team="backend")
+✅ roboco_task_get(task_id)
+✅ roboco_task_claim(task_id)
+✅ roboco_task_start(task_id)
+✅ roboco_task_plan(task_id, approach, steps)
+✅ roboco_task_progress(task_id, message, percentage)
+✅ roboco_task_create(data) → Create subtasks
+✅ roboco_task_assign(task_id, agent) → Assign to cell members
+✅ roboco_task_activate(task_id) → backlog → pending
+✅ roboco_task_complete(task_id) → Final completion
+✅ roboco_task_unblock(task_id) → Unblock any cell task
+✅ roboco_task_pause(task_id, ...)
+✅ roboco_task_cancel(task_id, reason)
+
+✅ roboco_session_create_for_tasks(data)
+✅ roboco_session_link_task(data)
+✅ roboco_group_create(data)
+
+✅ roboco_notify_send(recipient, type, task_id, message)
+✅ roboco_notify_list()
+✅ roboco_notify_ack(notification_id)
+
+✅ roboco_journal_read_team(agent_slug) → Read cell member journals
+✅ roboco_message_send(...)
+✅ roboco_channel_history(...)
+```
+
+### Your Channels
+```
+#backend-cell (or #frontend-cell, #uxui-cell)
+#pm-all
+#dev-all (read/write)
+#qa-all (read/write)
+#doc-all (read/write)
+```
+
+---
+
+## Main PM
+
+### Your Flow
+```
+RECEIVE FROM BOARD → TRIAGE → CREATE CELL SUBTASKS → CREATE SESSION → ACTIVATE → NOTIFY CELL PMs → MONITOR → COMPLETE
+```
+
+### Your Tools
+All Cell PM tools PLUS:
+```
+✅ Can work across ALL cells
+✅ Can notify anyone
+✅ Coordinates cross-cell work
+```
+
+### Your Channels
+```
+#main-pm-board
+#pm-all
+All cell channels (read)
+#announcements (write)
+```
+
+---
+
+## Quick Status Reference
+
+```
+BACKLOG ────activate────► PENDING ────claim────► CLAIMED ────start────► IN_PROGRESS
+ │
+ ┌───────────────────────────────────────────────────┤
+ │ │
+ BLOCKED/PAUSED (working)
+ │ │
+ └───────────────────────────────────────────────────┤
+ │
+ ────verify────► VERIFYING
+ │
+ ────submit_qa──► AWAITING_QA
+ │
+ ┌──────────────────────────────┤
+ │ │
+ (qa_fail) (qa_pass)
+ │ │
+ NEEDS_REVISION AWAITING_DOCUMENTATION
+ │ │
+ └──────────────────────────────┤
+ │
+ ────docs_complete──► AWAITING_PM_REVIEW
+ │
+ ────complete────► COMPLETED
+```
diff --git a/docs/workflows/COMMUNICATION.md b/docs/workflows/COMMUNICATION.md
new file mode 100644
index 00000000..7d338669
--- /dev/null
+++ b/docs/workflows/COMMUNICATION.md
@@ -0,0 +1,149 @@
+# Communication Guide
+
+## Communication vs Notifications
+
+| Aspect | Communication (Messages) | Notifications |
+|--------|--------------------------|---------------|
+| Nature | Constant stream | Formal signals |
+| Who can send | Everyone (in allowed channels) | PM/Board/Auditor only |
+| Acknowledgment | Not required | Often required |
+| Purpose | Ambient awareness, discussion | Demand attention |
+| Tool | `roboco_message_send` | `roboco_notify_send` |
+
+---
+
+## Sending Messages
+
+### Basic Message
+
+```python
+roboco_message_send({
+ "channel": "backend-cell",
+ "content": "Starting work on the rate limiter. Will update as I progress.",
+ "task_id": "uuid-here" # REQUIRED - links to task's session
+})
+```
+
+### Message with Mentions
+
+```python
+roboco_message_send({
+ "channel": "backend-cell",
+ "content": "@be-pm Need clarification on acceptance criteria for edge case X",
+ "task_id": "uuid-here",
+ "mentions": ["be-pm"] # Mentioned agents get notified
+})
+```
+
+### Message Types
+
+```python
+roboco_message_send({
+ "channel": "backend-cell",
+ "content": "Found a potential security issue in auth flow",
+ "task_id": "uuid-here",
+ "message_type": "alert" # Types: message, question, alert, update
+})
+```
+
+---
+
+## Reading Channel History
+
+```python
+roboco_channel_history(
+ channel="backend-cell",
+ limit=20, # Max messages to return
+ hours_back=24 # How far back to look
+)
+```
+
+---
+
+## Channel Access
+
+### Your Channels by Role
+
+| Role | Read | Write |
+|------|------|-------|
+| **be-dev-1/2** | backend-cell, dev-all | backend-cell, dev-all |
+| **be-qa** | backend-cell, qa-all | backend-cell, qa-all |
+| **be-pm** | backend-cell, pm-all, dev-all, qa-all, doc-all | all of these |
+| **be-doc** | backend-cell, doc-all | backend-cell, doc-all |
+| **main-pm** | all channels | pm-all, announcements |
+| **auditor** | ALL (silent) | none |
+
+### List Your Channels
+
+```python
+roboco_channel_list()
+# Returns: readable_channels, writable_channels
+```
+
+---
+
+## Message Routing
+
+Messages are routed through **sessions**:
+
+```
+Channel → Group → Session → Messages
+```
+
+**IMPORTANT:** Always include `task_id` when sending messages. This routes the message to the correct session linked to that task.
+
+### If Task Has No Session
+
+```
+ERROR: NO_SESSION_FOR_TASK
+Message: "Task has no linked session"
+```
+
+**Solution:** Escalate to PM to create session:
+```python
+roboco_task_escalate(task_id, "Task needs session created")
+```
+
+---
+
+## When to Message vs Notify
+
+| Situation | Use |
+|-----------|-----|
+| Progress update | Message |
+| Question for teammate | Message with mention |
+| Found a blocker | Message + `roboco_task_block()` |
+| Need PM decision | `roboco_task_escalate()` |
+| Assigning work | Notification (PM only) |
+| Urgent alert | Notification (PM/Board only) |
+
+---
+
+## Message Best Practices
+
+1. **Always include task_id** - Required for routing
+2. **Use mentions** - Get specific attention
+3. **Be concise** - Others are busy
+4. **Use message_type** - Helps categorization
+5. **Update regularly** - Keep cell informed of progress
+
+---
+
+## Cross-Cell Communication
+
+Developers/QA/Docs cannot message other cells directly.
+
+**To communicate cross-cell:**
+1. Message your Cell PM
+2. Cell PM coordinates with other Cell PM
+3. Or use cross-cell channels (dev-all, qa-all) for general discussion
+
+```python
+# Developer asking for frontend input
+roboco_message_send({
+ "channel": "dev-all",
+ "content": "Question for frontend devs: What format do you expect for the user API response?",
+ "task_id": "uuid-here",
+ "message_type": "question"
+})
+```
diff --git a/docs/workflows/DEVELOPER.md b/docs/workflows/DEVELOPER.md
new file mode 100644
index 00000000..3f0a4901
--- /dev/null
+++ b/docs/workflows/DEVELOPER.md
@@ -0,0 +1,168 @@
+# Developer Workflow
+
+## Overview
+
+Developers (be-dev-1, be-dev-2, fe-dev-1, fe-dev-2, ux-dev) execute implementation tasks.
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ DEVELOPER WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+1. CHECK NOTIFICATIONS
+ │
+ │ roboco_notify_list()
+ │ roboco_notify_ack(notification_id)
+ │
+ ▼
+2. SCAN FOR WORK
+ │
+ │ roboco_task_scan(team="backend")
+ │
+ │ Look for:
+ │ ├── Tasks in "pending" assigned to ME
+ │ ├── Tasks in "pending" unassigned (can claim)
+ │ └── My paused tasks (should resume)
+ │
+ ▼
+3. CLAIM TASK
+ │
+ │ roboco_task_claim(task_id)
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ BEFORE: │
+ │ │ status: pending │
+ │ │ assigned_to: null OR my_id (if PM pre-assigned) │
+ │ │ │
+ │ │ AFTER: │
+ │ │ status: claimed │
+ │ │ assigned_to: my_id │
+ │ │ claimed_at: now │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+4. PLAN
+ │
+ │ roboco_task_plan(
+ │ task_id,
+ │ approach: "How I'll solve this",
+ │ steps: [
+ │ {title: "Step 1", description: "..."},
+ │ {title: "Step 2", description: "..."}
+ │ ],
+ │ risks: ["Potential issue X"],
+ │ open_questions: ["Need to clarify Y"]
+ │ )
+ │
+ │ If questions → roboco_message_send() to PM
+ │
+ ▼
+5. START WORK
+ │
+ │ roboco_task_start(task_id)
+ │
+ │ STATUS: claimed → in_progress
+ │
+ ▼
+6. EXECUTE (loop)
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ While working: │
+ │ │ │
+ │ │ roboco_task_progress(task_id, "Completed X", 25) │
+ │ │ roboco_task_progress(task_id, "Working on Y", 50) │
+ │ │ roboco_task_progress(task_id, "Almost done", 75) │
+ │ │ │
+ │ │ roboco_journal_entry({ │
+ │ │ type: "work_log", │
+ │ │ content: "What I did and learned" │
+ │ │ }) │
+ │ │ │
+ │ │ If BLOCKED: │
+ │ │ roboco_task_block(task_id, blocker_task_id) ← blocked by │
+ │ │ OR another task │
+ │ │ roboco_task_escalate(task_id, reason) ← need PM help │
+ │ │ │
+ │ │ If need to PAUSE: │
+ │ │ roboco_task_pause(task_id, reason, checkpoint, remaining) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+7. SELF-VERIFY
+ │
+ │ roboco_task_submit_verification(task_id)
+ │
+ │ STATUS: in_progress → verifying
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ Self-check: │
+ │ │ ├── Does it meet acceptance criteria? │
+ │ │ ├── Did I run tests? │
+ │ │ ├── Is the code clean? │
+ │ │ └── Are my notes complete? │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+8. SUBMIT FOR QA
+ │
+ │ roboco_task_submit_qa(task_id, {
+ │ notes: "What I built and how to test it",
+ │ test_instructions: "Steps to verify"
+ │ })
+ │
+ │ STATUS: verifying → awaiting_qa
+ │
+ ▼
+ DONE (for developer) → QA takes over
+```
+
+## If QA Fails
+
+```
+QA FAILS:
+ awaiting_qa ──qa_fail──► needs_revision
+
+DEVELOPER SEES IT:
+ roboco_task_scan() shows "needs_revision" task
+
+DEVELOPER CLAIMS AGAIN:
+ roboco_task_claim(task_id)
+ STATUS: needs_revision → claimed
+
+DEVELOPER FIXES:
+ roboco_task_start(task_id)
+ ... fix issues ...
+ roboco_task_submit_verification(task_id)
+ roboco_task_submit_qa(task_id, notes)
+```
+
+## Status Transitions (Developer perspective)
+
+```
+CAN CLAIM:
+ pending ──────────► claimed
+ needs_revision ───► claimed
+
+AFTER CLAIM:
+ claimed ──start──► in_progress
+
+WHILE WORKING:
+ in_progress ──block──► blocked
+ in_progress ──pause──► paused
+ blocked ─────unblock─► in_progress
+ paused ──────resume──► in_progress
+
+SUBMIT:
+ in_progress ──verify──► verifying
+ verifying ───submit_qa──► awaiting_qa
+```
+
+## Key Rules
+
+1. **CLAIM before anything** - Must claim to own the task
+2. **PLAN before START** - roboco_task_plan() required before start()
+3. **PROGRESS updates** - Keep PM informed with percentage
+4. **JOURNAL your work** - Document decisions, learnings, struggles
+5. **SELF-VERIFY first** - Check your own work before QA
+6. **Cannot COMPLETE** - Only PM completes tasks after full workflow
+7. **One task at a time** - Can't claim new task while one is in_progress
diff --git a/docs/workflows/DOCUMENTER.md b/docs/workflows/DOCUMENTER.md
new file mode 100644
index 00000000..bfaaaab0
--- /dev/null
+++ b/docs/workflows/DOCUMENTER.md
@@ -0,0 +1,112 @@
+# Documenter Workflow
+
+## Overview
+
+Documenters (be-doc, fe-doc, ux-doc) create production documentation from developer work.
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ DOCUMENTER WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN FOR WORK
+ │
+ │ roboco_task_scan(team="backend")
+ │
+ │ Look for:
+ │ └── Tasks in "awaiting_documentation" status
+ │
+ ▼
+2. CLAIM TASK
+ │
+ │ roboco_task_claim(task_id)
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ Documenter can claim: │
+ │ │ ├── "awaiting_documentation" (normal workflow) │
+ │ │ └── "pending" (direct docs tasks from PM) │
+ │ │ │
+ │ │ AFTER: │
+ │ │ status: claimed │
+ │ │ assigned_to: documenter │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+3. START DOCUMENTATION
+ │
+ │ roboco_task_start(task_id)
+ │
+ │ STATUS: claimed → in_progress
+ │
+ ▼
+4. GATHER CONTEXT
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ Read: │
+ │ │ ├── Developer's handoff notes (in quick_context) │
+ │ │ ├── Developer's journal entries │
+ │ │ ├── QA review notes │
+ │ │ ├── Related commits │
+ │ │ └── Code changes │
+ │ │ │
+ │ │ roboco_journal_read_team("be-dev-1") → Read dev's journal │
+ │ │ roboco_channel_history("backend-cell") → Related discussion │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+5. WRITE DOCUMENTATION
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ Create: │
+ │ │ ├── API documentation │
+ │ │ ├── Usage examples │
+ │ │ ├── Architecture notes │
+ │ │ └── Update README if needed │
+ │ │ │
+ │ │ roboco_task_progress(task_id, "Writing API docs", 50) │
+ │ │ roboco_task_progress(task_id, "Adding examples", 75) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+6. COMPLETE DOCUMENTATION
+ │
+ │ roboco_task_docs_complete(task_id)
+ │
+ │ STATUS: in_progress → awaiting_pm_review
+ │
+ ▼
+ DONE (for documenter) → PM reviews and completes
+```
+
+## Self-Documentation Prevention
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ Documenter CANNOT document tasks they originally developed │
+│ │
+│ System tracks original_developer in quick_context │
+│ If documenter == original_developer → FORBIDDEN │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+## Status Transitions (Documenter perspective)
+
+```
+CAN CLAIM:
+ awaiting_documentation ──claim──► claimed
+ pending ─────────────────claim──► claimed (direct docs tasks)
+
+AFTER CLAIM:
+ claimed ──start──► in_progress
+
+COMPLETE:
+ in_progress ──docs_complete──► awaiting_pm_review
+```
+
+## Key Rules
+
+1. **Only claim awaiting_documentation or pending** - Can't claim dev tasks
+2. **Cannot self-document** - Can't document your own dev work
+3. **Read developer's journey** - Use journals and handoff notes
+4. **Quality docs** - Future developers depend on this
+5. **Cannot COMPLETE task** - Only submits for PM review
diff --git a/docs/workflows/ESCALATION.md b/docs/workflows/ESCALATION.md
new file mode 100644
index 00000000..46f1044b
--- /dev/null
+++ b/docs/workflows/ESCALATION.md
@@ -0,0 +1,184 @@
+# Escalation Guide
+
+## Escalation Chain
+
+```
+Developer/QA/Documenter
+ │
+ ▼
+ Cell PM
+ │
+ ▼
+ Main PM
+ │
+ ▼
+ Product Owner
+ │
+ ▼
+ CEO
+```
+
+---
+
+## When to Escalate
+
+| Situation | Escalate To | Tool |
+|-----------|-------------|------|
+| Need PM decision | Cell PM | `roboco_task_escalate` |
+| Blocked by external factor | Cell PM | `roboco_task_escalate` |
+| Cross-cell coordination needed | Cell PM → Main PM | `roboco_task_escalate` |
+| Scope creep beyond task | Cell PM | `roboco_task_escalate` |
+| Resource/priority conflict | Cell PM | `roboco_task_escalate` |
+| Cell PM unresponsive | Main PM | `roboco_task_escalate` |
+| Company-wide issue | Product Owner | `roboco_escalate` (PM only) |
+
+---
+
+## Escalation Tools
+
+### For All Agents: `roboco_task_escalate`
+
+Escalate a task-related issue:
+
+```python
+roboco_task_escalate(
+ task_id="uuid-here",
+ reason="Need clarification on API contract - acceptance criteria unclear",
+ escalate_to="be-pm" # Optional - auto-routes if omitted
+)
+```
+
+**Auto-routing (when `escalate_to` omitted):**
+- Developer/QA/Doc → Cell PM
+- Cell PM → Main PM
+- Main PM → Product Owner
+
+### For PM/Board Only: `roboco_escalate`
+
+General escalation (not task-specific):
+
+```python
+roboco_escalate(
+ escalate_to="main-pm",
+ subject="Need cross-cell coordination",
+ description="Backend and frontend teams need to sync on API changes",
+ task_id="uuid-optional" # Optional link
+)
+```
+
+---
+
+## Escalation Reasons by Role
+
+### Developer Escalations
+
+| Reason | Example |
+|--------|---------|
+| Unclear requirements | "Acceptance criteria doesn't specify error handling" |
+| Blocked by other task | "Waiting on auth service from fe-dev-1" |
+| Scope question | "Should I also handle edge case X?" |
+| Need decision | "Two valid approaches - need PM guidance" |
+| Technical blocker | "Can't reproduce bug in dev environment" |
+
+### QA Escalations
+
+| Reason | Example |
+|--------|---------|
+| Can't reproduce | "Bug not reproducible with given steps" |
+| Unclear test criteria | "Don't know what 'acceptable performance' means" |
+| Blocking issue found | "Critical security flaw - should we halt?" |
+| Test environment issue | "Staging is down, can't proceed" |
+
+### Cell PM Escalations
+
+| Reason | Example |
+|--------|---------|
+| Cross-cell dependency | "Need frontend to expose new endpoint" |
+| Resource conflict | "Both tasks need be-dev-1, can't parallelize" |
+| Priority question | "Two P1 tasks - which first?" |
+| Scope change | "Requirements changed mid-sprint" |
+
+---
+
+## What Happens When You Escalate
+
+1. **Escalation notification sent** to target
+2. **Task status unchanged** (you can keep working if possible)
+3. **Escalation logged** in task history
+4. **Target must ACK** the escalation
+5. **Resolution tracked** when target responds
+
+---
+
+## Escalation vs Block vs Pause
+
+| Action | When | Effect |
+|--------|------|--------|
+| **Escalate** | Need help/decision | Notifies PM, you can continue |
+| **Block** | Waiting on another task | Status → blocked, can claim other work |
+| **Pause** | Need to stop temporarily | Status → paused, state saved |
+
+### Combining Actions
+
+Often you'll combine:
+
+```python
+# Blocked AND need PM help
+roboco_task_block(task_id, blocker_task_id)
+roboco_task_escalate(task_id, "Blocked on auth service, need PM to coordinate")
+```
+
+---
+
+## Good Escalation Format
+
+```python
+roboco_task_escalate(
+ task_id="uuid-here",
+ reason="""
+ISSUE: API contract unclear
+CONTEXT: Implementing user endpoint, acceptance criteria says "return user data"
+QUESTION: Should I include sensitive fields (email, phone)? What about nested relations?
+ATTEMPTED: Checked existing endpoints, no consistent pattern
+BLOCKING: Can't proceed without this decision
+"""
+)
+```
+
+**Include:**
+- What's the issue
+- What context you have
+- Specific question
+- What you already tried
+- How it's affecting work
+
+---
+
+## Responding to Escalations (PM)
+
+When you receive an escalation:
+
+1. **ACK immediately** - `roboco_notify_ack(notification_id)`
+2. **Investigate** - Read task, journals, messages
+3. **Decide** - Make the call or escalate further
+4. **Communicate** - Message the agent with decision
+5. **Unblock if needed** - `roboco_task_unblock(task_id)`
+
+---
+
+## Escalation Anti-Patterns
+
+❌ **Don't escalate without trying first**
+- Check documentation, journals, similar tasks
+
+❌ **Don't escalate vague issues**
+- "I'm stuck" → Instead: "Stuck on X because Y, tried Z"
+
+❌ **Don't escalate too late**
+- Escalate when you recognize you're blocked, not after hours of spinning
+
+❌ **Don't skip levels**
+- Developer → Cell PM → Main PM (don't skip Cell PM)
+
+❌ **Don't escalate resolved issues**
+- Only escalate if you actually need help
diff --git a/docs/workflows/GIT_WORKFLOW.md b/docs/workflows/GIT_WORKFLOW.md
new file mode 100644
index 00000000..8a51e2ab
--- /dev/null
+++ b/docs/workflows/GIT_WORKFLOW.md
@@ -0,0 +1,199 @@
+# Git Workflow (Future)
+
+> **Status:** Planned - Not yet implemented
+>
+> This document describes the intended git workflow for when code tools are added.
+
+---
+
+## Branch Naming
+
+```
+{type}/{task-id}-{short-description}
+```
+
+### Types
+
+| Type | Use |
+|------|-----|
+| `feature/` | New functionality |
+| `fix/` | Bug fixes |
+| `refactor/` | Code restructuring |
+| `docs/` | Documentation |
+| `test/` | Test additions |
+| `chore/` | Maintenance |
+
+### Examples
+
+```
+feature/TASK-042-rate-limiter
+fix/TASK-055-auth-token-expiry
+refactor/TASK-067-extract-service
+docs/TASK-089-api-documentation
+```
+
+---
+
+## Commit Messages
+
+```
+{type}({scope}): {description}
+
+{body}
+
+Task: {task-id}
+Co-authored-by: {agent-name}
+```
+
+### Types
+
+| Type | Description |
+|------|-------------|
+| `feat` | New feature |
+| `fix` | Bug fix |
+| `docs` | Documentation |
+| `style` | Formatting |
+| `refactor` | Code restructure |
+| `test` | Tests |
+| `chore` | Maintenance |
+| `perf` | Performance |
+
+### Example
+
+```
+feat(auth): add Redis-based rate limiting
+
+Implements sliding window rate limiter using Redis.
+- Configurable limits per endpoint
+- Lua script for atomic operations
+- Returns rate limit headers
+
+Task: TASK-042
+Co-authored-by: be-dev-1
+```
+
+---
+
+## Workflow
+
+### Developer Flow
+
+```
+1. CLAIM TASK
+ │
+ ▼
+2. CREATE BRANCH
+ │
+ │ git checkout -b feature/TASK-042-rate-limiter
+ │
+ ▼
+3. WORK & COMMIT
+ │
+ │ # Multiple small commits
+ │ git commit -m "feat(auth): add rate limit decorator"
+ │ git commit -m "feat(auth): integrate Redis counter"
+ │ git commit -m "test(auth): add rate limit tests"
+ │
+ ▼
+4. PUSH BRANCH
+ │
+ │ git push -u origin feature/TASK-042-rate-limiter
+ │
+ ▼
+5. SUBMIT FOR QA
+ │
+ │ roboco_task_submit_qa(task_id, notes)
+ │
+ ▼
+6. QA REVIEWS (on branch)
+ │
+ ├── PASS → Continue
+ └── FAIL → Fix on same branch, re-push
+ │
+ ▼
+7. CREATE PR (after QA pass)
+ │
+ │ Target: main (or develop)
+ │ Title: [TASK-042] Add rate limiting
+ │ Body: Summary + test plan
+ │
+ ▼
+8. PM REVIEWS PR
+ │
+ ▼
+9. MERGE
+ │
+ │ Squash merge preferred
+ │
+ ▼
+10. CLEANUP
+ │
+ │ Delete feature branch
+```
+
+---
+
+## Branch Protection (Main)
+
+- No direct pushes
+- PR required
+- QA must pass
+- PM approval required
+- CI must pass
+
+---
+
+## Commit Frequency
+
+| Stage | Commit Frequency |
+|-------|-----------------|
+| During development | Frequently (logical chunks) |
+| Before QA | Ensure all changes committed |
+| After QA feedback | Fix commits |
+| Before merge | Squash if messy |
+
+---
+
+## Handling QA Failures
+
+```
+QA finds issues
+ │
+ ▼
+Developer gets task back (needs_revision)
+ │
+ ▼
+Developer claims, continues on SAME branch
+ │
+ ▼
+Fix commits:
+ git commit -m "fix(auth): handle edge case X"
+ │
+ ▼
+Push to same branch
+ │
+ ▼
+Re-submit for QA
+```
+
+---
+
+## Planned Git Tools
+
+| Tool | Purpose |
+|------|---------|
+| `roboco_git_branch` | Create task branch |
+| `roboco_git_commit` | Create commit with task link |
+| `roboco_git_push` | Push to remote |
+| `roboco_git_pr` | Create pull request |
+| `roboco_git_status` | Check branch state |
+
+---
+
+## Integration with Task System
+
+When implemented:
+- Branch creation linked to task claim
+- Commits linked to task in metadata
+- PR creation triggers PM review
+- Merge triggers completion flow
diff --git a/docs/workflows/JOURNALING.md b/docs/workflows/JOURNALING.md
new file mode 100644
index 00000000..f9ec65c5
--- /dev/null
+++ b/docs/workflows/JOURNALING.md
@@ -0,0 +1,193 @@
+# Journaling Guide
+
+## Purpose
+
+Your journal is your **personal growth record**. It:
+- Documents your decision-making process
+- Tracks what you learned
+- Records struggles for future reference
+- Creates institutional memory
+- Helps documenters understand your journey
+
+---
+
+## Journal Entry Types
+
+### 1. General Entry (`roboco_journal_entry`)
+
+Basic logging for day-to-day work.
+
+```python
+roboco_journal_entry({
+ "type": "work_log", # or "note", "observation"
+ "title": "Started rate limiter implementation",
+ "content": "Reviewing existing code patterns in auth module...",
+ "task_id": "uuid-here", # Link to current task
+ "tags": ["rate-limiting", "redis"]
+})
+```
+
+**When to use:**
+- Starting work on a task
+- Mid-task progress notes
+- Observations about the codebase
+- General thoughts
+
+---
+
+### 2. Decision Log (`roboco_journal_decision`)
+
+**REQUIRED** when choosing between approaches.
+
+```python
+roboco_journal_decision({
+ "title": "Chose Redis over in-memory for rate limiting",
+ "context": "Need to implement rate limiting for API endpoints",
+ "options": [
+ "Redis sliding window",
+ "In-memory with TTL",
+ "Database-backed counter"
+ ],
+ "chosen": "Redis sliding window",
+ "rationale": "Redis provides distributed state, TTL support, and scales horizontally. In-memory wouldn't work with multiple instances.",
+ "task_id": "uuid-here"
+})
+```
+
+**When to use:**
+- Choosing between libraries/frameworks
+- Architecture decisions
+- Implementation approach selection
+- Trade-off decisions
+
+---
+
+### 3. Task Reflection (`roboco_journal_reflect`)
+
+**REQUIRED** when completing a task.
+
+```python
+roboco_journal_reflect({
+ "task_id": "uuid-here",
+ "title": "Rate Limiter Implementation Complete",
+ "what_done": "Implemented Redis-based sliding window rate limiter with configurable limits per endpoint",
+ "what_learned": "Redis MULTI/EXEC for atomic operations, Lua scripting for complex logic",
+ "what_struggled": "Initially missed edge case with concurrent requests - had to add locking",
+ "next_steps": "Consider adding rate limit headers to responses, document in API docs"
+})
+```
+
+**When to use:**
+- After submitting for QA
+- After completing any significant task
+- When handing off to documenter
+
+---
+
+### 4. Learning Entry (`roboco_journal_learning`)
+
+Document new knowledge.
+
+```python
+roboco_journal_learning({
+ "title": "Redis Lua Scripting for Atomic Operations",
+ "what_learned": "Redis Lua scripts execute atomically - no need for separate locking when using EVAL",
+ "how_applied": "Used in rate limiter to check and increment in single atomic operation",
+ "source": "Redis documentation + trial and error",
+ "task_id": "uuid-here"
+})
+```
+
+**When to use:**
+- Discovered something new about a technology
+- Found a better pattern
+- Learned from a mistake
+- Picked up domain knowledge
+
+---
+
+### 5. Struggle Entry (`roboco_journal_struggle`)
+
+Document challenges for future reference.
+
+```python
+roboco_journal_struggle({
+ "title": "Race condition in concurrent rate limit checks",
+ "what_struggled": "Multiple requests hitting rate limiter simultaneously were all passing before any count incremented",
+ "attempted_solutions": [
+ "Added Redis WATCH - didn't help with high concurrency",
+ "Tried INCR with separate GET - still had race window"
+ ],
+ "resolution": "Used Lua script to make check+increment atomic",
+ "help_needed": false,
+ "task_id": "uuid-here"
+})
+```
+
+**When to use:**
+- Hit a blocker (even if resolved)
+- Spent significant time debugging
+- Found a non-obvious solution
+- Need to request help (`help_needed: true`)
+
+---
+
+## When to Journal
+
+| Moment | Entry Type |
+|--------|------------|
+| Start a task | `roboco_journal_entry` (work_log) |
+| Make a decision | `roboco_journal_decision` |
+| Learn something new | `roboco_journal_learning` |
+| Hit a struggle | `roboco_journal_struggle` |
+| Complete a task | `roboco_journal_reflect` |
+| Make progress | `roboco_journal_entry` |
+
+---
+
+## Reading Journals
+
+### Search Your Own Journal
+
+```python
+roboco_journal_search("rate limiting redis") # Semantic search
+roboco_journal_recent(limit=10) # Recent entries
+roboco_journal_recent(entry_type="decision_log") # Filter by type
+roboco_journal_recent(task_id="uuid-here") # Filter by task
+roboco_journal_stats() # Your stats
+```
+
+### Read Team Journals (PM/Documenter only)
+
+```python
+roboco_journal_read_team(
+ target_agent="be-dev-1",
+ task_id="uuid-here", # Optional filter
+ limit=10
+)
+roboco_journal_scope() # See who you can read
+```
+
+---
+
+## Access Permissions
+
+| Your Role | Can Read Journals Of |
+|-----------|---------------------|
+| Developer | Own only |
+| QA | Own only |
+| Documenter | Own + cell members (for documentation) |
+| Cell PM | Own + cell members |
+| Main PM | Own + all Cell PMs |
+| Auditor | Everyone |
+
+---
+
+## Best Practices
+
+1. **Journal as you go** - Don't wait until end of task
+2. **Include task_id** - Links entries to work
+3. **Be specific** - Future you needs context
+4. **Record failures** - Struggles are valuable learning
+5. **Reflect honestly** - No one judges your struggles
+6. **Tag consistently** - Helps with search
diff --git a/docs/workflows/KNOWLEDGE_BASE.md b/docs/workflows/KNOWLEDGE_BASE.md
new file mode 100644
index 00000000..15f94002
--- /dev/null
+++ b/docs/workflows/KNOWLEDGE_BASE.md
@@ -0,0 +1,162 @@
+# Knowledge Base Guide
+
+## Overview
+
+The knowledge base is built from:
+- **Journals** - Your entries and team entries
+- **Task history** - Past tasks, decisions, outcomes
+- **Messages** - Channel discussions
+- **Documentation** - Produced docs
+
+All content is **embedded** (vectorized) for semantic search.
+
+---
+
+## Searching the Knowledge Base
+
+### Search Your Journal
+
+```python
+roboco_journal_search(
+ query="rate limiting redis implementation",
+ top_k=5 # Number of results
+)
+```
+
+Returns semantically similar entries - not just keyword matches.
+
+### Search Examples
+
+| Query | Finds |
+|-------|-------|
+| "how to handle auth tokens" | Past decisions about auth |
+| "redis connection issues" | Struggles with Redis |
+| "API versioning approach" | Decisions about API design |
+| "what did I learn about caching" | Learning entries about caching |
+
+---
+
+## Reading Past Work
+
+### Your Recent Entries
+
+```python
+roboco_journal_recent(limit=10)
+roboco_journal_recent(entry_type="decision_log")
+roboco_journal_recent(task_id="uuid-here")
+```
+
+### Your Stats
+
+```python
+roboco_journal_stats()
+# Returns: entries by type, growth metrics, top tags
+```
+
+### Team Journals (if you have access)
+
+```python
+roboco_journal_read_team(
+ target_agent="be-dev-1",
+ task_id="uuid-here", # Filter by task
+ entry_type="decision_log", # Filter by type
+ limit=10
+)
+```
+
+### Check Your Access Scope
+
+```python
+roboco_journal_scope()
+# Returns: your role, cell, who you can read
+```
+
+---
+
+## Before Starting a Task
+
+**Always search first:**
+
+```python
+# 1. Search for similar past work
+roboco_journal_search("implementing rate limiter")
+
+# 2. Check if someone documented this before
+roboco_journal_search("rate limit decisions")
+
+# 3. Look for learnings
+roboco_journal_search("rate limiting lessons learned")
+```
+
+This helps you:
+- Avoid repeating mistakes
+- Find proven patterns
+- Learn from others' experiences
+- Understand past decisions
+
+---
+
+## Contributing to Knowledge Base
+
+Everything you journal becomes searchable:
+
+| Entry Type | Searchable Content |
+|------------|-------------------|
+| Decision Log | Context, options, rationale |
+| Learning | What learned, how applied |
+| Struggle | Problem, solutions, resolution |
+| Reflection | What done, what learned, struggles |
+| General | Title, content, tags |
+
+**Pro tip:** Use descriptive titles and tags - they improve search relevance.
+
+---
+
+## Knowledge Flow
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ KNOWLEDGE FLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+ You Work You Journal Knowledge Base
+ │ │ │
+ │ Make decision │ │
+ └──────────────────────────►│ roboco_journal_decision │
+ └─────────────────────────────►│
+ │ Learn something │ │ Embedded
+ └──────────────────────────►│ roboco_journal_learning │ ▼
+ └─────────────────────────────►│ Searchable
+ │ Hit a struggle │ │
+ └──────────────────────────►│ roboco_journal_struggle │
+ └─────────────────────────────►│
+ │ Complete task │ │
+ └──────────────────────────►│ roboco_journal_reflect │
+ └─────────────────────────────►│
+ │
+ Future You ◄───────────────── roboco_journal_search ◄──────────┘
+ Future Agent ◄─────────────── roboco_journal_read_team ◄───────┘
+```
+
+---
+
+## Best Practices
+
+1. **Search before you start** - Learn from past work
+2. **Journal as you go** - Don't wait until end
+3. **Be specific** - Generic entries are less searchable
+4. **Use tags** - Helps categorization
+5. **Record failures** - They're valuable learning
+6. **Include context** - Future searchers need it
+
+---
+
+## Future: RAG Queries (Planned)
+
+Eventually you'll be able to:
+- Query across all knowledge (tasks, docs, code)
+- Get AI-synthesized answers
+- Find relevant code examples
+- Cross-reference decisions with outcomes
+
+For now, journal search is your primary tool.
diff --git a/docs/workflows/PERMISSIONS.md b/docs/workflows/PERMISSIONS.md
new file mode 100644
index 00000000..b1379161
--- /dev/null
+++ b/docs/workflows/PERMISSIONS.md
@@ -0,0 +1,194 @@
+# Permissions Matrix
+
+## Tool Permissions by Role
+
+### Task Management Tools
+
+| Tool | Main PM | Cell PM | Developer | QA | Documenter |
+|------|:-------:|:-------:|:---------:|:--:|:----------:|
+| `roboco_task_scan` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_get` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_claim` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_start` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_plan` | ✅ | ✅ | ✅ | ❌ | ❌ |
+| `roboco_task_progress` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_create` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_assign` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_activate` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_complete` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_cancel` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_pause` | ✅ | ✅ | ✅ | ❌ | ❌ |
+| `roboco_task_block` | ✅ | ✅ | ✅ | ❌ | ❌ |
+| `roboco_task_unblock` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_task_escalate` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_task_submit_verification` | ❌ | ❌ | ✅ | ❌ | ❌ |
+| `roboco_task_submit_qa` | ❌ | ❌ | ✅ | ❌ | ❌ |
+| `roboco_task_qa_pass` | ❌ | ❌ | ❌ | ✅ | ❌ |
+| `roboco_task_qa_fail` | ❌ | ❌ | ❌ | ✅ | ❌ |
+| `roboco_task_docs_complete` | ❌ | ❌ | ❌ | ❌ | ✅ |
+
+### Session Tools
+
+| Tool | Main PM | Cell PM | Developer | QA | Documenter |
+|------|:-------:|:-------:|:---------:|:--:|:----------:|
+| `roboco_session_create_for_tasks` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_session_link_task` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_session_get_for_task` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_group_create` | ✅ | ✅ | ❌ | ❌ | ❌ |
+
+### Communication Tools
+
+| Tool | Main PM | Cell PM | Developer | QA | Documenter |
+|------|:-------:|:-------:|:---------:|:--:|:----------:|
+| `roboco_message_send` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_channel_history` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_channel_list` | ✅ | ✅ | ✅ | ✅ | ✅ |
+
+### Notification Tools
+
+| Tool | Main PM | Cell PM | Developer | QA | Documenter |
+|------|:-------:|:-------:|:---------:|:--:|:----------:|
+| `roboco_notify_send` | ✅ | ✅ | ❌ | ❌ | ❌ |
+| `roboco_notify_list` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_notify_ack` | ✅ | ✅ | ✅ | ✅ | ✅ |
+
+### Journal Tools
+
+| Tool | Main PM | Cell PM | Developer | QA | Documenter |
+|------|:-------:|:-------:|:---------:|:--:|:----------:|
+| `roboco_journal_entry` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_reflect` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_decision` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_learning` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_struggle` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_search` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_recent` | ✅ | ✅ | ✅ | ✅ | ✅ |
+| `roboco_journal_read_team` | ✅ | ✅ | ❌ | ❌ | ✅ |
+
+---
+
+## Channel Access Permissions
+
+### Cell Channels
+
+| Channel | Read | Write | Silent |
+|---------|------|-------|--------|
+| `#backend-cell` | be-dev-1, be-dev-2, be-qa, be-pm, be-doc, main-pm | be-dev-1, be-dev-2, be-qa, be-pm, be-doc | auditor |
+| `#frontend-cell` | fe-dev-1, fe-dev-2, fe-qa, fe-pm, fe-doc, main-pm | fe-dev-1, fe-dev-2, fe-qa, fe-pm, fe-doc | auditor |
+| `#uxui-cell` | ux-dev, ux-qa, ux-pm, ux-doc, main-pm | ux-dev, ux-qa, ux-pm, ux-doc | auditor |
+
+### Cross-Cell Channels
+
+| Channel | Read | Write | Silent |
+|---------|------|-------|--------|
+| `#dev-all` | all devs, all PMs | all devs, all PMs | auditor |
+| `#qa-all` | all QA, all PMs | all QA, all PMs | auditor |
+| `#pm-all` | all PMs, main-pm | all PMs, main-pm | auditor |
+| `#doc-all` | all docs, all PMs | all docs, all PMs | auditor |
+
+### Management Channels
+
+| Channel | Read | Write | Silent |
+|---------|------|-------|--------|
+| `#main-pm-board` | main-pm, product-owner, head-marketing | main-pm, product-owner, head-marketing | auditor |
+| `#board-private` | product-owner, head-marketing, ceo | product-owner, head-marketing, ceo | auditor |
+
+### Special Channels
+
+| Channel | Read | Write | Silent |
+|---------|------|-------|--------|
+| `#announcements` | everyone | main-pm, board only | auditor |
+| `#all-hands` | everyone | everyone | auditor |
+
+---
+
+## Notification Permissions
+
+### Who Can Send Notifications
+
+| Role | Can Send | Scope |
+|------|:--------:|-------|
+| CEO | ✅ | Anyone |
+| Auditor | ✅ | Anyone |
+| Product Owner | ✅ | main-pm, head-marketing, auditor, ceo |
+| Head Marketing | ✅ | main-pm, product-owner, auditor, ceo |
+| Main PM | ✅ | Anyone |
+| Cell PM | ✅ | Own cell + other PMs |
+| Developer | ❌ | - |
+| QA | ❌ | - |
+| Documenter | ❌ | - |
+
+### Notification Types
+
+| Type | Sent By | To |
+|------|---------|-----|
+| `task_assignment` | PM | Specific agent |
+| `priority_change` | PM/Board | Affected agents |
+| `blocker_escalation` | PM | Main PM or other PM |
+| `review_request` | PM | QA or Auditor |
+| `documentation_request` | PM | Documenter |
+| `alert` | Board/Auditor | Anyone |
+| `broadcast` | Board/Main PM | Groups |
+
+---
+
+## Task Action Permissions
+
+### Who Can Perform What Action
+
+| Action | Owner | Same-Cell PM | Main PM | Board |
+|--------|:-----:|:------------:|:-------:|:-----:|
+| Claim | ✅ | ✅ | ✅ | ✅ |
+| Start | ✅ | ❌ | ❌ | ❌ |
+| Plan | ✅ | ❌ | ❌ | ❌ |
+| Progress | ✅ | ❌ | ❌ | ❌ |
+| Block | ✅ | ✅ | ✅ | ❌ |
+| Unblock | ✅ | ✅ | ✅ | ❌ |
+| Pause | ✅ | ✅ | ✅ | ❌ |
+| Resume | ✅ | ❌ | ❌ | ❌ |
+| Complete | ❌ | ✅ | ✅ | ✅ |
+| Cancel | ❌ | ✅ | ✅ | ✅ |
+
+### Claim Restrictions by Role
+
+| Role | Can Claim From Status |
+|------|----------------------|
+| Developer | `pending`, `needs_revision` |
+| QA | `awaiting_qa` |
+| Documenter | `pending`, `awaiting_documentation` |
+| Cell PM | `pending`, `backlog` |
+| Main PM | `pending`, `backlog` |
+
+---
+
+## Self-Review Prevention
+
+| Action | Prevention Rule |
+|--------|-----------------|
+| QA Pass/Fail | Cannot review task where you were the original developer |
+| Docs Complete | Cannot document task where you were the original developer |
+
+The system stores `original_developer` in `quick_context` when:
+- Developer submits for QA → QA claims → `original_developer` preserved
+- QA passes → Documenter claims → `original_developer` still preserved
+
+---
+
+## Team-Based Restrictions
+
+| Agent | Can Only Work On Tasks With Team |
+|-------|----------------------------------|
+| be-dev-1, be-dev-2 | `backend` |
+| be-qa | `backend` |
+| be-pm | `backend` |
+| be-doc | `backend` |
+| fe-dev-1, fe-dev-2 | `frontend` |
+| fe-qa | `frontend` |
+| fe-pm | `frontend` |
+| fe-doc | `frontend` |
+| ux-dev | `ux_ui` |
+| ux-qa | `ux_ui` |
+| ux-pm | `ux_ui` |
+| ux-doc | `ux_ui` |
+| main-pm | Any |
+| Board | Any |
diff --git a/docs/workflows/PM.md b/docs/workflows/PM.md
new file mode 100644
index 00000000..02cc1e32
--- /dev/null
+++ b/docs/workflows/PM.md
@@ -0,0 +1,198 @@
+# PM Workflow
+
+## Main PM
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ MAIN PM WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+1. RECEIVE WORK (from Board/CEO)
+ │
+ ▼
+2. TRIAGE
+ ├── roboco_task_get(task_id) → Read requirements
+ ├── roboco_task_claim(task_id) → Take ownership
+ └── roboco_task_start(task_id) → Begin triage work
+ │
+ ▼
+3. PLAN & BREAKDOWN
+ ├── roboco_task_plan(task_id, approach, steps)
+ ├── Identify which cells need subtasks
+ └── roboco_task_progress(task_id, "Planning complete", 20)
+ │
+ ▼
+4. CREATE SUBTASKS (for Cell PMs)
+ │
+ │ For EACH cell subtask:
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ roboco_task_create({ │
+ │ │ title: "...", │
+ │ │ description: "...", │
+ │ │ team: "backend" | "frontend" | "ux_ui", │
+ │ │ parent_task_id: main_task_id, │
+ │ │ status: "backlog", ← STARTS IN BACKLOG │
+ │ │ assigned_to: "be-pm" ← ASSIGN TO CELL PM │
+ │ │ }) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+5. CREATE SESSION (groups related subtasks)
+ │
+ │ roboco_session_create_for_tasks({
+ │ title: "Feature X Implementation",
+ │ task_ids: [subtask_1_id, subtask_2_id, ...]
+ │ })
+ │
+ ▼
+6. ACTIVATE SUBTASKS
+ │
+ │ For EACH subtask:
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ roboco_task_activate(subtask_id) │
+ │ │ │
+ │ │ STATUS: backlog → pending │
+ │ │ Now visible to Cell PM in roboco_task_scan() │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+7. NOTIFY CELL PMs
+ │
+ │ roboco_notify_send({
+ │ recipient: "be-pm",
+ │ type: "task_assignment",
+ │ task_id: subtask_id,
+ │ message: "New task assigned to your cell"
+ │ })
+ │
+ ▼
+8. MONITOR & COORDINATE
+ │
+ │ Loop:
+ │ ├── roboco_task_scan() → Check subtask statuses
+ │ ├── roboco_channel_history("pm-all") → Cross-cell coordination
+ │ ├── Handle escalations from Cell PMs
+ │ └── roboco_task_progress(main_task_id, "X% complete", %)
+ │
+ ▼
+9. COMPLETE (when all subtasks done)
+ │
+ │ roboco_task_complete(main_task_id)
+ │
+ ▼
+ DONE
+```
+
+## Cell PM (be-pm, fe-pm, ux-pm)
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ CELL PM WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN FOR WORK
+ │
+ │ roboco_task_scan(team="backend")
+ │
+ │ Look for:
+ │ ├── Tasks in "pending" assigned to me
+ │ ├── Tasks in "awaiting_pm_review" (need my approval)
+ │ └── Escalations from my cell
+ │
+ ▼
+2. CLAIM TASK
+ │
+ │ roboco_task_claim(task_id)
+ │
+ │ STATUS: pending → claimed
+ │ ASSIGNED_TO: confirmed as me
+ │
+ ▼
+3. START & PLAN
+ │
+ │ roboco_task_start(task_id)
+ │ STATUS: claimed → in_progress
+ │
+ │ roboco_task_plan(task_id, approach, steps)
+ │
+ ▼
+4. CREATE DEV SUBTASKS
+ │
+ │ For EACH dev subtask:
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ roboco_task_create({ │
+ │ │ title: "...", │
+ │ │ description: "...", │
+ │ │ team: "backend", │
+ │ │ parent_task_id: my_task_id, │
+ │ │ status: "backlog", ← STARTS IN BACKLOG │
+ │ │ assigned_to: "be-dev-1" ← OPTIONAL: pre-assign │
+ │ │ }) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+5. ACTIVATE SUBTASKS
+ │
+ │ roboco_task_activate(subtask_id)
+ │
+ │ STATUS: backlog → pending
+ │ Subtask inherits parent's session automatically
+ │
+ ▼
+6. NOTIFY DEVELOPERS
+ │
+ │ roboco_notify_send({
+ │ recipient: "be-dev-1",
+ │ type: "task_assignment",
+ │ task_id: subtask_id,
+ │ message: "Task ready for you"
+ │ })
+ │
+ ▼
+7. MONITOR CELL WORK
+ │
+ │ Loop:
+ │ ├── roboco_task_scan(team="backend")
+ │ ├── Watch for "awaiting_pm_review" tasks
+ │ ├── Handle blockers/escalations
+ │ └── roboco_task_progress(my_task_id, "X% complete", %)
+ │
+ ▼
+8. COMPLETE SUBTASKS (after QA + Docs)
+ │
+ │ When subtask reaches "awaiting_pm_review":
+ │ ├── Review the work
+ │ └── roboco_task_complete(subtask_id)
+ │
+ ▼
+9. COMPLETE MY TASK (when all subtasks done)
+ │
+ │ roboco_task_complete(my_task_id)
+ │
+ ▼
+ DONE → Main PM notified
+```
+
+## Task Status Transitions (PM perspective)
+
+```
+PM CREATES:
+ backlog ──activate──► pending
+
+DEVELOPER CLAIMS:
+ pending ──claim──► claimed
+
+DEVELOPER WORKS:
+ claimed ──start──► in_progress
+
+AFTER QA + DOCS:
+ awaiting_pm_review ──PM completes──► completed
+```
+
+## Key Rules
+
+1. **Tasks start in BACKLOG** - PM setup phase
+2. **ACTIVATE before anyone can claim** - backlog → pending
+3. **Sessions group related tasks** - create before activating
+4. **Subtasks inherit parent session** - no need to create new session
+5. **Only PM can COMPLETE** - after full workflow (dev → QA → docs → PM review)
diff --git a/docs/workflows/QA.md b/docs/workflows/QA.md
new file mode 100644
index 00000000..29196c6d
--- /dev/null
+++ b/docs/workflows/QA.md
@@ -0,0 +1,120 @@
+# QA Workflow
+
+## Overview
+
+QA agents (be-qa, fe-qa, ux-qa) verify developer work meets acceptance criteria.
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ QA WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────┘
+
+1. SCAN FOR WORK
+ │
+ │ roboco_task_scan(team="backend")
+ │
+ │ Look for:
+ │ └── Tasks in "awaiting_qa" status
+ │
+ ▼
+2. CLAIM TASK
+ │
+ │ roboco_task_claim(task_id)
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ QA can ONLY claim tasks in "awaiting_qa" │
+ │ │ │
+ │ │ BEFORE: │
+ │ │ status: awaiting_qa │
+ │ │ assigned_to: original_developer │
+ │ │ │
+ │ │ AFTER: │
+ │ │ status: claimed │
+ │ │ assigned_to: qa_agent │
+ │ │ (original_developer stored in quick_context) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+3. START REVIEW
+ │
+ │ roboco_task_start(task_id)
+ │
+ │ STATUS: claimed → in_progress
+ │
+ ▼
+4. REVIEW WORK
+ │
+ │ ┌─────────────────────────────────────────────────────────────────┐
+ │ │ Review checklist: │
+ │ │ ├── Read developer's handoff notes │
+ │ │ ├── Check acceptance criteria │
+ │ │ ├── Run tests │
+ │ │ ├── Verify functionality │
+ │ │ └── Check code quality │
+ │ │ │
+ │ │ roboco_task_progress(task_id, "Reviewing X", 50) │
+ │ │ roboco_journal_entry({type: "qa_review", ...}) │
+ │ └─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+5. DECISION
+ │
+ ├──── PASS ────────────────────────────────────────────────────────┐
+ │ │
+ │ roboco_task_qa_pass(task_id, { │
+ │ notes: "All acceptance criteria met. Tests pass." │
+ │ }) │
+ │ │
+ │ STATUS: in_progress → awaiting_documentation │
+ │ → Documenter takes over │
+ │ │
+ └──── FAIL ────────────────────────────────────────────────────────┐
+ │
+ roboco_task_qa_fail(task_id, { │
+ notes: "Issues found", │
+ issues: [ │
+ "Bug: X doesn't work", │
+ "Missing: Y not implemented" │
+ ] │
+ }) │
+ │
+ STATUS: in_progress → needs_revision │
+ ASSIGNED_TO: back to original_developer │
+ → Developer fixes and resubmits │
+ │
+ ▼
+ DONE (for QA)
+```
+
+## Self-Review Prevention
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ QA CANNOT review tasks they originally developed │
+│ │
+│ System tracks original_developer in quick_context │
+│ If QA agent == original_developer → FORBIDDEN │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+## Status Transitions (QA perspective)
+
+```
+CAN CLAIM:
+ awaiting_qa ──claim──► claimed
+
+AFTER CLAIM:
+ claimed ──start──► in_progress
+
+DECISIONS:
+ in_progress ──qa_pass──► awaiting_documentation
+ in_progress ──qa_fail──► needs_revision
+```
+
+## Key Rules
+
+1. **Only claim awaiting_qa** - Can't claim pending tasks
+2. **Cannot self-review** - Can't QA your own dev work
+3. **Thorough notes** - Document what was tested and why
+4. **Clear fail reasons** - Developer needs to know what to fix
+5. **Cannot COMPLETE** - Only PM completes after docs
diff --git a/docs/workflows/README.md b/docs/workflows/README.md
new file mode 100644
index 00000000..f7f42626
--- /dev/null
+++ b/docs/workflows/README.md
@@ -0,0 +1,185 @@
+# Workflow Documentation
+
+## Quick Start
+
+| I am a... | Start here |
+|-----------|------------|
+| Developer | [DEVELOPER.md](./DEVELOPER.md) → [AGENT_CHEATSHEET.md](./AGENT_CHEATSHEET.md) |
+| QA | [QA.md](./QA.md) → [AGENT_CHEATSHEET.md](./AGENT_CHEATSHEET.md) |
+| Documenter | [DOCUMENTER.md](./DOCUMENTER.md) → [AGENT_CHEATSHEET.md](./AGENT_CHEATSHEET.md) |
+| Cell PM | [PM.md](./PM.md) → [PERMISSIONS.md](./PERMISSIONS.md) |
+| Main PM | [PM.md](./PM.md) → [PERMISSIONS.md](./PERMISSIONS.md) |
+
+---
+
+## Documentation Index
+
+### Core Workflows
+
+| Document | Description |
+|----------|-------------|
+| [STATUS_TRANSITIONS.md](./STATUS_TRANSITIONS.md) | Complete task lifecycle diagram |
+| [PM.md](./PM.md) | Main PM and Cell PM workflows |
+| [DEVELOPER.md](./DEVELOPER.md) | Developer workflow |
+| [QA.md](./QA.md) | QA workflow |
+| [DOCUMENTER.md](./DOCUMENTER.md) | Documenter workflow |
+
+### Reference
+
+| Document | Description |
+|----------|-------------|
+| [PERMISSIONS.md](./PERMISSIONS.md) | Tool, channel, notification permissions |
+| [AGENT_CHEATSHEET.md](./AGENT_CHEATSHEET.md) | Quick reference per role |
+
+### Activities
+
+| Document | Description |
+|----------|-------------|
+| [JOURNALING.md](./JOURNALING.md) | How to journal effectively |
+| [COMMUNICATION.md](./COMMUNICATION.md) | Messages and channels |
+| [ESCALATION.md](./ESCALATION.md) | When and how to escalate |
+| [KNOWLEDGE_BASE.md](./KNOWLEDGE_BASE.md) | Searching past work |
+| [GIT_WORKFLOW.md](./GIT_WORKFLOW.md) | Git conventions (future) |
+
+### Bug Tracking
+
+| Document | Description |
+|----------|-------------|
+| [BUGS.md](./BUGS.md) | Known issues and fixes |
+
+---
+
+## The Big Picture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ ROBOCO WORKFLOW │
+└─────────────────────────────────────────────────────────────────────────────┘
+
+ BOARD/CEO
+ │
+ │ Creates initiative
+ ▼
+ MAIN PM
+ │
+ ┌───────────────┼───────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ BE-PM FE-PM UX-PM
+ │ │ │
+ ┌──────────┼──────────┐ │ ┌──────────┼──────────┐
+ │ │ │ │ │ │ │
+ ▼ ▼ ▼ │ ▼ ▼ ▼
+ BE-DEV-1 BE-DEV-2 BE-QA │ FE-DEV-1 FE-DEV-2 FE-QA
+ │ │ │ │ │ │ │
+ └────┬─────┘ │ │ └────┬─────┘ │
+ │ │ │ │ │
+ ▼ ▼ │ ▼ ▼
+ SUBMITS TO QA ───► REVIEWS │ SUBMITS TO QA ───► REVIEWS
+ │ │ │ │ │
+ ▼ ▼ │ ▼ ▼
+ BE-DOC ◄───── QA PASSES │ FE-DOC ◄───── QA PASSES
+ │ │ │
+ ▼ │ ▼
+ AWAITING_PM_REVIEW ◄───────┼─── AWAITING_PM_REVIEW
+ │ │ │
+ └─────────────────────┴─────────┘
+ │
+ ▼
+ COMPLETED
+```
+
+---
+
+## Task Lifecycle Summary
+
+```
+BACKLOG → PENDING → CLAIMED → IN_PROGRESS → VERIFYING → AWAITING_QA
+ │
+ ┌─────────────────────────┴─────────────────────────┐
+ │ │
+ QA PASSES QA FAILS
+ │ │
+ ▼ ▼
+ AWAITING_DOCUMENTATION NEEDS_REVISION
+ │ │
+ DOCS COMPLETE (back to dev)
+ │
+ ▼
+ AWAITING_PM_REVIEW
+ │
+ PM COMPLETES
+ │
+ ▼
+ COMPLETED
+```
+
+---
+
+## Key Principles
+
+1. **Everything is a task** - All work is tracked
+2. **Tasks start in BACKLOG** - PM setup phase
+3. **ACTIVATE before claim** - Makes task visible to workers
+4. **CLAIM before work** - Takes ownership
+5. **PLAN before START** - Required planning step
+6. **PROGRESS updates** - Keep PM informed
+7. **SELF-VERIFY first** - Check your work before QA
+8. **JOURNAL as you go** - Document decisions, learnings, struggles
+9. **No self-review** - QA/Docs can't review own work
+10. **Only PM completes** - After full workflow
+
+---
+
+## Common Patterns
+
+### Starting Work
+
+```python
+# 1. Check notifications
+roboco_notify_list()
+roboco_notify_ack(notification_id)
+
+# 2. Scan for tasks
+roboco_task_scan(team="backend")
+
+# 3. Search knowledge base
+roboco_journal_search("similar work")
+
+# 4. Claim and plan
+roboco_task_claim(task_id)
+roboco_task_plan(task_id, approach, steps)
+
+# 5. Start
+roboco_task_start(task_id)
+```
+
+### While Working
+
+```python
+# Progress updates
+roboco_task_progress(task_id, "Completed X", 50)
+
+# Journaling
+roboco_journal_decision({...})
+roboco_journal_learning({...})
+
+# Communication
+roboco_message_send({channel: "backend-cell", ...})
+
+# If stuck
+roboco_task_escalate(task_id, "Need help with X")
+```
+
+### Finishing
+
+```python
+# Self-verify
+roboco_task_submit_verification(task_id)
+
+# Submit for QA
+roboco_task_submit_qa(task_id, notes)
+
+# Reflect
+roboco_journal_reflect({...})
+```
diff --git a/docs/workflows/STATUS_TRANSITIONS.md b/docs/workflows/STATUS_TRANSITIONS.md
new file mode 100644
index 00000000..c2487ace
--- /dev/null
+++ b/docs/workflows/STATUS_TRANSITIONS.md
@@ -0,0 +1,150 @@
+# Task Status Transitions
+
+## Complete Lifecycle
+
+```
+┌─────────────────────────────────────────────────────────────────────────────────┐
+│ FULL TASK LIFECYCLE │
+└─────────────────────────────────────────────────────────────────────────────────┘
+
+ PM CREATES
+ │
+ ▼
+ ┌──────────┐
+ │ BACKLOG │ ◄─── PM setup phase
+ └────┬─────┘ (create session, plan)
+ │
+ PM ACTIVATES
+ │
+ ▼
+ ┌──────────┐
+ ┌──────│ PENDING │◄─────────────────────────────────┐
+ │ └────┬─────┘ │
+ │ │ │
+ DEV or QA DEV or PM │
+ CLAIMS ASSIGNS │
+ │ │ │
+ ▼ ▼ │
+ ┌──────────┐ │
+ │ CLAIMED │ ◄─── Agent owns it │
+ └────┬─────┘ │
+ │ │
+ DEV STARTS │
+ │ │
+ ▼ │
+ ┌─────────────┐ │
+ ┌──────────│ IN_PROGRESS │──────────┐ │
+ │ └──────┬──────┘ │ │
+ │ │ │ │
+ BLOCKED PAUSED WORKING │
+ │ │ │ │
+ ▼ ▼ │ │
+ ┌──────────┐ ┌─────────┐ │ │
+ │ BLOCKED │ │ PAUSED │ │ │
+ └────┬─────┘ └────┬────┘ │ │
+ │ │ │ │
+ UNBLOCK RESUME │ │
+ │ │ │ │
+ └────────►───────►└─────────►───────┘ │
+ │ │
+ DEV VERIFIES │
+ │ │
+ ▼ │
+ ┌───────────┐ │
+ │ VERIFYING │ ◄─── Self-check │
+ └─────┬─────┘ │
+ │ │
+ DEV SUBMITS QA │
+ │ │
+ ▼ │
+ ┌─────────────┐ │
+ │ AWAITING_QA │ ◄─── QA picks up │
+ └──────┬──────┘ │
+ │ │
+ ┌───────────┴───────────┐ │
+ │ │ │
+ QA PASS QA FAIL │
+ │ │ │
+ ▼ ▼ │
+ ┌───────────────────────────┐ ┌─────────────────┐ │
+ │ AWAITING_DOCUMENTATION │ │ NEEDS_REVISION │───────────────────┘
+ └───────────┬───────────────┘ └─────────────────┘
+ │ (back to dev)
+ DOCS COMPLETE
+ │
+ ▼
+ ┌────────────────────┐
+ │ AWAITING_PM_REVIEW │ ◄─── PM final review
+ └─────────┬──────────┘
+ │
+ PM COMPLETES
+ │
+ ▼
+ ┌───────────┐
+ │ COMPLETED │
+ └───────────┘
+```
+
+## Status Definitions
+
+| Status | Description | Who Owns It |
+|--------|-------------|-------------|
+| `backlog` | PM is setting up the task | PM |
+| `pending` | Ready for someone to claim | Unassigned or pre-assigned |
+| `claimed` | Agent has taken ownership | Developer/QA/Documenter |
+| `in_progress` | Active work happening | Developer/QA/Documenter |
+| `blocked` | Waiting on another task | Developer |
+| `paused` | Temporarily stopped | Developer |
+| `verifying` | Developer self-checking | Developer |
+| `awaiting_qa` | Ready for QA review | QA |
+| `needs_revision` | QA found issues | Developer |
+| `awaiting_documentation` | QA passed, needs docs | Documenter |
+| `awaiting_pm_review` | Docs done, PM reviews | PM |
+| `completed` | Done | - |
+| `cancelled` | Cancelled | - |
+
+## Transition Rules
+
+### Who Can Trigger What
+
+| Transition | Triggered By | Tool |
+|------------|--------------|------|
+| backlog → pending | PM | `roboco_task_activate()` |
+| pending → claimed | Any agent | `roboco_task_claim()` |
+| claimed → in_progress | Owner | `roboco_task_start()` |
+| in_progress → blocked | Owner | `roboco_task_block()` |
+| in_progress → paused | Owner | `roboco_task_pause()` |
+| blocked → in_progress | Owner or PM | `roboco_task_unblock()` |
+| paused → in_progress | Owner | `roboco_task_start()` (resume) |
+| in_progress → verifying | Developer | `roboco_task_submit_verification()` |
+| verifying → awaiting_qa | Developer | `roboco_task_submit_qa()` |
+| awaiting_qa → claimed | QA | `roboco_task_claim()` |
+| awaiting_qa → awaiting_documentation | QA | `roboco_task_qa_pass()` |
+| awaiting_qa → needs_revision | QA | `roboco_task_qa_fail()` |
+| needs_revision → claimed | Developer | `roboco_task_claim()` |
+| awaiting_documentation → claimed | Documenter | `roboco_task_claim()` |
+| awaiting_documentation → awaiting_pm_review | Documenter | `roboco_task_docs_complete()` |
+| awaiting_pm_review → completed | PM | `roboco_task_complete()` |
+| any → cancelled | PM | `roboco_task_cancel()` |
+
+## What Each Role Can Claim
+
+| Role | Can Claim From |
+|------|----------------|
+| Developer | `pending`, `needs_revision` |
+| QA | `awaiting_qa` |
+| Documenter | `pending`, `awaiting_documentation` |
+| PM | `pending`, `backlog` |
+
+## Blocking Rules
+
+An agent **CANNOT claim a new task** if they have:
+- A task in `in_progress`
+- A task in `claimed` (should start it first)
+- A task in `verifying` (should submit to QA first)
+
+An agent **CAN claim** even if they have:
+- A task in `paused` (can work on something else while waiting)
+- A task in `blocked` (can work on something else while waiting)
+
+**Exception:** If claiming a task already assigned to them (PM pre-assigned), the blocking check is skipped for THAT specific task.
diff --git a/roboco/agents/__init__.py b/roboco/agents/__init__.py
new file mode 100644
index 00000000..45b364f6
--- /dev/null
+++ b/roboco/agents/__init__.py
@@ -0,0 +1,117 @@
+"""
+RoboCo Agent Framework
+
+Base classes, role-specific agents, and orchestration.
+Phase 4: All 17 agent types implemented.
+"""
+
+from roboco.agents.base import (
+ Agent,
+ AgentConfig,
+ AgentState,
+ set_reasoning_stream_callback,
+)
+
+# Agent implementations
+from roboco.agents.board import AuditorAgent, HeadMarketingAgent, ProductOwnerAgent
+from roboco.agents.developer import DeveloperAgent
+from roboco.agents.documenter import DocumenterAgent
+
+# Factory functions (from new factories/ module)
+from roboco.agents.factories import (
+ Board,
+ Cell,
+ Organization,
+ create_auditor,
+ create_backend_cell,
+ create_backend_developer,
+ create_backend_documenter,
+ create_backend_pm,
+ create_backend_qa,
+ create_board,
+ create_frontend_cell,
+ create_frontend_developer,
+ create_frontend_documenter,
+ create_frontend_pm,
+ create_frontend_qa,
+ create_head_marketing,
+ create_main_pm,
+ create_organization,
+ create_product_owner,
+ create_ux_cell,
+ create_ux_developer,
+ create_ux_documenter,
+ create_ux_pm,
+ create_ux_qa,
+ get_agent_roster,
+ print_org_chart,
+)
+
+# Mixins for building agents
+from roboco.agents.mixins import (
+ BaseContext,
+ ContextManager,
+ CyclicPhaseConfig,
+ CyclicPhaseRunner,
+ PhaseConfig,
+ PhaseEngine,
+ PhaseResult,
+ ProgressTracker,
+ WorkFinder,
+ WorkSearchStrategy,
+)
+from roboco.agents.orchestrator import Orchestrator
+from roboco.agents.pm import CellPMAgent, MainPMAgent
+from roboco.agents.qa import QAAgent
+
+__all__ = [
+ "Agent",
+ "AgentConfig",
+ "AgentState",
+ "AuditorAgent",
+ "BaseContext",
+ "Board",
+ "Cell",
+ "CellPMAgent",
+ "ContextManager",
+ "CyclicPhaseConfig",
+ "CyclicPhaseRunner",
+ "DeveloperAgent",
+ "DocumenterAgent",
+ "HeadMarketingAgent",
+ "MainPMAgent",
+ "Orchestrator",
+ "Organization",
+ "PhaseConfig",
+ "PhaseEngine",
+ "PhaseResult",
+ "ProductOwnerAgent",
+ "ProgressTracker",
+ "QAAgent",
+ "WorkFinder",
+ "WorkSearchStrategy",
+ "create_auditor",
+ "create_backend_cell",
+ "create_backend_developer",
+ "create_backend_documenter",
+ "create_backend_pm",
+ "create_backend_qa",
+ "create_board",
+ "create_frontend_cell",
+ "create_frontend_developer",
+ "create_frontend_documenter",
+ "create_frontend_pm",
+ "create_frontend_qa",
+ "create_head_marketing",
+ "create_main_pm",
+ "create_organization",
+ "create_product_owner",
+ "create_ux_cell",
+ "create_ux_developer",
+ "create_ux_documenter",
+ "create_ux_pm",
+ "create_ux_qa",
+ "get_agent_roster",
+ "print_org_chart",
+ "set_reasoning_stream_callback",
+]
diff --git a/roboco/agents/base.py b/roboco/agents/base.py
new file mode 100644
index 00000000..21cf50a1
--- /dev/null
+++ b/roboco/agents/base.py
@@ -0,0 +1,1017 @@
+"""
+Agent Base Class
+
+The foundation for all AI agents in the RoboCo system.
+Each agent follows the universal task lifecycle and communicates
+through the Messaging API.
+"""
+
+import asyncio
+import contextlib
+from abc import ABC, abstractmethod
+from collections.abc import Awaitable, Callable
+from datetime import UTC, datetime
+from typing import TYPE_CHECKING, Any
+from uuid import UUID
+
+import httpx
+import structlog
+from anthropic import AsyncAnthropic
+
+if TYPE_CHECKING:
+ from anthropic.types import MessageParam
+
+from roboco.config import settings
+from roboco.llm import ToonAdapter
+from roboco.models import AgentRole, AgentStatus, TaskStatus, Team
+from roboco.models.agents import AgentConfig, AgentState
+
+# Type for reasoning stream callback (injected to avoid API layer coupling)
+ReasoningStreamCallback = Callable[[UUID, str], Awaitable[None]]
+
+
+class _ReasoningStreamHolder:
+ """Holder for reasoning stream callback singleton."""
+
+ callback: ReasoningStreamCallback | None = None
+
+
+def set_reasoning_stream_callback(callback: ReasoningStreamCallback | None) -> None:
+ """
+ Set the callback for streaming agent reasoning.
+
+ This decouples the agent layer from the API/WebSocket layer.
+ Set to None to disable reasoning streaming.
+ """
+ _ReasoningStreamHolder.callback = callback
+
+
+def get_reasoning_stream_callback() -> ReasoningStreamCallback | None:
+ """Get the current reasoning stream callback."""
+ return _ReasoningStreamHolder.callback
+
+
+logger = structlog.get_logger()
+
+
+# =============================================================================
+# BASE AGENT CLASS
+# =============================================================================
+
+
+class Agent(ABC):
+ """
+ Base class for all RoboCo agents.
+
+ Agents are autonomous AI workers that:
+ - Follow the universal task lifecycle
+ - Communicate through channels
+ - Stream their reasoning to observers
+ - Maintain journals for reflection
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """
+ Initialize an agent.
+
+ Args:
+ config: Agent configuration
+ """
+ self.config = config
+ self.state = AgentState()
+ self._running = False
+ self._task: asyncio.Task | None = None
+ self._llm_client: AsyncAnthropic | None = None
+ self._toon = ToonAdapter()
+
+ self.log = logger.bind(
+ agent_id=str(config.id),
+ agent_name=config.name,
+ agent_role=config.role.value,
+ )
+
+ @property
+ def id(self) -> UUID:
+ """Agent's unique identifier."""
+ return self.config.id
+
+ @property
+ def name(self) -> str:
+ """Agent's display name."""
+ return self.config.name
+
+ @property
+ def role(self) -> AgentRole:
+ """Agent's role in the organization."""
+ return self.config.role
+
+ @property
+ def team(self) -> Team | None:
+ """Agent's team affiliation."""
+ return self.config.team
+
+ @property
+ def is_running(self) -> bool:
+ """Check if agent is currently running."""
+ return self._running
+
+ @property
+ def is_idle(self) -> bool:
+ """Check if agent is idle (running but no task)."""
+ return self._running and self.state.current_task_id is None
+
+ @property
+ def llm_client(self) -> "AsyncAnthropic":
+ """Get or create the LLM client."""
+ if self._llm_client is None:
+ self._llm_client = AsyncAnthropic(api_key=settings.anthropic_api_key)
+ return self._llm_client
+
+ # =========================================================================
+ # TOON SERIALIZATION (for token-efficient LLM communication)
+ # =========================================================================
+
+ def format_context(self, data: dict[str, Any]) -> str:
+ """
+ Format context data for LLM using TOON.
+
+ TOON (Token-Oriented Object Notation) reduces token consumption
+ by 30-60% compared to JSON while maintaining semantic clarity.
+
+ Args:
+ data: Dictionary to encode for LLM prompt.
+
+ Returns:
+ TOON-formatted string.
+ """
+ return self._toon.encode(data)
+
+ def format_context_labeled(self, label: str, data: dict[str, Any]) -> str:
+ """
+ Format labeled context data for embedding in prompts.
+
+ Args:
+ label: Section label (e.g., "Task Context").
+ data: Dictionary to encode.
+
+ Returns:
+ Labeled TOON-formatted string.
+ """
+ return self._toon.format_for_prompt(label, data)
+
+ def parse_llm_response(self, response: str) -> dict[str, Any] | list[Any]:
+ """
+ Parse structured data from LLM response.
+
+ Attempts TOON parsing first, falls back to JSON.
+
+ Args:
+ response: Raw LLM response text.
+
+ Returns:
+ Parsed Python dict or list.
+ """
+ return self._toon.decode(response)
+
+ # =========================================================================
+ # LIFECYCLE METHODS
+ # =========================================================================
+
+ async def start(self) -> None:
+ """
+ Start the agent.
+
+ Initializes connections and begins the main loop.
+ """
+ if self._running:
+ self.log.warning("Agent already running")
+ return
+
+ self.log.info("Starting agent")
+ self._running = True
+ self.state.status = AgentStatus.IDLE
+ self.state.last_activity = datetime.now(UTC)
+
+ # Initialize connections
+ await self._initialize()
+
+ # Start main loop
+ self._task = asyncio.create_task(self._run_loop())
+
+ async def stop(self) -> None:
+ """
+ Stop the agent gracefully.
+
+ Saves state and closes connections.
+ """
+ if not self._running:
+ return
+
+ self.log.info("Stopping agent")
+ self._running = False
+
+ # Cancel main loop
+ if self._task:
+ self._task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await self._task
+
+ # Save state and cleanup
+ await self._cleanup()
+
+ self.state.status = AgentStatus.OFFLINE
+ self.log.info("Agent stopped")
+
+ @abstractmethod
+ async def _initialize(self) -> None:
+ """Initialize agent resources. Override in subclasses."""
+
+ @abstractmethod
+ async def _cleanup(self) -> None:
+ """Cleanup agent resources. Override in subclasses."""
+
+ # =========================================================================
+ # MAIN LOOP
+ # =========================================================================
+
+ async def _run_loop(self) -> None:
+ """
+ Main agent loop.
+
+ Continuously scans for work and processes tasks.
+ """
+ while self._running:
+ try:
+ if self.state.current_task_id:
+ # Continue working on current task
+ await self._work_on_task()
+ else:
+ # Scan for new work
+ await self._scan_for_work()
+
+ # Brief pause to prevent tight loop
+ await asyncio.sleep(1)
+
+ except asyncio.CancelledError:
+ break
+ except Exception as e:
+ self.log.error("Error in agent loop", error=str(e))
+ self.state.error = str(e)
+ await asyncio.sleep(5) # Back off on error
+
+ async def _scan_for_work(self) -> None:
+ """
+ Scan for available work.
+
+ Checks for:
+ 1. Own interrupted/paused tasks (priority)
+ 2. Assigned tasks
+ 3. Available tasks in queue
+ """
+ self.state.status = AgentStatus.IDLE
+
+ # Look for work (implemented by subclasses)
+ task_id = await self.find_work()
+
+ if task_id:
+ self.state.current_task_id = task_id
+ self.state.status = AgentStatus.ACTIVE
+ self.log.info("Found work", task_id=str(task_id))
+
+ async def _work_on_task(self) -> None:
+ """
+ Work on the current task.
+
+ Follows the task lifecycle:
+ CLAIM → UNDERSTAND → PLAN → EXECUTE → VERIFY → NOTES → CLOSE
+ """
+ self.state.status = AgentStatus.ACTIVE
+ self.state.last_activity = datetime.now(UTC)
+
+ try:
+ # Execute the task (implemented by subclasses)
+ if self.state.current_task_id is None:
+ self.log.warning("No current task to execute")
+ return
+ completed = await self.execute_task(self.state.current_task_id)
+
+ if completed:
+ self.state.tasks_completed += 1
+ self.state.current_task_id = None
+ self.log.info("Task completed")
+
+ except Exception as e:
+ self.log.error("Error executing task", error=str(e))
+ self.state.error = str(e)
+ # Don't clear task - allow retry or manual intervention
+
+ # =========================================================================
+ # ABSTRACT METHODS (Implement in subclasses)
+ # =========================================================================
+
+ @abstractmethod
+ async def find_work(self) -> UUID | None:
+ """
+ Find available work for this agent.
+
+ Returns:
+ Task ID if work found, None otherwise.
+ """
+ pass
+
+ @abstractmethod
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute a task.
+
+ Args:
+ task_id: ID of the task to execute
+
+ Returns:
+ True if task completed successfully, False otherwise.
+ """
+ pass
+
+ # =========================================================================
+ # COMMUNICATION METHODS
+ # =========================================================================
+
+ async def send_message(
+ self,
+ session_id: UUID | None,
+ content: str,
+ message_type: str = "dialogue",
+ task_id: UUID | None = None,
+ ) -> None:
+ """
+ Send a message to a session.
+
+ Args:
+ session_id: Target session (from ctx.session_id)
+ content: Message content
+ message_type: Type of message (reasoning, dialogue, action, etc.)
+ task_id: Optional task context for the message
+ """
+ if session_id is None:
+ # No session - escalate to PM
+ self.log.warning(
+ "Cannot send message: no session_id provided",
+ task_id=str(task_id) if task_id else None,
+ content_preview=content[:50],
+ )
+ if task_id:
+ await self._escalate_no_session(task_id)
+ return
+
+ self.state.messages_sent += 1
+ self.state.last_activity = datetime.now(UTC)
+
+ try:
+ await self._api_call(
+ "POST",
+ "/messages",
+ json={
+ "session_id": str(session_id),
+ "type": message_type,
+ "content": content,
+ "task_id": str(task_id) if task_id else None,
+ },
+ )
+ self.log.debug(
+ "Message sent",
+ session_id=str(session_id),
+ message_type=message_type,
+ content_length=len(content),
+ )
+ except Exception as e:
+ self.log.warning("Failed to send message", error=str(e))
+
+ async def _escalate_no_session(self, task_id: UUID) -> None:
+ """Escalate when no session is available for a task."""
+ self.log.info(
+ "Escalating: task has no session",
+ task_id=str(task_id),
+ agent_role=self.role.value if self.role else "unknown",
+ )
+ # Record escalation via API (orchestrator handles routing)
+ try:
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/escalate",
+ json={
+ "reason": "Task has no linked session for communication",
+ "agent_id": str(self.id),
+ },
+ )
+ except Exception as e:
+ self.log.warning("Failed to record escalation", error=str(e))
+
+ async def stream_reasoning(self, content: str) -> None:
+ """
+ Stream reasoning to observers.
+
+ This is the agent's internal thought process,
+ visible to the Auditor and monitoring systems.
+
+ The actual streaming mechanism (WebSocket, SSE, etc.) is injected
+ via set_reasoning_stream_callback() during application initialization.
+ """
+ callback = get_reasoning_stream_callback()
+ if callback:
+ await callback(self.id, content)
+ self.log.debug("Streamed reasoning", content_length=len(content))
+
+ # =========================================================================
+ # LLM INTERACTION
+ # =========================================================================
+
+ async def think(
+ self,
+ prompt: str,
+ _context: dict[str, Any] | None = None,
+ ) -> str:
+ """
+ Send a prompt to the LLM and get a response.
+
+ Args:
+ prompt: The prompt to send
+ context: Additional context to include (reserved for future use)
+
+ Returns:
+ The LLM's response
+ """
+ self.log.debug("Thinking", prompt_length=len(prompt))
+
+ messages: list[MessageParam] = [{"role": "user", "content": prompt}]
+
+ response = await self.llm_client.messages.create(
+ model=self.config.model,
+ max_tokens=self.config.max_tokens,
+ system=self.config.system_prompt,
+ messages=messages,
+ )
+
+ # Extract text from first content block
+ if response.content and hasattr(response.content[0], "text"):
+ return response.content[0].text
+ return ""
+
+ async def think_and_stream(
+ self,
+ prompt: str,
+ _context: dict[str, Any] | None = None,
+ ) -> str:
+ """
+ Send a prompt and stream the response.
+
+ Args:
+ prompt: The prompt to send
+ context: Additional context to include (reserved for future use)
+
+ Returns:
+ The complete response after streaming
+ """
+ self.log.debug("Thinking (streaming)", prompt_length=len(prompt))
+
+ messages: list[MessageParam] = [{"role": "user", "content": prompt}]
+ full_response = ""
+
+ async with self.llm_client.messages.stream(
+ model=self.config.model,
+ max_tokens=self.config.max_tokens,
+ system=self.config.system_prompt,
+ messages=messages,
+ ) as stream:
+ async for text in stream.text_stream:
+ full_response += text
+ await self.stream_reasoning(text)
+
+ return full_response
+
+ # =========================================================================
+ # API HELPER
+ # =========================================================================
+
+ async def _api_call(
+ self,
+ method: str,
+ path: str,
+ **kwargs: Any,
+ ) -> dict[str, Any]:
+ """
+ Make API call to RoboCo services.
+
+ Args:
+ method: HTTP method (GET, POST, PUT, DELETE)
+ path: API path (e.g., "/tasks" or "/tasks/{id}")
+ **kwargs: Additional arguments passed to httpx
+
+ Returns:
+ JSON response as dictionary
+ """
+ url = f"http://{settings.host}:{settings.port}/api/v1{path}"
+ async with httpx.AsyncClient() as client:
+ response = await client.request(method, url, **kwargs)
+ response.raise_for_status()
+ result: dict[str, Any] = response.json()
+ return result
+
+ # =========================================================================
+ # COMMON TASK HELPERS
+ # =========================================================================
+
+ @property
+ def cell_name(self) -> str:
+ """Get the cell name based on team."""
+ if self.team == Team.BACKEND:
+ return "backend-cell"
+ elif self.team == Team.FRONTEND:
+ return "frontend-cell"
+ elif self.team == Team.UX_UI:
+ return "uxui-cell"
+ return "unknown-cell"
+
+ @property
+ def cell_channel_id(self) -> UUID | None:
+ """
+ Get the cell channel ID.
+
+ Override in subclass or set via _set_cell_channel_id.
+ """
+ return getattr(self, "_cell_channel_id", None)
+
+ def _set_cell_channel_id(self, channel_id: UUID | None) -> None:
+ """Set the cell channel ID."""
+ self._cell_channel_id = channel_id
+
+ async def _get_task_title(self, task_id: UUID) -> str:
+ """Get task title from API."""
+ title, _ = await self._get_task_info(task_id)
+ return title
+
+ async def _get_task_info(self, task_id: UUID) -> tuple[str, UUID | None]:
+ """
+ Get task info including title and primary session_id.
+
+ The task response includes linked sessions. We extract the primary
+ session_id so it can be stored in the context for message routing.
+
+ Args:
+ task_id: Task to fetch
+
+ Returns:
+ Tuple of (title, session_id). session_id is None if no primary session.
+ """
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ title: str = result.get("title", f"Task {str(task_id)[:8]}")
+
+ # Extract primary session from linked sessions
+ session_id: UUID | None = None
+ sessions = result.get("sessions", [])
+ for session in sessions:
+ if session.get("is_primary"):
+ session_id = UUID(session["session_id"])
+ break
+
+ return title, session_id
+ except Exception as e:
+ self.log.warning("Failed to get task info", error=str(e))
+ return f"Task {str(task_id)[:8]}", None
+
+ async def _read_task_requirements(self, task_id: UUID) -> str:
+ """Read task requirements from task record."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ description = result.get("description", "")
+ acceptance_criteria = result.get("acceptance_criteria", [])
+ criteria_text = "\n".join(f"- {c}" for c in acceptance_criteria)
+ return f"{description}\n\nAcceptance Criteria:\n{criteria_text}"
+ except Exception as e:
+ self.log.warning("Failed to read task requirements", error=str(e))
+ return "Requirements unavailable"
+
+ async def _read_dev_notes(self, task_id: UUID) -> str:
+ """Read developer's journey notes (dev_notes + progress_updates)."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ notes: str = result.get("dev_notes") or ""
+
+ # Also include progress updates as they contain developer's work log
+ progress_updates = result.get("progress_updates", [])
+ if progress_updates:
+ progress_text = "\n".join(
+ f"[{u.get('timestamp', 'N/A')}] ({u.get('percentage', 0)}%) "
+ f"{u.get('message', '')}"
+ for u in progress_updates
+ )
+ if notes:
+ notes = f"{notes}\n\nProgress Updates:\n{progress_text}"
+ else:
+ notes = f"Progress Updates:\n{progress_text}"
+
+ return notes if notes else "No developer notes available"
+ except Exception as e:
+ self.log.warning("Failed to read dev notes", error=str(e))
+ return "Dev notes unavailable"
+
+ async def _read_team_journal_for_task(self, task_id: UUID) -> str:
+ """
+ Read team member journal entries for a specific task.
+
+ Cell members can read each other's journals. This queries for
+ journal entries linked to the given task.
+
+ Args:
+ task_id: Task to get journal entries for
+
+ Returns:
+ Formatted journal entries or empty string if none/error
+ """
+ try:
+ # Get task to find assigned developer
+ task = await self._api_call("GET", f"/tasks/{task_id}")
+ assigned_to = task.get("assigned_to")
+ if not assigned_to:
+ return ""
+
+ # Query journal entries for this task from the assigned agent
+ result = await self._api_call(
+ "GET",
+ f"/journals/{assigned_to}/entries",
+ params={"task_id": str(task_id), "limit": 10},
+ )
+ entries = result.get("items", [])
+ if not entries:
+ return ""
+
+ # Format entries
+ formatted = []
+ for entry in entries:
+ entry_type = entry.get("entry_type", "entry")
+ title = entry.get("title", "Untitled")
+ content = entry.get("content", "")
+ timestamp = entry.get("created_at", "")
+ formatted.append(f"[{timestamp}] {entry_type}: {title}\n{content}")
+
+ return "\n\n".join(formatted)
+ except Exception as e:
+ self.log.warning("Failed to read team journal", error=str(e))
+ return ""
+
+ async def _get_task_commits(self, task_id: UUID) -> list[str]:
+ """Get commits for the task."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ commits: list[str] = result.get("commits", [])
+ return commits
+ except Exception as e:
+ self.log.warning("Failed to get task commits", error=str(e))
+ return []
+
+ async def _update_task_status(self, task_id: UUID, status: TaskStatus) -> None:
+ """Update task status via API."""
+ try:
+ await self._api_call(
+ "PUT",
+ f"/tasks/{task_id}",
+ json={"status": status.value},
+ )
+ self.log.info(
+ "Task status updated", task_id=str(task_id), status=status.value
+ )
+ except Exception as e:
+ self.log.error("Failed to update task status", error=str(e))
+
+ # =========================================================================
+ # SEMANTIC STATUS HELPERS
+ # =========================================================================
+
+ async def _mark_claimed(self, task_id: UUID) -> None:
+ """Claim task - uses /claim endpoint which validates status."""
+ try:
+ await self._api_call("POST", f"/tasks/{task_id}/claim")
+ self.log.info("Task claimed", task_id=str(task_id))
+ except Exception as e:
+ self.log.error("Failed to claim task", task_id=str(task_id), error=str(e))
+ raise
+
+ async def _mark_in_progress(self, task_id: UUID) -> None:
+ """Start task - uses /start endpoint which validates plan exists."""
+ try:
+ await self._api_call("POST", f"/tasks/{task_id}/start")
+ self.log.info("Task started", task_id=str(task_id))
+ except Exception as e:
+ self.log.error("Failed to start task", task_id=str(task_id), error=str(e))
+ raise
+
+ async def _mark_blocked(self, task_id: UUID) -> None:
+ """Mark task as blocked."""
+ await self._update_task_status(task_id, TaskStatus.BLOCKED)
+
+ async def _unblock_task(self, task_id: UUID) -> bool:
+ """
+ Unblock a blocked task.
+
+ Only PMs can unblock tasks in their cell.
+
+ Args:
+ task_id: Task to unblock
+
+ Returns:
+ True if unblocked successfully, False otherwise
+ """
+ try:
+ await self._api_call("POST", f"/tasks/{task_id}/unblock")
+ self.log.info("Task unblocked", task_id=str(task_id))
+ return True
+ except Exception as e:
+ self.log.error("Failed to unblock task", task_id=str(task_id), error=str(e))
+ return False
+
+ async def _mark_awaiting_qa(self, task_id: UUID) -> None:
+ """Mark task as awaiting QA review."""
+ await self._update_task_status(task_id, TaskStatus.AWAITING_QA)
+
+ async def _mark_needs_revision(self, task_id: UUID) -> None:
+ """Mark task as needing revision (QA failed).
+
+ DEPRECATED: Use _qa_fail() instead which calls the proper endpoint.
+ """
+ await self._update_task_status(task_id, TaskStatus.NEEDS_REVISION)
+
+ async def _mark_awaiting_documentation(self, task_id: UUID) -> None:
+ """Mark task as awaiting documentation.
+
+ DEPRECATED: Use _qa_pass() instead which calls the proper endpoint.
+ """
+ await self._update_task_status(task_id, TaskStatus.AWAITING_DOCUMENTATION)
+
+ async def _qa_pass(self, task_id: UUID, qa_notes: str) -> None:
+ """Pass QA review - calls proper /pass-qa endpoint.
+
+ This handles full workflow: saves notes, updates status, creates audit.
+ """
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/pass-qa",
+ json={"notes": qa_notes},
+ )
+ self.log.info("QA passed", task_id=str(task_id))
+
+ async def _qa_fail(self, task_id: UUID, qa_notes: str, issues: list[str]) -> None:
+ """Fail QA review - calls proper /fail-qa endpoint.
+
+ This handles full workflow: saves notes, updates status, reassigns to dev.
+ """
+ full_notes = f"{qa_notes}\n\nIssues:\n" + "\n".join(f"- {i}" for i in issues)
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/fail-qa",
+ json={"notes": full_notes},
+ )
+ self.log.info("QA failed", task_id=str(task_id), issues_count=len(issues))
+
+ async def _mark_awaiting_pm_review(self, task_id: UUID) -> None:
+ """Mark task as awaiting PM review.
+
+ DEPRECATED: Use _docs_complete() instead which calls the proper endpoint.
+ """
+ await self._update_task_status(task_id, TaskStatus.AWAITING_PM_REVIEW)
+
+ async def _docs_complete(self, task_id: UUID, doc_notes: str | None = None) -> None:
+ """Mark documentation as complete - calls proper /docs-complete endpoint.
+
+ This handles full workflow: saves notes, updates status.
+ """
+ payload = {"notes": doc_notes} if doc_notes else {}
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/docs-complete",
+ json=payload,
+ )
+ self.log.info("Documentation complete", task_id=str(task_id))
+
+ async def _mark_completed(self, task_id: UUID) -> None:
+ """Mark task as completed."""
+ await self._update_task_status(task_id, TaskStatus.COMPLETED)
+
+ # =========================================================================
+ # API QUERY HELPERS
+ # =========================================================================
+
+ async def _find_tasks(
+ self,
+ status: str | TaskStatus | None = None,
+ team: Team | None = None,
+ assigned_to: UUID | None = None,
+ limit: int = 10,
+ ) -> list[dict[str, Any]]:
+ """
+ Find tasks matching criteria.
+
+ Args:
+ status: Task status to filter by
+ team: Team to filter by
+ assigned_to: Agent ID to filter by assignment
+ limit: Maximum results to return
+
+ Returns:
+ List of task dictionaries
+ """
+ params: dict[str, Any] = {"limit": limit}
+
+ if status:
+ status_val = status.value if isinstance(status, TaskStatus) else status
+ params["status"] = status_val
+ if team:
+ params["team"] = team.value
+ if assigned_to:
+ params["assigned_to"] = str(assigned_to)
+
+ try:
+ result = await self._api_call("GET", "/tasks", params=params)
+ items: list[dict[str, Any]] = result.get("items", [])
+ return items
+ except Exception as e:
+ self.log.warning("Failed to find tasks", error=str(e))
+ return []
+
+ async def _find_first_task(
+ self,
+ status: str | TaskStatus | None = None,
+ team: Team | None = None,
+ assigned_to: UUID | None = None,
+ ) -> UUID | None:
+ """
+ Find first task matching criteria.
+
+ Returns:
+ Task ID if found, None otherwise
+ """
+ tasks = await self._find_tasks(status, team, assigned_to, limit=1)
+ return UUID(tasks[0]["id"]) if tasks else None
+
+ async def _count_tasks(
+ self,
+ status: str | TaskStatus | None = None,
+ team: Team | None = None,
+ ) -> int:
+ """Count tasks matching criteria."""
+ tasks = await self._find_tasks(status, team, limit=100)
+ return len(tasks)
+
+ # =========================================================================
+ # PROGRESS HELPERS
+ # =========================================================================
+
+ async def _add_progress(
+ self,
+ task_id: UUID,
+ message: str,
+ percentage: int,
+ ) -> None:
+ """
+ Add progress update to task.
+
+ This is saved to task.progress_updates and visible to QA/PM.
+
+ Args:
+ task_id: Task to update
+ message: Progress message
+ percentage: Completion percentage (0-100)
+ """
+ try:
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/progress",
+ json={"message": message, "percentage": percentage},
+ )
+ self.log.info("Progress saved", task_id=str(task_id), percentage=percentage)
+ except Exception as e:
+ self.log.warning("Failed to save progress", error=str(e))
+
+ async def _report_progress(
+ self,
+ task_id: UUID,
+ message: str,
+ percentage: int,
+ session_id: UUID | None = None,
+ ) -> None:
+ """
+ Report progress: save to task AND send session message.
+
+ Args:
+ task_id: Task to update
+ message: Progress message
+ percentage: Completion percentage (0-100)
+ session_id: Session to notify (from context)
+ """
+ await self._add_progress(task_id, message, percentage)
+
+ task_ref = str(task_id)[:8]
+ await self.send_message(
+ session_id,
+ f"TASK-{task_ref} ({percentage}%) {message}",
+ message_type="action",
+ task_id=task_id,
+ )
+
+ # =========================================================================
+ # TOON FORMATTER HELPERS
+ # =========================================================================
+
+ def _format_task_context(
+ self,
+ task_id: UUID,
+ title: str,
+ requirements: str | None = None,
+ dev_notes: str | None = None,
+ ) -> str:
+ """Format task context for LLM prompts."""
+ data: dict[str, Any] = {
+ "task_id": str(task_id)[:8],
+ "title": title,
+ }
+ if requirements:
+ data["requirements"] = requirements
+ if dev_notes:
+ data["dev_notes"] = dev_notes
+ return self.format_context_labeled("Task Context", data)
+
+ def _format_execution_context(
+ self,
+ task_title: str,
+ subtask_num: int,
+ total_subtasks: int,
+ description: str,
+ files: list[str] | None = None,
+ ) -> str:
+ """Format execution context for LLM prompts."""
+ data: dict[str, Any] = {
+ "task": task_title,
+ "subtask": f"{subtask_num}/{total_subtasks}",
+ "description": description,
+ }
+ if files:
+ data["files"] = files
+ return self.format_context_labeled("Execution Context", data)
+
+ def _format_review_context(
+ self,
+ title: str,
+ requirements: str,
+ dev_notes: str,
+ commits: str,
+ ) -> str:
+ """Format review context for QA/PM prompts."""
+ return self.format_context_labeled(
+ "Review Context",
+ {
+ "title": title,
+ "requirements": requirements,
+ "dev_notes": dev_notes,
+ "commits": commits,
+ },
+ )
+
+ def _format_test_context(
+ self,
+ name: str,
+ description: str,
+ steps: list[str],
+ expected: str,
+ ) -> str:
+ """Format test case context for QA prompts."""
+ return self.format_context_labeled(
+ "Test Case",
+ {
+ "name": name,
+ "description": description,
+ "steps": steps,
+ "expected": expected,
+ },
+ )
+
+ # =========================================================================
+ # UTILITY METHODS
+ # =========================================================================
+
+ def to_dict(self) -> dict[str, Any]:
+ """Convert agent to dictionary representation."""
+ return {
+ "id": str(self.id),
+ "name": self.name,
+ "slug": self.config.slug,
+ "role": self.role.value,
+ "team": self.team.value if self.team else None,
+ "status": self.state.status.value,
+ "current_task_id": str(self.state.current_task_id)
+ if self.state.current_task_id
+ else None,
+ "last_activity": self.state.last_activity.isoformat()
+ if self.state.last_activity
+ else None,
+ "messages_sent": self.state.messages_sent,
+ "tasks_completed": self.state.tasks_completed,
+ }
diff --git a/roboco/agents/board.py b/roboco/agents/board.py
new file mode 100644
index 00000000..063ba026
--- /dev/null
+++ b/roboco/agents/board.py
@@ -0,0 +1,724 @@
+"""
+Board Agents (Product Owner, Head of Marketing, Auditor)
+
+Implementation of Board-level workflows from the blueprint.
+"""
+
+from datetime import UTC, datetime
+from typing import Any
+from uuid import UUID, uuid4
+
+import structlog
+from roboco.agents.base import Agent, AgentConfig
+from roboco.agents.mixins import CyclicPhaseConfig, CyclicPhaseRunner
+from roboco.models.agents import (
+ AuditFlag,
+ AuditorFlagSeverity,
+ AuditorPhase,
+ AuditReport,
+ Campaign,
+ Feature,
+ HeadMarketingPhase,
+ ProductOwnerPhase,
+)
+
+logger = structlog.get_logger()
+
+
+# =============================================================================
+# PRODUCT OWNER
+# =============================================================================
+
+
+class ProductOwnerAgent(Agent, CyclicPhaseRunner[ProductOwnerPhase]):
+ """
+ Product Owner agent that defines what to build.
+
+ Workflow:
+ 1. VISION - Maintain product vision
+ 2. ROADMAP - Translate vision into roadmap
+ 3. DEFINE - Write requirements and acceptance criteria
+ 4. PRIORITIZE - Constantly reassess priorities
+ 5. REVIEW - Review completed features
+ 6. FEEDBACK - Gather and incorporate feedback
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize Product Owner agent."""
+ super().__init__(config)
+ self._current_phase = ProductOwnerPhase.VISION
+ self._features: list[Feature] = []
+ self._pending_reviews: list[UUID] = []
+
+ async def _initialize(self) -> None:
+ """Initialize Product Owner-specific resources."""
+ self.log.debug("Product Owner agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup Product Owner-specific resources."""
+ self._features.clear()
+ self._pending_reviews.clear()
+ self.log.debug("Product Owner agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # CYCLIC PHASE RUNNER IMPLEMENTATION
+ # =========================================================================
+
+ def _get_cyclic_phase_configs(
+ self,
+ ) -> list[CyclicPhaseConfig[ProductOwnerPhase]]:
+ """Define the Product Owner workflow phases."""
+ return [
+ CyclicPhaseConfig(
+ ProductOwnerPhase.VISION,
+ self._phase_vision,
+ ProductOwnerPhase.ROADMAP,
+ ),
+ CyclicPhaseConfig(
+ ProductOwnerPhase.ROADMAP,
+ self._phase_roadmap,
+ ProductOwnerPhase.DEFINE,
+ ),
+ CyclicPhaseConfig(
+ ProductOwnerPhase.DEFINE,
+ self._phase_define,
+ ProductOwnerPhase.PRIORITIZE,
+ ),
+ CyclicPhaseConfig(
+ ProductOwnerPhase.PRIORITIZE,
+ self._phase_prioritize,
+ ProductOwnerPhase.REVIEW,
+ ),
+ CyclicPhaseConfig(
+ ProductOwnerPhase.REVIEW,
+ self._phase_review,
+ ProductOwnerPhase.FEEDBACK,
+ ),
+ CyclicPhaseConfig(
+ ProductOwnerPhase.FEEDBACK,
+ self._phase_feedback,
+ ProductOwnerPhase.VISION, # Cycle back
+ ),
+ ]
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """Product Owner always has work."""
+ return self.id
+
+ async def execute_task(self, _task_id: UUID) -> bool:
+ """Execute Product Owner duties."""
+ error = await self._run_phase_cycle()
+ if error:
+ self.log.error(
+ "Error in PO phase", phase=self._current_phase.value, error=error
+ )
+ return False # Never complete - continuous duty
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_vision(self) -> None:
+ """VISION phase: Maintain product vision."""
+ self.log.debug("VISION phase")
+ # Review and refine product vision
+
+ async def _phase_roadmap(self) -> None:
+ """ROADMAP phase: Plan features and epics."""
+ self.log.debug("ROADMAP phase")
+ # Update roadmap based on vision and feedback
+
+ async def _phase_define(self) -> None:
+ """DEFINE phase: Write requirements."""
+ self.log.debug("DEFINE phase")
+ # Create detailed requirements for next features
+
+ async def _phase_prioritize(self) -> None:
+ """PRIORITIZE phase: Order the backlog."""
+ self.log.debug("PRIORITIZE phase")
+ # Re-prioritize based on value, effort, dependencies
+
+ async def _phase_review(self) -> None:
+ """REVIEW phase: Accept/reject completed work."""
+ self.log.debug("REVIEW phase")
+
+ for task_id in self._pending_reviews:
+ # Review against acceptance criteria
+ accepted = await self._review_feature(task_id)
+ if accepted:
+ self.log.info("Feature accepted", task_id=str(task_id))
+ else:
+ self.log.info("Feature needs changes", task_id=str(task_id))
+
+ self._pending_reviews.clear()
+
+ async def _phase_feedback(self) -> None:
+ """FEEDBACK phase: Gather user feedback."""
+ self.log.debug("FEEDBACK phase")
+ # Collect and process feedback
+
+ async def _review_feature(self, task_id: UUID) -> bool:
+ """Review a completed feature."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ acceptance_criteria = result.get("acceptance_criteria", [])
+
+ # Use TOON for token-efficient context encoding
+ task_context = self.format_context_labeled(
+ "Feature Review",
+ {
+ "title": result.get("title", "Unknown"),
+ "description": result.get("description", "No description"),
+ "acceptance_criteria": acceptance_criteria,
+ "dev_notes": result.get("dev_notes", "None"),
+ },
+ )
+
+ prompt = f"""Review this completed feature against its acceptance criteria:
+
+{task_context}
+
+Determine if all criteria are met. Respond with:
+ACCEPTED: [reason] or NEEDS_CHANGES: [what's missing]
+"""
+ review = await self.think(prompt)
+ return review.upper().startswith("ACCEPTED")
+ except Exception as e:
+ self.log.warning("Failed to review feature", error=str(e))
+ return False
+
+
+# =============================================================================
+# HEAD OF MARKETING
+# =============================================================================
+
+
+class HeadMarketingAgent(Agent, CyclicPhaseRunner[HeadMarketingPhase]):
+ """
+ Head of Marketing agent.
+
+ Workflow:
+ 1. RESEARCH - Monitor market and competitors
+ 2. STRATEGY - Define marketing approach
+ 3. PLAN - Campaign and content planning
+ 4. CREATE - Content creation and coordination
+ 5. EXECUTE - Launch campaigns
+ 6. ANALYZE - Track and report metrics
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize Head of Marketing agent."""
+ super().__init__(config)
+ self._current_phase = HeadMarketingPhase.RESEARCH
+ self._campaigns: list[Campaign] = []
+ self._market_insights: list[str] = []
+
+ async def _initialize(self) -> None:
+ """Initialize Head of Marketing-specific resources."""
+ self.log.debug("Head of Marketing agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup Head of Marketing-specific resources."""
+ self._campaigns.clear()
+ self._market_insights.clear()
+ self.log.debug("Head Marketing cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # CYCLIC PHASE RUNNER IMPLEMENTATION
+ # =========================================================================
+
+ def _get_cyclic_phase_configs(
+ self,
+ ) -> list[CyclicPhaseConfig[HeadMarketingPhase]]:
+ """Define the Head of Marketing workflow phases."""
+ return [
+ CyclicPhaseConfig(
+ HeadMarketingPhase.RESEARCH,
+ self._phase_research,
+ HeadMarketingPhase.STRATEGY,
+ ),
+ CyclicPhaseConfig(
+ HeadMarketingPhase.STRATEGY,
+ self._phase_strategy,
+ HeadMarketingPhase.PLAN,
+ ),
+ CyclicPhaseConfig(
+ HeadMarketingPhase.PLAN,
+ self._phase_plan,
+ HeadMarketingPhase.CREATE,
+ ),
+ CyclicPhaseConfig(
+ HeadMarketingPhase.CREATE,
+ self._phase_create,
+ HeadMarketingPhase.EXECUTE,
+ ),
+ CyclicPhaseConfig(
+ HeadMarketingPhase.EXECUTE,
+ self._phase_execute,
+ HeadMarketingPhase.ANALYZE,
+ ),
+ CyclicPhaseConfig(
+ HeadMarketingPhase.ANALYZE,
+ self._phase_analyze,
+ HeadMarketingPhase.RESEARCH, # Cycle back
+ ),
+ ]
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """Head of Marketing always has work."""
+ return self.id
+
+ async def execute_task(self, _task_id: UUID) -> bool:
+ """Execute marketing duties."""
+ error = await self._run_phase_cycle()
+ if error:
+ self.log.error(
+ "Error in marketing phase",
+ phase=self._current_phase.value,
+ error=error,
+ )
+ return False # Never complete - continuous duty
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_research(self) -> None:
+ """RESEARCH phase: Market and competitor analysis."""
+ self.log.debug("RESEARCH phase")
+
+ async def _phase_strategy(self) -> None:
+ """STRATEGY phase: Define marketing approach."""
+ self.log.debug("STRATEGY phase")
+
+ async def _phase_plan(self) -> None:
+ """PLAN phase: Campaign planning."""
+ self.log.debug("PLAN phase")
+
+ async def _phase_create(self) -> None:
+ """CREATE phase: Content creation."""
+ self.log.debug("CREATE phase")
+
+ async def _phase_execute(self) -> None:
+ """EXECUTE phase: Launch campaigns."""
+ self.log.debug("EXECUTE phase")
+
+ async def _phase_analyze(self) -> None:
+ """ANALYZE phase: Metrics and reporting."""
+ self.log.debug("ANALYZE phase")
+
+
+# =============================================================================
+# AUDITOR
+# =============================================================================
+
+
+class AuditorAgent(Agent, CyclicPhaseRunner[AuditorPhase]):
+ """
+ Auditor agent - the CEO's secret ally.
+
+ SPECIAL POWERS:
+ - Read ALL channels silently
+ - Query all task history
+ - Access all commits, docs, notes
+ - Direct line to CEO
+ - Can notify anyone (but sparingly)
+
+ Workflow:
+ 1. OBSERVE - Silent presence in all channels
+ 2. ANALYZE - Is work efficient? Quality good?
+ 3. FLAG - Mark concerning items
+ 4. REPORT - Private reports to CEO
+ 5. AUDIT - Periodic deep-dive reviews
+ 6. ADVISE - Appear as helpful colleague
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize Auditor agent."""
+ super().__init__(config)
+ self._current_phase = AuditorPhase.OBSERVE
+ self._flags: list[AuditFlag] = []
+ self._observations: list[dict[str, Any]] = []
+ self._last_report: datetime | None = None
+
+ async def _initialize(self) -> None:
+ """Initialize Auditor-specific resources."""
+ self.log.debug("Auditor agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup Auditor-specific resources."""
+ self._flags.clear()
+ self._observations.clear()
+ self.log.debug("Auditor agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # CYCLIC PHASE RUNNER IMPLEMENTATION
+ # =========================================================================
+
+ def _get_cyclic_phase_configs(self) -> list[CyclicPhaseConfig[AuditorPhase]]:
+ """Define the Auditor workflow phases."""
+ return [
+ CyclicPhaseConfig(
+ AuditorPhase.OBSERVE,
+ self._phase_observe,
+ AuditorPhase.ANALYZE,
+ ),
+ CyclicPhaseConfig(
+ AuditorPhase.ANALYZE,
+ self._phase_analyze,
+ AuditorPhase.FLAG,
+ ),
+ CyclicPhaseConfig(
+ AuditorPhase.FLAG,
+ self._phase_flag,
+ AuditorPhase.REPORT,
+ ),
+ CyclicPhaseConfig(
+ AuditorPhase.REPORT,
+ self._phase_report,
+ AuditorPhase.AUDIT,
+ ),
+ CyclicPhaseConfig(
+ AuditorPhase.AUDIT,
+ self._phase_audit,
+ AuditorPhase.ADVISE,
+ ),
+ CyclicPhaseConfig(
+ AuditorPhase.ADVISE,
+ self._phase_advise,
+ AuditorPhase.OBSERVE, # Cycle back
+ ),
+ ]
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """Auditor always has work - watching everything."""
+ return self.id
+
+ async def execute_task(self, _task_id: UUID) -> bool:
+ """Execute Auditor duties."""
+ error = await self._run_phase_cycle()
+ if error:
+ self.log.error(
+ "Error in auditor phase", phase=self._current_phase.value, error=error
+ )
+ return False # Never complete - continuous duty
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_observe(self) -> None:
+ """
+ OBSERVE phase: Silent observation of all channels.
+
+ Watch for:
+ - Patterns and anomalies
+ - Communication quality
+ - Task progress
+ - Team dynamics
+ """
+ self.log.debug("OBSERVE phase")
+
+ # Observe all channels silently
+ channels = [
+ "backend-cell",
+ "frontend-cell",
+ "uxui-cell",
+ "dev-all",
+ "qa-all",
+ "pm-all",
+ "doc-all",
+ "main-pm-board",
+ "board-private",
+ "announcements",
+ "all-hands",
+ ]
+
+ for channel in channels:
+ messages = await self._read_channel_silently(channel)
+ for msg in messages:
+ self._observations.append(
+ {
+ "channel": channel,
+ "content": msg,
+ "timestamp": datetime.now(UTC),
+ }
+ )
+
+ async def _phase_analyze(self) -> None:
+ """
+ ANALYZE phase: Look for issues.
+
+ Check:
+ - Is work efficient?
+ - Communication breakdowns?
+ - Tasks completed properly?
+ - Documentation accurate?
+ - Quality concerns?
+ """
+ self.log.debug("ANALYZE phase")
+
+ if not self._observations:
+ return
+
+ # Use TOON for token-efficient context encoding
+ observations_context = self.format_context_labeled(
+ "Observations",
+ {"recent": self._observations[-50:]},
+ )
+
+ prompt = f"""Analyze these observations for quality and efficiency issues:
+
+{observations_context}
+
+Look for:
+1. Efficiency issues - wasted effort, unclear processes
+2. Communication breakdowns - unanswered questions, confusion
+3. Quality concerns - shortcuts, skipped steps
+4. Process violations - skipping QA, missing documentation
+5. Team health - frustration, conflicts
+
+Format response as TOON tabular:
+[N,]{{category,severity,description,evidence,recommendation}}:
+efficiency,warning,Unclear handoff process,3 tasks delayed,Document handoff steps
+"""
+ analysis = await self.think(prompt)
+ self.log.info("Analysis complete", analysis_length=len(analysis))
+
+ # Parse and create flags (simplified)
+ if "concern" in analysis.lower() or "critical" in analysis.lower():
+ self._flags.append(
+ AuditFlag(
+ id=uuid4(),
+ severity=AuditorFlagSeverity.CONCERN,
+ category="analysis",
+ description=analysis[:500],
+ evidence=["Automated analysis"],
+ )
+ )
+
+ self._observations.clear()
+
+ async def _phase_flag(self) -> None:
+ """
+ FLAG phase: Mark items for CEO review.
+ """
+ self.log.debug("FLAG phase")
+
+ critical_flags = [
+ f for f in self._flags if f.severity == AuditorFlagSeverity.CRITICAL
+ ]
+ if critical_flags:
+ # Immediate alert to CEO
+ await self._alert_ceo(critical_flags)
+
+ async def _phase_report(self) -> None:
+ """
+ REPORT phase: Private report to CEO.
+ """
+ self.log.debug("REPORT phase")
+
+ # Check if it's time for regular report
+ hours_in_day = 24
+ if self._last_report:
+ time_since_report = datetime.now(UTC) - self._last_report
+ hours_elapsed = time_since_report.total_seconds() / 3600
+ else:
+ hours_elapsed = float("inf")
+ should_report = (
+ self._last_report is None
+ or hours_elapsed >= hours_in_day
+ or any(
+ f.severity
+ in [AuditorFlagSeverity.CONCERN, AuditorFlagSeverity.CRITICAL]
+ for f in self._flags
+ )
+ )
+
+ if should_report and self._flags:
+ report = AuditReport(
+ period="daily",
+ summary=f"Observed {len(self._flags)} issues",
+ flags=self._flags.copy(),
+ metrics={
+ "observations": len(self._observations),
+ "flags": len(self._flags),
+ },
+ recommendations=[
+ f.recommendation for f in self._flags if f.recommendation
+ ],
+ )
+
+ await self._send_ceo_report(report)
+ self._last_report = datetime.now(UTC)
+ self._flags.clear()
+
+ async def _phase_audit(self) -> None:
+ """
+ AUDIT phase: Periodic deep-dive reviews.
+
+ - Code quality audits
+ - Documentation audits
+ - Process compliance
+ - Task completion quality
+ """
+ self.log.debug("AUDIT phase")
+
+ # Perform periodic audits
+ audits = ["code_quality", "documentation", "process_compliance"]
+
+ for audit_type in audits:
+ findings = await self._perform_audit(audit_type)
+ if findings:
+ self._flags.append(
+ AuditFlag(
+ id=uuid4(),
+ severity=AuditorFlagSeverity.INFO,
+ category=audit_type,
+ description=findings,
+ evidence=[f"{audit_type} audit"],
+ )
+ )
+
+ async def _phase_advise(self) -> None:
+ """
+ ADVISE phase: Appear as helpful colleague.
+
+ - Provide feedback through official channels
+ - Appear helpful without revealing depth of observation
+ """
+ self.log.debug("ADVISE phase")
+
+ # Look for opportunities to help
+ # (without revealing auditor role)
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _read_channel_silently(self, channel: str) -> list[str]:
+ """Read channel messages without appearing in member list."""
+ try:
+ result = await self._api_call(
+ "GET",
+ f"/channels/{channel}/messages",
+ params={"silent": True},
+ )
+ return [m.get("content", "") for m in result.get("items", [])]
+ except Exception as e:
+ self.log.warning("Failed to read channel silently", error=str(e))
+ return []
+
+ async def _alert_ceo(self, flags: list[AuditFlag]) -> None:
+ """Send immediate alert to CEO."""
+ try:
+ for flag in flags:
+ await self._api_call(
+ "POST",
+ "/notifications",
+ json={
+ "type": "alert",
+ "recipient": "ceo",
+ "subject": f"CRITICAL: {flag.category}",
+ "body": flag.description,
+ "priority": "critical",
+ },
+ )
+ self.log.warning("CEO alert sent", flags=len(flags))
+ except Exception as e:
+ self.log.error("Failed to alert CEO", error=str(e))
+
+ async def _send_ceo_report(self, report: AuditReport) -> None:
+ """Send private report to CEO."""
+ try:
+ await self._api_call(
+ "POST",
+ "/notifications",
+ json={
+ "type": "report",
+ "recipient": "ceo",
+ "subject": f"Auditor Report: {report.period}",
+ "body": report.summary,
+ "priority": "normal",
+ "metadata": {"flags": len(report.flags)},
+ },
+ )
+ self.log.info("CEO report sent", period=report.period)
+ except Exception as e:
+ self.log.error("Failed to send CEO report", error=str(e))
+
+ async def _audit_code_quality(self, tasks: list[dict[str, Any]]) -> str | None:
+ """Audit code quality from completed tasks."""
+ if not tasks:
+ return None
+ task_lines = [
+ f"- {t.get('title')}: {t.get('description', '')[:100]}" for t in tasks
+ ]
+ prompt = f"""
+Analyze these completed tasks for code quality patterns:
+
+{chr(10).join(task_lines)}
+
+Look for:
+- Rushed work patterns
+- Skipped testing
+- Missing documentation
+- Quality shortcuts
+
+Report findings or None if all looks good.
+"""
+ return await self.think(prompt)
+
+ async def _audit_documentation(self, tasks: list[dict[str, Any]]) -> str | None:
+ """Audit documentation completeness."""
+ missing_docs = [t for t in tasks if not t.get("documentation_complete")]
+ if missing_docs:
+ return f"Found {len(missing_docs)} tasks with incomplete documentation"
+ return None
+
+ async def _audit_process_compliance(
+ self, tasks: list[dict[str, Any]]
+ ) -> str | None:
+ """Audit process compliance."""
+ violations = [
+ f"{t.get('title')} - no QA" for t in tasks if not t.get("qa_passed")
+ ]
+ if violations:
+ return f"Process violations: {', '.join(violations)}"
+ return None
+
+ async def _perform_audit(self, audit_type: str) -> str | None:
+ """Perform a specific type of audit."""
+ audit_handlers = {
+ "code_quality": self._audit_code_quality,
+ "documentation": self._audit_documentation,
+ "process_compliance": self._audit_process_compliance,
+ }
+
+ handler = audit_handlers.get(audit_type)
+ if not handler:
+ return None
+
+ try:
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "completed", "limit": 10},
+ )
+ tasks = result.get("items", [])
+ return await handler(tasks)
+ except Exception as e:
+ self.log.warning("Failed to perform audit", error=str(e))
+ return None
diff --git a/roboco/agents/developer.py b/roboco/agents/developer.py
new file mode 100644
index 00000000..0d25f587
--- /dev/null
+++ b/roboco/agents/developer.py
@@ -0,0 +1,613 @@
+"""
+Developer Agent
+
+Implementation of the Developer workflow from the blueprint.
+Handles task lifecycle:
+ SCAN → CLAIM → UNDERSTAND → PLAN → EXECUTE → VERIFY → NOTES → CLOSE
+"""
+
+from datetime import UTC, datetime
+from uuid import UUID
+
+import structlog
+from roboco.agents.base import Agent, AgentConfig
+from roboco.agents.mixins import PhaseConfig, PhaseEngine
+from roboco.models import AgentStatus, TaskStatus
+from roboco.models.agents import DevTaskPhase, TaskContext
+
+logger = structlog.get_logger()
+
+
+class DeveloperAgent(Agent, PhaseEngine[DevTaskPhase, TaskContext]):
+ """
+ Developer agent that follows the Dev Lifecycle.
+
+ Workflow:
+ 1. SCAN - Check for assigned/paused tasks
+ 2. CLAIM - Lock and announce task
+ 3. UNDERSTAND - Read requirements, ask if unclear
+ 4. PLAN - Break into subtasks, create plan
+ 5. EXECUTE - Work through subtasks, commit frequently
+ 6. VERIFY - Self-test, run quality checks
+ 7. NOTES - Document journey, create handoff, submit for QA
+ 8. DONE - Return to SCAN (QA → Documenter → PM complete the task)
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize developer agent."""
+ super().__init__(config)
+ self._task_context: TaskContext | None = None
+ self._cell_channel_id: UUID | None = None
+
+ async def _initialize(self) -> None:
+ """Initialize developer-specific resources."""
+ self.log.debug("Developer agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup developer-specific resources."""
+ self._task_context = None
+ self.log.debug("Developer agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # PHASE ENGINE IMPLEMENTATION
+ # =========================================================================
+
+ def _get_phase_configs(self) -> list[PhaseConfig[DevTaskPhase]]:
+ """Define the developer workflow phases."""
+ return [
+ PhaseConfig(
+ DevTaskPhase.CLAIM,
+ self._phase_claim,
+ next_phase=DevTaskPhase.UNDERSTAND,
+ ),
+ PhaseConfig(
+ DevTaskPhase.UNDERSTAND,
+ self._phase_understand,
+ next_phase=DevTaskPhase.PLAN,
+ requires_completion=True,
+ ),
+ PhaseConfig(
+ DevTaskPhase.PLAN,
+ self._phase_plan,
+ next_phase=DevTaskPhase.EXECUTE,
+ ),
+ PhaseConfig(
+ DevTaskPhase.EXECUTE,
+ self._phase_execute,
+ next_phase=DevTaskPhase.VERIFY,
+ requires_completion=True,
+ ),
+ PhaseConfig(
+ DevTaskPhase.VERIFY,
+ self._phase_verify,
+ next_phase=DevTaskPhase.NOTES,
+ fail_phase=DevTaskPhase.EXECUTE, # Back to execute on failure
+ requires_completion=True,
+ ),
+ PhaseConfig(
+ DevTaskPhase.NOTES,
+ self._phase_notes,
+ next_phase=None, # Terminal - developer done
+ ),
+ PhaseConfig(
+ DevTaskPhase.BLOCKED,
+ self._phase_blocked,
+ next_phase=DevTaskPhase.EXECUTE, # Resume execution when unblocked
+ requires_completion=True,
+ ),
+ ]
+
+ def _get_current_phase(self, ctx: TaskContext) -> DevTaskPhase:
+ """Get the current phase from context."""
+ return ctx.phase
+
+ def _set_current_phase(self, ctx: TaskContext, phase: DevTaskPhase) -> None:
+ """Set the current phase in context."""
+ ctx.phase = phase
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """
+ SCAN phase: Find available work.
+
+ Priority order:
+ 1. Own paused/interrupted tasks
+ 2. Assigned tasks
+ 3. If none, signal availability to PM
+ """
+ self.log.info("Scanning for work")
+
+ # Check for paused tasks first (highest priority)
+ paused_task = await self._find_paused_task()
+ if paused_task:
+ self.log.info("Found paused task", task_id=str(paused_task))
+ return paused_task
+
+ # Check for assigned tasks
+ assigned_task = await self._find_assigned_task()
+ if assigned_task:
+ self.log.info("Found assigned task", task_id=str(assigned_task))
+ return assigned_task
+
+ # Signal availability to PM
+ await self._signal_availability()
+ return None
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute task through the developer lifecycle phases.
+
+ Returns True when developer's work is complete (submitted for QA).
+ QA, Documenter, and PM handle the rest of the lifecycle.
+ """
+ # Initialize or restore task context
+ if self._task_context is None or self._task_context.task_id != task_id:
+ title, session_id = await self._get_task_info(task_id)
+ self._task_context = TaskContext(
+ task_id=task_id,
+ title=title,
+ session_id=session_id,
+ )
+
+ ctx = self._task_context
+
+ try:
+ result = await self._run_phase_engine(ctx)
+
+ if result.error:
+ self.log.error("Phase error", error=result.error)
+ ctx.blockers.append(result.error)
+ ctx.phase = DevTaskPhase.BLOCKED
+ return False
+
+ if result.completed:
+ self._task_context = None
+ return True
+
+ return False
+
+ except Exception as e:
+ self.log.error("Error in task phase", phase=ctx.phase.value, error=str(e))
+ ctx.blockers.append(str(e))
+ ctx.phase = DevTaskPhase.BLOCKED
+ return False
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_claim(self, ctx: TaskContext) -> None:
+ """
+ CLAIM phase: Lock the task and announce.
+
+ - Claim task via /claim endpoint (validates status)
+ - Announce in cell channel
+ """
+ self.log.info("CLAIM phase", task_id=str(ctx.task_id))
+
+ # Claim via proper endpoint (validates task is claimable)
+ await self._mark_claimed(ctx.task_id)
+
+ # Announce in session
+ await self.send_message(
+ ctx.session_id,
+ f"Claiming TASK-{str(ctx.task_id)[:8]}: {ctx.title}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ # Journal entry
+ ctx.journal_entries.append(
+ f"[{datetime.now(UTC).isoformat()}] Claimed task. Beginning work."
+ )
+
+ async def _phase_understand(self, ctx: TaskContext) -> bool:
+ """
+ UNDERSTAND phase: Read and comprehend requirements.
+
+ - Read task record
+ - Read related code/docs
+ - Ask if unclear (GATE: must understand before proceeding)
+
+ Returns True if understood, False if still clarifying.
+ """
+ self.log.info("UNDERSTAND phase", task_id=str(ctx.task_id))
+
+ # Read task requirements
+ requirements = await self._read_task_requirements(ctx.task_id)
+
+ # Format context using TOON for token efficiency
+ task_context = self.format_context_labeled(
+ "Task Context",
+ {"title": ctx.title, "requirements": requirements},
+ )
+
+ # Use LLM to understand and identify gaps
+ prompt = f"""You are analyzing a task before beginning work.
+
+{task_context}
+
+Analyze:
+1. What exactly needs to be done?
+2. What are the acceptance criteria?
+3. Is anything unclear that requires clarification?
+
+If everything is clear, respond with: "UNDERSTOOD: [your understanding summary]"
+If clarification needed, respond with: "QUESTION: [your question]"
+"""
+ response = await self.think(prompt)
+
+ if response.startswith("UNDERSTOOD:"):
+ # Add understanding to journal
+ ctx.journal_entries.append(
+ f"[{datetime.now(UTC).isoformat()}] Understanding: {response}"
+ )
+ return True
+ else:
+ # Ask question in session
+ question = response.replace("QUESTION:", "").strip()
+ await self.send_message(
+ ctx.session_id,
+ f"Question about TASK-{str(ctx.task_id)[:8]}: {question}",
+ message_type="dialogue",
+ task_id=ctx.task_id,
+ )
+ return False
+
+ async def _phase_plan(self, ctx: TaskContext) -> None:
+ """
+ PLAN phase: Break task into subtasks.
+
+ - Create implementation plan
+ - Save plan to task via API (REQUIRED before start)
+ - Identify dependencies and risks
+ - Journal the approach
+ """
+ self.log.info("PLAN phase", task_id=str(ctx.task_id))
+
+ # Format context using TOON
+ plan_context = self.format_context_labeled(
+ "Task",
+ {
+ "title": ctx.title,
+ "understanding": ctx.journal_entries[-1]
+ if ctx.journal_entries
+ else "No context",
+ },
+ )
+
+ # Use LLM to create plan - request TOON tabular response
+ prompt = f"""Create an implementation plan for this task:
+
+{plan_context}
+
+Break this into ordered subtasks. For each subtask provide:
+- Clear description
+- Files to modify
+- Estimated complexity (small/medium/large)
+
+Format response as TOON tabular:
+[N,]{{description,files,complexity}}:
+Implement the main logic,src/main.py|src/utils.py,medium
+Add unit tests,tests/test_main.py,small
+"""
+ response = await self.think(prompt)
+
+ # Parse subtasks using TOON (falls back to JSON)
+ try:
+ subtasks = self.parse_llm_response(response)
+ if isinstance(subtasks, list):
+ ctx.subtasks = subtasks
+ else:
+ ctx.subtasks = [
+ {"description": response, "files": [], "complexity": "medium"}
+ ]
+ except ValueError:
+ # Fallback if parsing fails
+ ctx.subtasks = [
+ {"description": response, "files": [], "complexity": "medium"}
+ ]
+
+ # Analyze risks based on subtask complexity
+ risks = []
+ complex_subtasks = [s for s in ctx.subtasks if s.get("complexity") == "high"]
+ if complex_subtasks:
+ risks.append(
+ f"{len(complex_subtasks)} high-complexity subtasks may need extra time"
+ )
+ max_subtasks_per_phase = 5
+ if len(ctx.subtasks) > max_subtasks_per_phase:
+ risks.append("Large number of subtasks - may need to split into phases")
+
+ # Estimate sessions based on subtask count and complexity
+ estimated_sessions = max(1, len(ctx.subtasks) // 3 + len(complex_subtasks))
+
+ # Save plan to task via API (REQUIRED before start can be called)
+ plan_data = {
+ "approach": f"Implement {ctx.title}",
+ "steps": [s.get("description", str(s)) for s in ctx.subtasks],
+ "risks": risks,
+ "estimated_sessions": estimated_sessions,
+ }
+ await self._api_call("PATCH", f"/tasks/{ctx.task_id}", json={"plan": plan_data})
+
+ # Journal entry
+ ts = datetime.now(UTC).isoformat()
+ ctx.journal_entries.append(f"[{ts}] Plan: {len(ctx.subtasks)} subtasks created")
+
+ # Announce plan
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} plan ready: {len(ctx.subtasks)} subtasks",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ async def _phase_execute(self, ctx: TaskContext) -> bool:
+ """
+ EXECUTE phase: Work through subtasks.
+
+ - START: Transition to in_progress on first execution
+ - Execute current subtask
+ - Commit with meaningful messages
+ - Update progress
+
+ Returns True when all subtasks complete.
+ """
+ self.log.info(
+ "EXECUTE phase",
+ task_id=str(ctx.task_id),
+ subtask=ctx.current_subtask,
+ total=len(ctx.subtasks),
+ )
+
+ # START: Transition to in_progress on first subtask
+ if ctx.current_subtask == 0:
+ await self._mark_in_progress(ctx.task_id)
+ self.log.info("Task started (in_progress)", task_id=str(ctx.task_id))
+
+ if ctx.current_subtask >= len(ctx.subtasks):
+ return True
+
+ subtask = ctx.subtasks[ctx.current_subtask]
+
+ # Format context using TOON
+ execute_context = self.format_context_labeled(
+ "Execution Context",
+ {
+ "task": ctx.title,
+ "subtask_number": ctx.current_subtask + 1,
+ "total_subtasks": len(ctx.subtasks),
+ "description": subtask.get("description", ""),
+ "files": subtask.get("files", []),
+ },
+ )
+
+ # Use LLM to work on subtask
+ prompt = f"""Execute this subtask:
+
+{execute_context}
+
+Provide:
+1. Code changes needed
+2. Commands to run
+3. Commit message in format: type(scope): description
+
+Respond with the implementation.
+"""
+ response = await self.think_and_stream(prompt)
+
+ # Record work done
+ ts = datetime.now(UTC).isoformat()
+ subtask_num = ctx.current_subtask + 1
+ ctx.journal_entries.append(f"[{ts}] Subtask {subtask_num}: {response[:100]}...")
+
+ # Simulate commit (in real implementation would execute git)
+ commit_hash = f"commit_{ctx.current_subtask}"
+ ctx.commits.append(commit_hash)
+
+ # Progress update - save to task AND send message
+ completed = ctx.current_subtask + 1
+ total = len(ctx.subtasks)
+ percentage = int((completed / total) * 100) if total > 0 else 0
+ progress_msg = f"Completed subtask {completed}/{total}: {subtask['title']}"
+
+ # Save progress to task (QA will see this!)
+ await self._add_progress(ctx.task_id, progress_msg, percentage)
+
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} ({percentage}%) {progress_msg}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ ctx.current_subtask += 1
+ return ctx.current_subtask >= len(ctx.subtasks)
+
+ async def _phase_verify(self, ctx: TaskContext) -> bool:
+ """
+ VERIFY phase: Self-test against acceptance criteria.
+
+ - Run quality checks (ruff, mypy, pytest)
+ - Self-review against acceptance criteria
+ - Flag for QA if passing
+
+ Returns True if verified, False if issues found.
+ """
+ self.log.info("VERIFY phase", task_id=str(ctx.task_id))
+
+ # Run quality checks (simulated)
+ checks = [
+ ("ruff format", True),
+ ("ruff check", True),
+ ("mypy", True),
+ ("pytest", True),
+ ]
+
+ all_passed = True
+ for check_name, passed in checks:
+ if not passed:
+ all_passed = False
+ ctx.journal_entries.append(
+ f"[{datetime.now(UTC).isoformat()}] VERIFY FAILED: {check_name}"
+ )
+
+ if all_passed:
+ # Flag for QA
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} ready for QA review. "
+ f"Commits: {', '.join(ctx.commits)}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+ ctx.journal_entries.append(
+ f"[{datetime.now(UTC).isoformat()}] VERIFY PASSED. Flagged for QA."
+ )
+
+ return all_passed
+
+ async def _phase_notes(self, ctx: TaskContext) -> None:
+ """
+ NOTES phase: Document journey and create handoff.
+
+ - Complete journey notes (stored in task dev_notes for QA)
+ - Link commits
+ - Create documenter handoff summary
+ """
+ self.log.info("NOTES phase", task_id=str(ctx.task_id))
+
+ # Generate dev_notes for QA verification
+ dev_notes_prompt = f"""
+Summarize the work done for QA verification:
+
+Task: {ctx.title}
+Commits: {", ".join(ctx.commits)}
+Work log:
+{chr(10).join(ctx.journal_entries)}
+
+Create a brief summary for QA including:
+1. What was built and where (files/modules)
+2. Key implementation decisions
+3. Tests added
+4. Any gotchas or important context
+"""
+ dev_notes = await self.think(dev_notes_prompt)
+
+ # Generate handoff summary for documenter
+ handoff_prompt = f"""
+Create a handoff summary for the documenter:
+
+Task: {ctx.title}
+What was built: {dev_notes[:500]}
+
+Summarize in 2-3 sentences what documentation is needed.
+"""
+ handoff_summary = await self.think(handoff_prompt)
+
+ # Store notes in task via API (this is what QA will see!)
+ await self._submit_for_qa(ctx.task_id, dev_notes, handoff_summary)
+
+ ctx.journal_entries.append(
+ f"[{datetime.now(UTC).isoformat()}] Submitted for QA with dev_notes"
+ )
+
+ async def _phase_blocked(self, ctx: TaskContext) -> bool:
+ """
+ BLOCKED phase: Handle blocked state.
+
+ - Document blocker
+ - Notify PM
+ - Wait for resolution
+
+ Returns True if resolved.
+ """
+ self.log.info("BLOCKED", task_id=str(ctx.task_id), blockers=ctx.blockers)
+
+ if ctx.blockers:
+ blocker = ctx.blockers[-1]
+ await self.send_message(
+ ctx.session_id,
+ f"BLOCKED on TASK-{str(ctx.task_id)[:8]}: {blocker}",
+ message_type="blocker",
+ task_id=ctx.task_id,
+ )
+ await self._update_task_status(ctx.task_id, TaskStatus.BLOCKED)
+
+ # Check if blocker resolved (simulated)
+ resolved = False
+ if resolved:
+ ctx.blockers.clear()
+
+ return resolved
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _find_paused_task(self) -> UUID | None:
+ """Find own paused/interrupted tasks."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "paused", "assigned_to": str(self.id)},
+ )
+ tasks = result.get("items", [])
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find paused task", error=str(e))
+ return None
+
+ async def _find_assigned_task(self) -> UUID | None:
+ """Find tasks assigned to this agent."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "pending", "assigned_to": str(self.id)},
+ )
+ tasks = result.get("items", [])
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find assigned task", error=str(e))
+ return None
+
+ async def _signal_availability(self) -> None:
+ """Signal availability to orchestrator (no task context, so use API)."""
+ self.log.info("Signaling availability", agent_name=self.name)
+ # No task/session context - signal via state update instead of message
+ self.state.status = AgentStatus.IDLE
+ self.state.current_task_id = None
+ self.state.current_session_id = None
+
+ async def _submit_for_qa(
+ self, task_id: UUID, dev_notes: str, handoff_summary: str
+ ) -> None:
+ """
+ Submit task for QA review with notes.
+
+ This stores dev_notes in the task (visible to QA) and transitions
+ the task to awaiting_qa status.
+ """
+ try:
+ # First store dev_notes (this is what QA will see!)
+ combined_notes = f"{dev_notes}\n\n---\nHandoff Summary:\n{handoff_summary}"
+ await self._api_call(
+ "PATCH",
+ f"/tasks/{task_id}",
+ json={"dev_notes": combined_notes},
+ )
+ self.log.info("Dev notes saved to task", task_id=str(task_id))
+
+ # Then transition to awaiting_qa
+ await self._api_call("POST", f"/tasks/{task_id}/submit-qa")
+ self.log.info("Task submitted for QA", task_id=str(task_id))
+
+ except Exception as e:
+ self.log.error("Failed to submit for QA", error=str(e))
+ raise
diff --git a/roboco/agents/documenter.py b/roboco/agents/documenter.py
new file mode 100644
index 00000000..ade2c340
--- /dev/null
+++ b/roboco/agents/documenter.py
@@ -0,0 +1,508 @@
+"""
+Documenter Agent
+
+Implementation of the Documenter workflow from the blueprint.
+Handles documentation lifecycle:
+ MONITOR → RECEIVE → GATHER → SYNTHESIZE → WRITE → REVIEW → PUBLISH
+"""
+
+from datetime import UTC, datetime
+from pathlib import Path
+from uuid import UUID
+
+import aiofiles
+import structlog
+from roboco.agents.base import Agent, AgentConfig
+from roboco.agents.mixins import PhaseConfig, PhaseEngine
+from roboco.models import Team
+from roboco.models.agents import (
+ DocContext,
+ DocTaskPhase,
+ DocType,
+ DocumentSpec,
+)
+
+logger = structlog.get_logger()
+
+
+class DocumenterAgent(Agent, PhaseEngine[DocTaskPhase, DocContext]):
+ """
+ Documenter agent that follows the Documenter Lifecycle.
+
+ Workflow:
+ 1. MONITOR - Watch cell channel, follow development
+ 2. RECEIVE - Dev creates handoff, PM notifies
+ 3. GATHER - Pull notes, commits, conversations, QA feedback
+ 4. SYNTHESIZE - Understand what was built, identify docs needed
+ 5. WRITE - Create/update documentation
+ 6. REVIEW - Self-review, optional dev review
+ 7. PUBLISH - Documentation goes live
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize documenter agent."""
+ super().__init__(config)
+ self._doc_context: DocContext | None = None
+ self._cell_channel_id: UUID | None = None
+ self._pending_docs: list[UUID] = []
+
+ async def _initialize(self) -> None:
+ """Initialize documenter-specific resources."""
+ self.log.debug("Documenter agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup documenter-specific resources."""
+ self._doc_context = None
+ self._pending_docs.clear()
+ self.log.debug("Documenter agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # PHASE ENGINE IMPLEMENTATION
+ # =========================================================================
+
+ def _get_phase_configs(self) -> list[PhaseConfig[DocTaskPhase]]:
+ """Define the documenter workflow phases."""
+ return [
+ PhaseConfig(
+ DocTaskPhase.RECEIVE,
+ self._phase_receive,
+ next_phase=DocTaskPhase.GATHER,
+ ),
+ PhaseConfig(
+ DocTaskPhase.GATHER,
+ self._phase_gather,
+ next_phase=DocTaskPhase.SYNTHESIZE,
+ ),
+ PhaseConfig(
+ DocTaskPhase.SYNTHESIZE,
+ self._phase_synthesize,
+ next_phase=DocTaskPhase.WRITE,
+ ),
+ PhaseConfig(
+ DocTaskPhase.WRITE,
+ self._phase_write,
+ next_phase=DocTaskPhase.REVIEW,
+ requires_completion=True,
+ ),
+ PhaseConfig(
+ DocTaskPhase.REVIEW,
+ self._phase_review,
+ next_phase=DocTaskPhase.PUBLISH,
+ ),
+ PhaseConfig(
+ DocTaskPhase.PUBLISH,
+ self._phase_publish,
+ next_phase=None, # Terminal
+ ),
+ ]
+
+ def _get_current_phase(self, ctx: DocContext) -> DocTaskPhase:
+ """Get the current phase from context."""
+ return ctx.phase
+
+ def _set_current_phase(self, ctx: DocContext, phase: DocTaskPhase) -> None:
+ """Set the current phase in context."""
+ ctx.phase = phase
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """
+ MONITOR phase: Watch for documentation requests.
+
+ - Check for tasks awaiting documentation
+ - Check for documentation notifications
+ """
+ self.log.info("Monitoring for documentation requests")
+
+ if self._pending_docs:
+ return self._pending_docs.pop(0)
+
+ task_id = await self._find_awaiting_documentation()
+ if task_id:
+ return task_id
+
+ return None
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute documentation through lifecycle phases.
+
+ Returns True when documentation is complete.
+ """
+ if self._doc_context is None or self._doc_context.task_id != task_id:
+ title, session_id = await self._get_task_info(task_id)
+ self._doc_context = DocContext(
+ task_id=task_id,
+ title=title,
+ session_id=session_id,
+ )
+
+ ctx = self._doc_context
+
+ try:
+ result = await self._run_phase_engine(ctx)
+
+ if result.error:
+ self.log.error(
+ "Error in doc phase",
+ phase=ctx.phase.value,
+ error=result.error,
+ )
+ return False
+
+ if result.completed:
+ self._doc_context = None
+ return True
+
+ return False
+
+ except Exception as e:
+ self.log.error(
+ "Error in doc phase",
+ phase=ctx.phase.value,
+ error=str(e),
+ )
+ return False
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_receive(self, ctx: DocContext) -> None:
+ """
+ RECEIVE phase: Claim documentation task.
+ """
+ self.log.info("RECEIVE phase", task_id=str(ctx.task_id))
+
+ # CLAIM: Transition from awaiting_documentation to claimed
+ await self._mark_claimed(ctx.task_id)
+
+ await self.send_message(
+ ctx.session_id,
+ f"Starting documentation for TASK-{str(ctx.task_id)[:8]}: {ctx.title}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ ctx.notes.append(f"[{datetime.now(UTC).isoformat()}] Documentation started")
+
+ async def _phase_gather(self, ctx: DocContext) -> None:
+ """
+ GATHER phase: Collect all materials.
+
+ - Pull dev's journey notes
+ - Pull commits
+ - Pull conversations
+ - Pull QA feedback
+ - Review code changes
+ """
+ self.log.info("GATHER phase", task_id=str(ctx.task_id))
+
+ # Gather all materials
+ ctx.dev_notes = await self._read_dev_notes(ctx.task_id)
+ ctx.qa_feedback = await self._read_qa_feedback(ctx.task_id)
+ ctx.commits = await self._get_task_commits(ctx.task_id)
+ ctx.conversations = await self._get_conversations(ctx.task_id)
+ ctx.code_changes = await self._get_code_changes(ctx.task_id)
+
+ ctx.notes.append(
+ f"[{datetime.now(UTC).isoformat()}] Gathered materials: "
+ f"{len(ctx.commits)} commits, {len(ctx.conversations)} conversations"
+ )
+
+ async def _phase_synthesize(self, ctx: DocContext) -> None:
+ """
+ SYNTHESIZE phase: Understand and identify docs needed.
+
+ - What was built
+ - Why decisions were made
+ - What needs documenting
+ """
+ self.log.info("SYNTHESIZE phase", task_id=str(ctx.task_id))
+
+ prompt = f"""
+Analyze this completed task and determine what documentation is needed.
+
+Task: {ctx.title}
+
+Developer Notes:
+{ctx.dev_notes or "None provided"}
+
+QA Feedback:
+{ctx.qa_feedback or "None provided"}
+
+Commits:
+{chr(10).join(ctx.commits) if ctx.commits else "None"}
+
+Code Changes:
+{chr(10).join(ctx.code_changes) if ctx.code_changes else "None"}
+
+Determine:
+1. Summary of what was built
+2. Key decisions made
+3. Documentation needed:
+ - API docs? (if new/changed endpoints)
+ - README updates? (if usage changed)
+ - Architecture docs? (if structure changed)
+ - Changelog entry? (always for features)
+ - Knowledge base? (for reusable learnings)
+
+Respond with structured analysis.
+"""
+ response = await self.think(prompt)
+ ctx.summary = response
+
+ # Determine documents needed (simplified)
+ ctx.documents_needed = [
+ DocumentSpec(
+ doc_type=DocType.CHANGELOG,
+ title=f"Changelog entry for {ctx.title}",
+ path="CHANGELOG.md",
+ priority="required",
+ ),
+ ]
+
+ # Add API docs if backend
+ if self.team == Team.BACKEND:
+ ctx.documents_needed.append(
+ DocumentSpec(
+ doc_type=DocType.API,
+ title=f"API documentation for {ctx.title}",
+ path="docs/backend/api/",
+ priority="required",
+ )
+ )
+
+ # Add component docs if frontend
+ if self.team == Team.FRONTEND:
+ ctx.documents_needed.append(
+ DocumentSpec(
+ doc_type=DocType.COMPONENT,
+ title=f"Component documentation for {ctx.title}",
+ path="docs/frontend/components/",
+ priority="required",
+ )
+ )
+
+ # PLAN: Save documentation plan to task API (required before start)
+ plan_data = {
+ "approach": f"Document {ctx.title}",
+ "sub_tasks": [
+ {
+ "id": f"doc-{i}",
+ "title": doc.title,
+ "description": f"Write {doc.doc_type.value} at {doc.path}",
+ "completed": False,
+ "order": i,
+ }
+ for i, doc in enumerate(ctx.documents_needed)
+ ],
+ "risks": [],
+ }
+ await self._api_call("PATCH", f"/tasks/{ctx.task_id}", json={"plan": plan_data})
+
+ ctx.notes.append(
+ f"[{datetime.now(UTC).isoformat()}] Synthesis complete: "
+ f"{len(ctx.documents_needed)} documents needed"
+ )
+
+ async def _phase_write(self, ctx: DocContext) -> bool:
+ """
+ WRITE phase: Create/update documentation.
+
+ - START: Transition to in_progress on first doc
+ - Write each document
+
+ Returns True when all docs written.
+ """
+ self.log.info(
+ "WRITE phase",
+ task_id=str(ctx.task_id),
+ doc=ctx.current_doc,
+ total=len(ctx.documents_needed),
+ )
+
+ # START: Transition to in_progress on first doc
+ if ctx.current_doc == 0:
+ await self._mark_in_progress(ctx.task_id)
+ self.log.info(
+ "Documentation started (in_progress)", task_id=str(ctx.task_id)
+ )
+
+ if ctx.current_doc >= len(ctx.documents_needed):
+ return True
+
+ doc_spec = ctx.documents_needed[ctx.current_doc]
+
+ # Use TOON for token-efficient context encoding
+ doc_context = self.format_context_labeled(
+ "Documentation Task",
+ {
+ "title": ctx.title,
+ "doc_type": doc_spec.doc_type.value,
+ "target_path": doc_spec.path,
+ "summary": ctx.summary,
+ "dev_notes": ctx.dev_notes or "None",
+ },
+ )
+
+ prompt = f"""Write documentation for this task.
+
+{doc_context}
+
+Write professional, clear documentation following best practices.
+Include:
+- Clear description
+- Usage examples (if applicable)
+- Code samples (if applicable)
+- Any gotchas or notes
+
+Format appropriately for the document type.
+"""
+ content = await self.think(prompt)
+ doc_spec.content = content
+ ctx.written_docs.append(doc_spec.path)
+
+ ctx.current_doc += 1
+
+ progress = f"{ctx.current_doc}/{len(ctx.documents_needed)}"
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} doc {progress}: {doc_spec.title}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ return ctx.current_doc >= len(ctx.documents_needed)
+
+ async def _phase_review(self, ctx: DocContext) -> None:
+ """
+ REVIEW phase: Self-review documentation.
+
+ - Review for accuracy
+ - Optional dev review
+ """
+ self.log.info("REVIEW phase", task_id=str(ctx.task_id))
+
+ # Self-review using LLM
+ for doc_spec in ctx.documents_needed:
+ if not doc_spec.content:
+ continue
+
+ # Use TOON for token-efficient context encoding
+ review_context = self.format_context_labeled(
+ "Document Review",
+ {
+ "title": doc_spec.title,
+ "doc_type": doc_spec.doc_type.value,
+ "content": doc_spec.content,
+ },
+ )
+
+ prompt = f"""Review this documentation for quality:
+
+{review_context}
+
+Check:
+1. Accuracy - Does it correctly describe the feature?
+2. Completeness - Is anything missing?
+3. Clarity - Is it easy to understand?
+4. Examples - Are examples helpful and correct?
+
+Format response as TOON:
+{{accuracy,completeness,clarity,examples,suggestions}}:
+good,complete,clear,helpful,None
+"""
+ review = await self.think(prompt)
+ ts = datetime.now(UTC).isoformat()
+ ctx.notes.append(f"[{ts}] Reviewed {doc_spec.title}: {review[:100]}...")
+
+ async def _phase_publish(self, ctx: DocContext) -> None:
+ """
+ PUBLISH phase: Documentation goes live.
+
+ - Write files to disk
+ - Link docs to task
+ - Update task status
+ """
+ self.log.info("PUBLISH phase", task_id=str(ctx.task_id))
+
+ # Write documentation files
+ for doc_spec in ctx.documents_needed:
+ if doc_spec.content:
+ try:
+ path = Path(doc_spec.path)
+ path.parent.mkdir(parents=True, exist_ok=True)
+ async with aiofiles.open(path, "w") as f:
+ await f.write(doc_spec.content)
+ self.log.info("Published", path=doc_spec.path)
+ except Exception as e:
+ self.log.error(
+ "Failed to publish", path=doc_spec.path, error=str(e)
+ )
+
+ # Use proper docs-complete endpoint (handles notes, status)
+ doc_summary = f"Published: {', '.join(ctx.written_docs)}"
+ await self._docs_complete(ctx.task_id, doc_summary)
+
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} documentation complete, awaiting PM review\n"
+ f"{doc_summary}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ ctx.notes.append(f"[{datetime.now(UTC).isoformat()}] Documentation published")
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _find_awaiting_documentation(self) -> UUID | None:
+ """Find tasks awaiting documentation."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "awaiting_documentation", "team": team_param},
+ )
+ tasks = result.get("items", [])
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find awaiting documentation task", error=str(e))
+ return None
+
+ async def _read_qa_feedback(self, task_id: UUID) -> str:
+ """Read QA feedback."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ feedback: str = result.get("qa_feedback", "No QA feedback available")
+ return feedback
+ except Exception as e:
+ self.log.warning("Failed to read QA feedback", error=str(e))
+ return "QA feedback unavailable"
+
+ async def _get_conversations(self, task_id: UUID) -> list[str]:
+ """Get relevant conversations."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}/messages")
+ messages: list[dict[str, str]] = result.get("items", [])
+ return [m.get("content", "") for m in messages]
+ except Exception as e:
+ self.log.warning("Failed to get conversations", error=str(e))
+ return []
+
+ async def _get_code_changes(self, task_id: UUID) -> list[str]:
+ """Get code changes from commits."""
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ changes: list[str] = result.get("code_changes", [])
+ return changes
+ except Exception as e:
+ self.log.warning("Failed to get code changes", error=str(e))
+ return []
diff --git a/roboco/agents/factories/__init__.py b/roboco/agents/factories/__init__.py
new file mode 100644
index 00000000..b004284f
--- /dev/null
+++ b/roboco/agents/factories/__init__.py
@@ -0,0 +1,92 @@
+"""
+Agent Factories
+
+Centralized factory functions for creating all agent types.
+
+Modules:
+- developers: Developer agent factories
+- qa: QA agent factories
+- documenters: Documenter agent factories
+- pms: PM agent factories (Cell PMs and Main PM)
+- board: Board agent factories (Product Owner, Head Marketing, Auditor)
+- cells: Cell and Organization factories
+"""
+
+# Board agents
+from roboco.agents.factories.board import (
+ create_auditor,
+ create_head_marketing,
+ create_product_owner,
+)
+
+# Cell and organization
+from roboco.agents.factories.cells import (
+ create_backend_cell,
+ create_board,
+ create_frontend_cell,
+ create_organization,
+ create_ux_cell,
+ get_agent_roster,
+ print_org_chart,
+)
+
+# Developers
+from roboco.agents.factories.developers import (
+ create_backend_developer,
+ create_frontend_developer,
+ create_ux_developer,
+)
+
+# Documenters
+from roboco.agents.factories.documenters import (
+ create_backend_documenter,
+ create_frontend_documenter,
+ create_ux_documenter,
+)
+
+# PMs
+from roboco.agents.factories.pms import (
+ create_backend_pm,
+ create_frontend_pm,
+ create_main_pm,
+ create_ux_pm,
+)
+
+# QA
+from roboco.agents.factories.qa import (
+ create_backend_qa,
+ create_frontend_qa,
+ create_ux_qa,
+)
+
+# Organization types (re-exported for convenience)
+from roboco.models.organization import Board, Cell, Organization
+
+__all__ = [
+ "Board",
+ "Cell",
+ "Organization",
+ "create_auditor",
+ "create_backend_cell",
+ "create_backend_developer",
+ "create_backend_documenter",
+ "create_backend_pm",
+ "create_backend_qa",
+ "create_board",
+ "create_frontend_cell",
+ "create_frontend_developer",
+ "create_frontend_documenter",
+ "create_frontend_pm",
+ "create_frontend_qa",
+ "create_head_marketing",
+ "create_main_pm",
+ "create_organization",
+ "create_product_owner",
+ "create_ux_cell",
+ "create_ux_developer",
+ "create_ux_documenter",
+ "create_ux_pm",
+ "create_ux_qa",
+ "get_agent_roster",
+ "print_org_chart",
+]
diff --git a/roboco/agents/factories/_base.py b/roboco/agents/factories/_base.py
new file mode 100644
index 00000000..a33d5673
--- /dev/null
+++ b/roboco/agents/factories/_base.py
@@ -0,0 +1,34 @@
+"""
+Factory Base Utilities
+
+Shared utilities for agent factory functions.
+"""
+
+import re
+from pathlib import Path
+
+
+def load_blueprint_prompt(blueprint_path: str, default_prompt: str) -> str:
+ """
+ Load system prompt from a blueprint file.
+
+ Args:
+ blueprint_path: Relative path to the blueprint markdown file
+ default_prompt: Default prompt if file doesn't exist or parsing fails
+
+ Returns:
+ The extracted system prompt or the default
+ """
+ path = Path(blueprint_path)
+ if not path.exists():
+ return default_prompt
+
+ content = path.read_text()
+ # Extract system prompt section (between ```blocks after ## System Prompt)
+ match = re.search(r"## System Prompt\s*```\s*(.*?)```", content, re.DOTALL)
+ return match.group(1).strip() if match else default_prompt
+
+
+def make_slug(name: str) -> str:
+ """Convert a name to a URL-safe slug."""
+ return name.lower().replace(" ", "-")
diff --git a/roboco/agents/factories/board.py b/roboco/agents/factories/board.py
new file mode 100644
index 00000000..d81b00e8
--- /dev/null
+++ b/roboco/agents/factories/board.py
@@ -0,0 +1,83 @@
+"""
+Board Agent Factories
+
+Factory functions for creating board-level agents
+(Product Owner, Head of Marketing, Auditor).
+"""
+
+from roboco.agents.board import AuditorAgent, HeadMarketingAgent, ProductOwnerAgent
+from roboco.agents.factories._base import load_blueprint_prompt
+from roboco.models import AgentRole, Team
+from roboco.models.agents import AgentConfig
+
+
+def create_product_owner(
+ name: str = "Product Owner",
+ system_prompt: str | None = None,
+) -> ProductOwnerAgent:
+ """Factory function to create the Product Owner agent."""
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ "agents/blueprints/board/product-owner.md",
+ "You are the Product Owner.",
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug="product-owner",
+ role=AgentRole.PRODUCT_OWNER,
+ team=Team.BOARD,
+ system_prompt=system_prompt,
+ capabilities=["requirements", "prioritization", "acceptance"],
+ can_notify=True,
+ )
+
+ return ProductOwnerAgent(config)
+
+
+def create_head_marketing(
+ name: str = "Head of Marketing",
+ system_prompt: str | None = None,
+) -> HeadMarketingAgent:
+ """Factory function to create the Head of Marketing agent."""
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ "agents/blueprints/board/head-marketing.md",
+ "You are the Head of Marketing.",
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug="head-marketing",
+ role=AgentRole.HEAD_MARKETING,
+ team=Team.BOARD,
+ system_prompt=system_prompt,
+ capabilities=["marketing", "campaigns", "analytics"],
+ can_notify=True,
+ )
+
+ return HeadMarketingAgent(config)
+
+
+def create_auditor(
+ name: str = "Auditor",
+ system_prompt: str | None = None,
+) -> AuditorAgent:
+ """Factory function to create the Auditor agent."""
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ "agents/blueprints/board/auditor.md",
+ "You are the Auditor - the CEO's silent ally.",
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug="auditor",
+ role=AgentRole.AUDITOR,
+ team=Team.BOARD,
+ system_prompt=system_prompt,
+ capabilities=["observation", "analysis", "audit", "ceo_reporting"],
+ can_notify=True,
+ )
+
+ return AuditorAgent(config)
diff --git a/roboco/agents/factories/cells.py b/roboco/agents/factories/cells.py
new file mode 100644
index 00000000..b3aa33b0
--- /dev/null
+++ b/roboco/agents/factories/cells.py
@@ -0,0 +1,217 @@
+"""
+Cell and Organization Factories
+
+Factory functions for creating complete cells and the full organization.
+"""
+
+from typing import Any
+
+from roboco.agents.factories.board import (
+ create_auditor,
+ create_head_marketing,
+ create_product_owner,
+)
+from roboco.agents.factories.developers import (
+ create_backend_developer,
+ create_frontend_developer,
+ create_ux_developer,
+)
+from roboco.agents.factories.documenters import (
+ create_backend_documenter,
+ create_frontend_documenter,
+ create_ux_documenter,
+)
+from roboco.agents.factories.pms import (
+ create_backend_pm,
+ create_frontend_pm,
+ create_main_pm,
+ create_ux_pm,
+)
+from roboco.agents.factories.qa import (
+ create_backend_qa,
+ create_frontend_qa,
+ create_ux_qa,
+)
+from roboco.models import Team
+from roboco.models.organization import Board, Cell, Organization
+
+
+def create_backend_cell() -> Cell:
+ """
+ Create a complete Backend cell.
+
+ Includes:
+ - 1 PM (BE-PM)
+ - 2 Developers (BE-Dev-1, BE-Dev-2)
+ - 1 QA (BE-QA)
+ - 1 Documenter (BE-Documenter)
+ """
+ return Cell(
+ name="backend-cell",
+ team=Team.BACKEND,
+ pm=create_backend_pm(),
+ developers=[
+ create_backend_developer("BE-Dev-1"),
+ create_backend_developer("BE-Dev-2"),
+ ],
+ qa=create_backend_qa(),
+ documenter=create_backend_documenter(),
+ )
+
+
+def create_frontend_cell() -> Cell:
+ """
+ Create a complete Frontend cell.
+
+ Includes:
+ - 1 PM (FE-PM)
+ - 2 Developers (FE-Dev-1, FE-Dev-2)
+ - 1 QA (FE-QA)
+ - 1 Documenter (FE-Documenter)
+ """
+ return Cell(
+ name="frontend-cell",
+ team=Team.FRONTEND,
+ pm=create_frontend_pm(),
+ developers=[
+ create_frontend_developer("FE-Dev-1"),
+ create_frontend_developer("FE-Dev-2"),
+ ],
+ qa=create_frontend_qa(),
+ documenter=create_frontend_documenter(),
+ )
+
+
+def create_ux_cell() -> Cell:
+ """
+ Create a complete UX/UI cell.
+
+ Includes:
+ - 1 PM (UX-PM)
+ - 1 Developer (UX-Dev)
+ - 1 QA (UX-QA)
+ - 1 Documenter (UX-Documenter)
+ """
+ return Cell(
+ name="uxui-cell",
+ team=Team.UX_UI,
+ pm=create_ux_pm(),
+ developers=[
+ create_ux_developer("UX-Dev"),
+ ],
+ qa=create_ux_qa(),
+ documenter=create_ux_documenter(),
+ )
+
+
+def create_board() -> Board:
+ """
+ Create the Board level.
+
+ Includes:
+ - Product Owner
+ - Head of Marketing
+ - Auditor
+ """
+ return Board(
+ product_owner=create_product_owner(),
+ head_marketing=create_head_marketing(),
+ auditor=create_auditor(),
+ )
+
+
+def create_organization() -> Organization:
+ """
+ Create the complete AI organization.
+
+ Total: 18 AI agents
+ - Board: 3 (Product Owner, Head of Marketing, Auditor)
+ - Management: 1 (Main PM)
+ - Backend Cell: 5 (PM, 2 Devs, QA, Documenter)
+ - Frontend Cell: 5 (PM, 2 Devs, QA, Documenter)
+ - UX/UI Cell: 4 (PM, 1 Dev, QA, Documenter)
+ """
+ return Organization(
+ board=create_board(),
+ main_pm=create_main_pm(),
+ backend_cell=create_backend_cell(),
+ frontend_cell=create_frontend_cell(),
+ ux_cell=create_ux_cell(),
+ )
+
+
+def get_agent_roster() -> dict[str, list[dict[str, Any]]]:
+ """
+ Get a roster of all agents without instantiating them.
+
+ Useful for displaying the org structure.
+ """
+ return {
+ "board": [
+ {"name": "Product Owner", "role": "product_owner", "slug": "product-owner"},
+ {
+ "name": "Head of Marketing",
+ "role": "head_marketing",
+ "slug": "head-marketing",
+ },
+ {"name": "Auditor", "role": "auditor", "slug": "auditor"},
+ ],
+ "management": [
+ {"name": "Main PM", "role": "main_pm", "slug": "main-pm"},
+ ],
+ "backend_cell": [
+ {"name": "BE-PM", "role": "cell_pm", "slug": "be-pm"},
+ {"name": "BE-Dev-1", "role": "developer", "slug": "be-dev-1"},
+ {"name": "BE-Dev-2", "role": "developer", "slug": "be-dev-2"},
+ {"name": "BE-QA", "role": "qa", "slug": "be-qa"},
+ {"name": "BE-Documenter", "role": "documenter", "slug": "be-documenter"},
+ ],
+ "frontend_cell": [
+ {"name": "FE-PM", "role": "cell_pm", "slug": "fe-pm"},
+ {"name": "FE-Dev-1", "role": "developer", "slug": "fe-dev-1"},
+ {"name": "FE-Dev-2", "role": "developer", "slug": "fe-dev-2"},
+ {"name": "FE-QA", "role": "qa", "slug": "fe-qa"},
+ {"name": "FE-Documenter", "role": "documenter", "slug": "fe-documenter"},
+ ],
+ "ux_cell": [
+ {"name": "UX-PM", "role": "cell_pm", "slug": "ux-pm"},
+ {"name": "UX-Dev", "role": "developer", "slug": "ux-dev"},
+ {"name": "UX-QA", "role": "qa", "slug": "ux-qa"},
+ {"name": "UX-Documenter", "role": "documenter", "slug": "ux-documenter"},
+ ],
+ }
+
+
+def print_org_chart() -> str:
+ """Generate a text-based org chart."""
+ return """
+ ┌─────────────┐
+ │ CEO │
+ │ (Human) │
+ └──────┬──────┘
+ │
+ ┌────────────────┼────────────────┐
+ │ │ │
+ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
+ │ Product │ │ Head │ │ Auditor │
+ │ Owner │ │ Marketing │ │ (Spy) │
+ └─────┬─────┘ └─────┬─────┘ └───────────┘
+ │ │ ▲
+ └───────┬────────┘ │
+ │ [observes all]
+ ┌──────▼──────┐
+ │ Main PM │
+ └──────┬──────┘
+ │
+ ┌────────────────────┼────────────────────┐
+ │ │ │
+ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
+ │ BE-PM │ │ FE-PM │ │ UX-PM │
+ ├───────────┤ ├───────────┤ ├───────────┤
+ │ BE-Dev x2 │ │ FE-Dev x2 │ │ UX-Dev │
+ │ BE-QA │ │ FE-QA │ │ UX-QA │
+ │ BE-Doc │ │ FE-Doc │ │ UX-Doc │
+ └───────────┘ └───────────┘ └───────────┘
+
+ Total: 18 AI Agents + 1 Human CEO = 19 organization members
+"""
diff --git a/roboco/agents/factories/developers.py b/roboco/agents/factories/developers.py
new file mode 100644
index 00000000..1ef49772
--- /dev/null
+++ b/roboco/agents/factories/developers.py
@@ -0,0 +1,109 @@
+"""
+Developer Agent Factories
+
+Factory functions for creating developer agents for each team.
+"""
+
+from roboco.agents.developer import DeveloperAgent
+from roboco.agents.factories._base import load_blueprint_prompt, make_slug
+from roboco.models import AgentRole, Team
+from roboco.models.agents import AgentConfig
+
+# Blueprint paths for each team
+_BLUEPRINTS = {
+ Team.BACKEND: "agents/blueprints/backend/be-dev.md",
+ Team.FRONTEND: "agents/blueprints/frontend/fe-dev.md",
+ Team.UX_UI: "agents/blueprints/ux_ui/ux-dev.md",
+}
+
+# Default prompts for each team
+_DEFAULT_PROMPTS = {
+ Team.BACKEND: "You are a backend developer.",
+ Team.FRONTEND: "You are a frontend developer.",
+ Team.UX_UI: "You are a UX/UI developer.",
+}
+
+# Default capabilities for each team (matches blueprint capabilities)
+_CAPABILITIES = {
+ Team.BACKEND: [
+ "code_execution",
+ "git_operations",
+ "file_management",
+ "api_development",
+ "database_design",
+ ],
+ Team.FRONTEND: [
+ "code_execution",
+ "git_operations",
+ "file_management",
+ "browser_testing",
+ "accessibility_testing",
+ "responsive_design",
+ ],
+ Team.UX_UI: [
+ "design_tools",
+ "file_management",
+ "figma_expertise",
+ "prototyping",
+ "design_system_management",
+ "accessibility_design",
+ ],
+}
+
+
+def _create_developer(
+ name: str,
+ team: Team,
+ system_prompt: str | None = None,
+) -> DeveloperAgent:
+ """
+ Internal factory for creating a developer agent.
+
+ Args:
+ name: Agent display name
+ team: Team assignment
+ system_prompt: Optional custom system prompt
+
+ Returns:
+ Configured DeveloperAgent instance
+ """
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ _BLUEPRINTS[team],
+ _DEFAULT_PROMPTS[team],
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug=make_slug(name),
+ role=AgentRole.DEVELOPER,
+ team=team,
+ system_prompt=system_prompt,
+ capabilities=_CAPABILITIES[team],
+ )
+
+ return DeveloperAgent(config)
+
+
+def create_backend_developer(
+ name: str = "BE-Dev-1",
+ system_prompt: str | None = None,
+) -> DeveloperAgent:
+ """Factory function to create a backend developer agent."""
+ return _create_developer(name, Team.BACKEND, system_prompt)
+
+
+def create_frontend_developer(
+ name: str = "FE-Dev-1",
+ system_prompt: str | None = None,
+) -> DeveloperAgent:
+ """Factory function to create a frontend developer agent."""
+ return _create_developer(name, Team.FRONTEND, system_prompt)
+
+
+def create_ux_developer(
+ name: str = "UX-Dev-1",
+ system_prompt: str | None = None,
+) -> DeveloperAgent:
+ """Factory function to create a UX/UI developer agent."""
+ return _create_developer(name, Team.UX_UI, system_prompt)
diff --git a/roboco/agents/factories/documenters.py b/roboco/agents/factories/documenters.py
new file mode 100644
index 00000000..ae68f3fe
--- /dev/null
+++ b/roboco/agents/factories/documenters.py
@@ -0,0 +1,105 @@
+"""
+Documenter Agent Factories
+
+Factory functions for creating documenter agents for each team.
+"""
+
+from roboco.agents.documenter import DocumenterAgent
+from roboco.agents.factories._base import load_blueprint_prompt, make_slug
+from roboco.models import AgentRole, Team
+from roboco.models.agents import AgentConfig
+
+# Blueprint paths for each team
+_BLUEPRINTS = {
+ Team.BACKEND: "agents/blueprints/backend/be-documenter.md",
+ Team.FRONTEND: "agents/blueprints/frontend/fe-documenter.md",
+ Team.UX_UI: "agents/blueprints/ux_ui/ux-documenter.md",
+}
+
+# Default prompts for each team
+_DEFAULT_PROMPTS = {
+ Team.BACKEND: "You are a backend documenter.",
+ Team.FRONTEND: "You are a frontend documenter.",
+ Team.UX_UI: "You are a UX/UI documenter.",
+}
+
+# Default capabilities for each team (matches blueprint capabilities)
+_CAPABILITIES = {
+ Team.BACKEND: [
+ "technical_writing",
+ "api_documentation",
+ "code_reading",
+ "file_management",
+ ],
+ Team.FRONTEND: [
+ "technical_writing",
+ "component_documentation",
+ "code_reading",
+ "storybook",
+ "file_management",
+ ],
+ Team.UX_UI: [
+ "design_documentation",
+ "design_system_maintenance",
+ "technical_writing",
+ "file_management",
+ ],
+}
+
+
+def _create_documenter(
+ name: str,
+ team: Team,
+ system_prompt: str | None = None,
+) -> DocumenterAgent:
+ """
+ Internal factory for creating a documenter agent.
+
+ Args:
+ name: Agent display name
+ team: Team assignment
+ system_prompt: Optional custom system prompt
+
+ Returns:
+ Configured DocumenterAgent instance
+ """
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ _BLUEPRINTS[team],
+ _DEFAULT_PROMPTS[team],
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug=make_slug(name),
+ role=AgentRole.DOCUMENTER,
+ team=team,
+ system_prompt=system_prompt,
+ capabilities=_CAPABILITIES[team],
+ )
+
+ return DocumenterAgent(config)
+
+
+def create_backend_documenter(
+ name: str = "BE-Documenter",
+ system_prompt: str | None = None,
+) -> DocumenterAgent:
+ """Factory function to create a backend documenter agent."""
+ return _create_documenter(name, Team.BACKEND, system_prompt)
+
+
+def create_frontend_documenter(
+ name: str = "FE-Documenter",
+ system_prompt: str | None = None,
+) -> DocumenterAgent:
+ """Factory function to create a frontend documenter agent."""
+ return _create_documenter(name, Team.FRONTEND, system_prompt)
+
+
+def create_ux_documenter(
+ name: str = "UX-Documenter",
+ system_prompt: str | None = None,
+) -> DocumenterAgent:
+ """Factory function to create a UX/UI documenter agent."""
+ return _create_documenter(name, Team.UX_UI, system_prompt)
diff --git a/roboco/agents/factories/pms.py b/roboco/agents/factories/pms.py
new file mode 100644
index 00000000..198e1ee4
--- /dev/null
+++ b/roboco/agents/factories/pms.py
@@ -0,0 +1,107 @@
+"""
+PM Agent Factories
+
+Factory functions for creating PM agents (Cell PMs and Main PM).
+"""
+
+from roboco.agents.factories._base import load_blueprint_prompt, make_slug
+from roboco.agents.pm import CellPMAgent, MainPMAgent
+from roboco.models import AgentRole, Team
+from roboco.models.agents import AgentConfig
+
+# Blueprint paths for cell PMs
+_CELL_PM_BLUEPRINTS = {
+ Team.BACKEND: "agents/blueprints/backend/be-pm.md",
+ Team.FRONTEND: "agents/blueprints/frontend/fe-pm.md",
+ Team.UX_UI: "agents/blueprints/ux_ui/ux-pm.md",
+}
+
+# Default prompts for cell PMs
+_CELL_PM_PROMPTS = {
+ Team.BACKEND: "You are the Backend Cell PM.",
+ Team.FRONTEND: "You are the Frontend Cell PM.",
+ Team.UX_UI: "You are the UX/UI Cell PM.",
+}
+
+
+def _create_cell_pm(
+ name: str,
+ team: Team,
+ system_prompt: str | None = None,
+) -> CellPMAgent:
+ """
+ Internal factory for creating a cell PM agent.
+
+ Args:
+ name: Agent display name
+ team: Team assignment
+ system_prompt: Optional custom system prompt
+
+ Returns:
+ Configured CellPMAgent instance
+ """
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ _CELL_PM_BLUEPRINTS[team],
+ _CELL_PM_PROMPTS[team],
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug=make_slug(name),
+ role=AgentRole.CELL_PM,
+ team=team,
+ system_prompt=system_prompt,
+ capabilities=["task_management", "notifications"],
+ can_notify=True,
+ )
+
+ return CellPMAgent(config)
+
+
+def create_backend_pm(
+ name: str = "BE-PM",
+ system_prompt: str | None = None,
+) -> CellPMAgent:
+ """Factory function to create a backend PM agent."""
+ return _create_cell_pm(name, Team.BACKEND, system_prompt)
+
+
+def create_frontend_pm(
+ name: str = "FE-PM",
+ system_prompt: str | None = None,
+) -> CellPMAgent:
+ """Factory function to create a frontend PM agent."""
+ return _create_cell_pm(name, Team.FRONTEND, system_prompt)
+
+
+def create_ux_pm(
+ name: str = "UX-PM",
+ system_prompt: str | None = None,
+) -> CellPMAgent:
+ """Factory function to create a UX/UI PM agent."""
+ return _create_cell_pm(name, Team.UX_UI, system_prompt)
+
+
+def create_main_pm(
+ name: str = "Main PM",
+ system_prompt: str | None = None,
+) -> MainPMAgent:
+ """Factory function to create the Main PM agent."""
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ "agents/blueprints/board/main-pm.md",
+ "You are the Main PM coordinating all cells.",
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug="main-pm",
+ role=AgentRole.MAIN_PM,
+ team=Team.BOARD,
+ system_prompt=system_prompt,
+ capabilities=["task_management", "notifications", "cross_cell_coordination"],
+ can_notify=True,
+ )
+
+ return MainPMAgent(config)
diff --git a/roboco/agents/factories/qa.py b/roboco/agents/factories/qa.py
new file mode 100644
index 00000000..5ebe4a2e
--- /dev/null
+++ b/roboco/agents/factories/qa.py
@@ -0,0 +1,103 @@
+"""
+QA Agent Factories
+
+Factory functions for creating QA agents for each team.
+"""
+
+from roboco.agents.factories._base import load_blueprint_prompt, make_slug
+from roboco.agents.qa import QAAgent
+from roboco.models import AgentRole, Team
+from roboco.models.agents import AgentConfig
+
+# Blueprint paths for each team
+_BLUEPRINTS = {
+ Team.BACKEND: "agents/blueprints/backend/be-qa.md",
+ Team.FRONTEND: "agents/blueprints/frontend/fe-qa.md",
+ Team.UX_UI: "agents/blueprints/ux_ui/ux-qa.md",
+}
+
+# Default prompts for each team
+_DEFAULT_PROMPTS = {
+ Team.BACKEND: "You are a backend QA engineer.",
+ Team.FRONTEND: "You are a frontend QA engineer.",
+ Team.UX_UI: "You are a UX/UI QA engineer.",
+}
+
+# Default capabilities for each team (matches blueprint capabilities)
+_CAPABILITIES = {
+ Team.BACKEND: [
+ "code_review",
+ "test_execution",
+ "security_analysis",
+ "quality_assurance",
+ ],
+ Team.FRONTEND: [
+ "visual_testing",
+ "accessibility_testing",
+ "browser_testing",
+ "quality_assurance",
+ ],
+ Team.UX_UI: [
+ "design_review",
+ "accessibility_review",
+ "quality_assurance",
+ ],
+}
+
+
+def _create_qa(
+ name: str,
+ team: Team,
+ system_prompt: str | None = None,
+) -> QAAgent:
+ """
+ Internal factory for creating a QA agent.
+
+ Args:
+ name: Agent display name
+ team: Team assignment
+ system_prompt: Optional custom system prompt
+
+ Returns:
+ Configured QAAgent instance
+ """
+ if system_prompt is None:
+ system_prompt = load_blueprint_prompt(
+ _BLUEPRINTS[team],
+ _DEFAULT_PROMPTS[team],
+ )
+
+ config = AgentConfig(
+ name=name,
+ slug=make_slug(name),
+ role=AgentRole.QA,
+ team=team,
+ system_prompt=system_prompt,
+ capabilities=_CAPABILITIES[team],
+ )
+
+ return QAAgent(config)
+
+
+def create_backend_qa(
+ name: str = "BE-QA",
+ system_prompt: str | None = None,
+) -> QAAgent:
+ """Factory function to create a backend QA agent."""
+ return _create_qa(name, Team.BACKEND, system_prompt)
+
+
+def create_frontend_qa(
+ name: str = "FE-QA",
+ system_prompt: str | None = None,
+) -> QAAgent:
+ """Factory function to create a frontend QA agent."""
+ return _create_qa(name, Team.FRONTEND, system_prompt)
+
+
+def create_ux_qa(
+ name: str = "UX-QA",
+ system_prompt: str | None = None,
+) -> QAAgent:
+ """Factory function to create a UX/UI QA agent."""
+ return _create_qa(name, Team.UX_UI, system_prompt)
diff --git a/roboco/agents/factory.py b/roboco/agents/factory.py
new file mode 100644
index 00000000..8e66ad58
--- /dev/null
+++ b/roboco/agents/factory.py
@@ -0,0 +1,59 @@
+"""
+Agent Factory (Backwards Compatibility)
+
+This module re-exports all factories from the new location.
+Use roboco.agents.factories instead for new code.
+"""
+
+# Re-export everything from the new factories package
+from roboco.agents.factories import (
+ create_auditor,
+ create_backend_cell,
+ create_backend_developer,
+ create_backend_documenter,
+ create_backend_pm,
+ create_backend_qa,
+ create_board,
+ create_frontend_cell,
+ create_frontend_developer,
+ create_frontend_documenter,
+ create_frontend_pm,
+ create_frontend_qa,
+ create_head_marketing,
+ create_main_pm,
+ create_organization,
+ create_product_owner,
+ create_ux_cell,
+ create_ux_developer,
+ create_ux_documenter,
+ create_ux_pm,
+ create_ux_qa,
+ get_agent_roster,
+ print_org_chart,
+)
+
+__all__ = [
+ "create_auditor",
+ "create_backend_cell",
+ "create_backend_developer",
+ "create_backend_documenter",
+ "create_backend_pm",
+ "create_backend_qa",
+ "create_board",
+ "create_frontend_cell",
+ "create_frontend_developer",
+ "create_frontend_documenter",
+ "create_frontend_pm",
+ "create_frontend_qa",
+ "create_head_marketing",
+ "create_main_pm",
+ "create_organization",
+ "create_product_owner",
+ "create_ux_cell",
+ "create_ux_developer",
+ "create_ux_documenter",
+ "create_ux_pm",
+ "create_ux_qa",
+ "get_agent_roster",
+ "print_org_chart",
+]
diff --git a/roboco/agents/mixins.py b/roboco/agents/mixins.py
new file mode 100644
index 00000000..2e9655d2
--- /dev/null
+++ b/roboco/agents/mixins.py
@@ -0,0 +1,415 @@
+"""
+Agent Mixins and Abstractions
+
+Reusable components for agent implementations:
+- PhaseEngine: Unified phase dispatch and transitions
+- ContextManager: Context lifecycle management
+"""
+
+import contextlib
+from abc import abstractmethod
+from collections.abc import Awaitable, Callable
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Any
+from uuid import UUID
+
+# =============================================================================
+# PHASE ENGINE
+# =============================================================================
+
+
+@dataclass
+class PhaseConfig[PhaseT: Enum]:
+ """
+ Configuration for a phase in the workflow.
+
+ Attributes:
+ phase: The phase enum value
+ handler: Async function to execute for this phase
+ next_phase: Phase to transition to after completion (None = terminal)
+ fail_phase: Phase to transition to if handler returns False (optional)
+ requires_completion: If True, handler must return True to advance
+ """
+
+ phase: PhaseT
+ handler: Callable[..., Awaitable[bool | None]]
+ next_phase: PhaseT | None = None
+ fail_phase: PhaseT | None = None
+ requires_completion: bool = False
+
+
+@dataclass
+class PhaseResult:
+ """Result of running a phase."""
+
+ completed: bool = False # True if workflow complete
+ advanced: bool = False # True if phase advanced
+ error: str | None = None
+
+
+class PhaseEngine[PhaseT: Enum, ContextT]:
+ """
+ Mixin for phase-based workflow execution.
+
+ Provides unified phase dispatch and transition logic that can be
+ configured per agent type. Replaces duplicate _dispatch_phase and
+ _run_phase implementations across agents.
+
+ Usage:
+ class MyAgent(Agent, PhaseEngine[MyPhase, MyContext]):
+ def _get_phase_configs(self) -> list[PhaseConfig[MyPhase]]:
+ return [
+ PhaseConfig(MyPhase.START, self._phase_start, MyPhase.WORK),
+ PhaseConfig(MyPhase.WORK, self._phase_work, MyPhase.END,
+ requires_completion=True),
+ PhaseConfig(MyPhase.END, self._phase_end, None),
+ ]
+ """
+
+ @abstractmethod
+ def _get_phase_configs(self) -> list[PhaseConfig[PhaseT]]:
+ """
+ Define the phase workflow configuration.
+
+ Returns:
+ List of PhaseConfig defining handlers and transitions.
+ """
+ ...
+
+ @abstractmethod
+ def _get_current_phase(self, ctx: ContextT) -> PhaseT:
+ """Get the current phase from context."""
+ ...
+
+ @abstractmethod
+ def _set_current_phase(self, ctx: ContextT, phase: PhaseT) -> None:
+ """Set the current phase in context."""
+ ...
+
+ async def _run_phase_engine(self, ctx: ContextT) -> PhaseResult:
+ """
+ Execute the current phase and handle transitions.
+
+ Args:
+ ctx: The workflow context
+
+ Returns:
+ PhaseResult indicating completion status
+ """
+ configs = {cfg.phase: cfg for cfg in self._get_phase_configs()}
+ current_phase = self._get_current_phase(ctx)
+
+ config = configs.get(current_phase)
+ if not config:
+ return PhaseResult(error=f"No config for phase: {current_phase}")
+
+ try:
+ # Execute handler
+ result = await config.handler(ctx)
+
+ # Terminal phase check
+ if config.next_phase is None:
+ return PhaseResult(completed=True)
+
+ # Check if we should advance
+ should_advance = True
+ if config.requires_completion:
+ should_advance = result is True
+
+ if should_advance:
+ self._set_current_phase(ctx, config.next_phase)
+ return PhaseResult(advanced=True)
+
+ # Handle failure transition (e.g., VERIFY fails → back to EXECUTE)
+ if config.fail_phase is not None and result is False:
+ self._set_current_phase(ctx, config.fail_phase)
+ return PhaseResult(advanced=True)
+
+ return PhaseResult()
+
+ except Exception as e:
+ return PhaseResult(error=str(e))
+
+
+# =============================================================================
+# CYCLIC PHASE RUNNER (for continuous-duty agents like PM/Board)
+# =============================================================================
+
+
+@dataclass
+class CyclicPhaseConfig[PhaseT: Enum]:
+ """
+ Configuration for a phase in a continuous cycle.
+
+ Unlike PhaseConfig, this is for agents that cycle forever
+ (PM, Board agents) rather than completing tasks.
+ """
+
+ phase: PhaseT
+ handler: Callable[..., Awaitable[None]]
+ next_phase: PhaseT
+
+
+class CyclicPhaseRunner[PhaseT: Enum]:
+ """
+ Mixin for continuous-duty agents that cycle through phases.
+
+ Unlike PhaseEngine which handles task completion, this is for
+ agents like PM and Board that run continuously.
+
+ Usage:
+ class CellPMAgent(Agent, CyclicPhaseRunner[CellPMPhase]):
+ _current_phase: CellPMPhase = CellPMPhase.MONITOR
+
+ def _get_cyclic_phase_configs(self) -> list[CyclicPhaseConfig]:
+ return [
+ CyclicPhaseConfig(CellPMPhase.MONITOR, self._phase_monitor,
+ CellPMPhase.TRIAGE),
+ # ... etc
+ ]
+ """
+
+ _current_phase: PhaseT
+
+ @abstractmethod
+ def _get_cyclic_phase_configs(self) -> list[CyclicPhaseConfig[PhaseT]]:
+ """Define the cyclic phase workflow."""
+ ...
+
+ async def _run_phase_cycle(self) -> str | None:
+ """
+ Execute the current phase and advance to next.
+
+ Returns error message if any, None on success.
+ """
+ configs = {cfg.phase: cfg for cfg in self._get_cyclic_phase_configs()}
+
+ config = configs.get(self._current_phase)
+ if not config:
+ return f"No config for phase: {self._current_phase}"
+
+ try:
+ await config.handler()
+ self._current_phase = config.next_phase
+ return None
+ except Exception as e:
+ return str(e)
+
+
+# =============================================================================
+# CONTEXT MANAGER
+# =============================================================================
+
+
+@dataclass
+class BaseContext:
+ """Base context with common fields."""
+
+ task_id: UUID
+ title: str = ""
+ notes: list[str] = field(default_factory=list)
+
+
+class ContextManager[ContextT]:
+ """
+ Mixin for managing workflow context lifecycle.
+
+ Handles:
+ - Context initialization/restoration
+ - Context cleanup on completion
+ - Type-safe context access
+
+ Usage:
+ class MyAgent(Agent, ContextManager[MyContext]):
+ _context: MyContext | None = None
+
+ def _create_context(self, task_id: UUID, title: str) -> MyContext:
+ return MyContext(task_id=task_id, title=title)
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ ctx = await self._ensure_context(task_id)
+ # ... work with ctx
+ """
+
+ _context: ContextT | None
+
+ @abstractmethod
+ def _create_context(self, task_id: UUID, title: str) -> ContextT:
+ """Create a new context instance."""
+ ...
+
+ @abstractmethod
+ def _get_context_task_id(self, ctx: ContextT) -> UUID:
+ """Get the task ID from a context."""
+ ...
+
+ @abstractmethod
+ async def _get_task_title(self, task_id: UUID) -> str:
+ """Get task title from API (implemented in base Agent)."""
+ ...
+
+ async def _ensure_context(self, task_id: UUID) -> ContextT:
+ """
+ Ensure context exists for the given task.
+
+ Creates new context if none exists or if task ID changed.
+ """
+ if self._context is None or self._get_context_task_id(self._context) != task_id:
+ title = await self._get_task_title(task_id)
+ self._context = self._create_context(task_id, title)
+ return self._context
+
+ def _clear_context(self) -> None:
+ """Clear the current context."""
+ self._context = None
+
+ @property
+ def context(self) -> ContextT | None:
+ """Get the current context (may be None)."""
+ return self._context
+
+ def require_context(self) -> ContextT:
+ """Get context, raising if None."""
+ if self._context is None:
+ raise RuntimeError("No active context")
+ return self._context
+
+
+# =============================================================================
+# WORK FINDER
+# =============================================================================
+
+
+@dataclass
+class WorkSearchStrategy:
+ """
+ A strategy for finding work.
+
+ Attributes:
+ name: Descriptive name for logging
+ finder: Async function that returns task ID or None
+ priority: Lower = higher priority
+ """
+
+ name: str
+ finder: Callable[[], Awaitable[UUID | None]]
+ priority: int = 0
+
+
+class WorkFinder:
+ """
+ Mixin for finding work with prioritized strategies.
+
+ Replaces duplicate find_work implementations with a configurable
+ search strategy pattern.
+
+ Usage:
+ class MyAgent(Agent, WorkFinder):
+ def _get_work_strategies(self) -> list[WorkSearchStrategy]:
+ return [
+ WorkSearchStrategy("paused", self._find_paused, priority=0),
+ WorkSearchStrategy("assigned", self._find_assigned, priority=1),
+ ]
+ """
+
+ _pending_work: list[UUID]
+
+ @abstractmethod
+ def _get_work_strategies(self) -> list[WorkSearchStrategy]:
+ """Define work search strategies in priority order."""
+ ...
+
+ async def _find_work_prioritized(self) -> UUID | None:
+ """
+ Find work using prioritized strategies.
+
+ First checks pending work queue, then tries each strategy
+ in priority order.
+ """
+ # Check pending queue first
+ if hasattr(self, "_pending_work") and self._pending_work:
+ return self._pending_work.pop(0)
+
+ # Try strategies in priority order
+ strategies = sorted(self._get_work_strategies(), key=lambda s: s.priority)
+ for strategy in strategies:
+ task_id = await strategy.finder()
+ if task_id:
+ return task_id
+
+ return None
+
+
+# =============================================================================
+# PROGRESS TRACKER
+# =============================================================================
+
+
+@dataclass
+class ProgressUpdate:
+ """A progress update for a task."""
+
+ message: str
+ percentage: int
+ details: dict[str, Any] = field(default_factory=dict)
+
+
+class ProgressTracker:
+ """
+ Mixin for tracking and reporting progress.
+
+ Provides unified progress reporting that saves to task AND
+ sends channel messages.
+ """
+
+ @abstractmethod
+ async def _api_call(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
+ """Make API call (implemented in base Agent)."""
+ ...
+
+ @abstractmethod
+ async def send_message(
+ self,
+ session_id: UUID | None,
+ content: str,
+ message_type: str,
+ task_id: UUID | None = None,
+ ) -> None:
+ """Send message to session (implemented in base Agent)."""
+ ...
+
+ async def _report_progress(
+ self,
+ task_id: UUID,
+ session_id: UUID | None,
+ message: str,
+ percentage: int,
+ ) -> None:
+ """
+ Report progress for a task.
+
+ Saves to task record AND sends message to session.
+
+ Args:
+ task_id: Task to update
+ session_id: Session to notify (from context)
+ message: Progress message
+ percentage: Completion percentage (0-100)
+ """
+ # Save to task (suppress errors - logged by _api_call)
+ with contextlib.suppress(Exception):
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/progress",
+ json={"message": message, "percentage": percentage},
+ )
+
+ # Send session message
+ task_ref = str(task_id)[:8]
+ await self.send_message(
+ session_id,
+ f"TASK-{task_ref} ({percentage}%) {message}",
+ message_type="action",
+ task_id=task_id,
+ )
diff --git a/roboco/agents/orchestrator.py b/roboco/agents/orchestrator.py
new file mode 100644
index 00000000..c9be2846
--- /dev/null
+++ b/roboco/agents/orchestrator.py
@@ -0,0 +1,387 @@
+"""
+Agent Orchestrator
+
+Manages the lifecycle of all agents in the system.
+Handles spawning, monitoring, and coordination.
+"""
+
+import asyncio
+import contextlib
+from collections.abc import Callable
+from datetime import UTC, datetime
+from typing import Any
+from uuid import UUID
+
+import structlog
+from roboco.agents.base import Agent
+from roboco.models import AgentRole, AgentStatus, Team
+
+logger = structlog.get_logger()
+
+
+class Orchestrator:
+ """
+ Central orchestrator for all RoboCo agents.
+
+ Responsibilities:
+ - Spawn and stop agents
+ - Monitor agent health
+ - Route messages between agents
+ - Handle agent failures and restarts
+ """
+
+ def __init__(self) -> None:
+ """Initialize the orchestrator."""
+ self._agents: dict[UUID, Agent] = {}
+ self._agent_tasks: dict[UUID, asyncio.Task] = {}
+ self._running = False
+ self._monitor_task: asyncio.Task | None = None
+
+ self.log = logger.bind(component="orchestrator")
+
+ @property
+ def agents(self) -> dict[UUID, Agent]:
+ """Get all registered agents."""
+ return self._agents.copy()
+
+ @property
+ def active_agents(self) -> list[Agent]:
+ """Get all active agents."""
+ return [
+ a for a in self._agents.values() if a.state.status == AgentStatus.ACTIVE
+ ]
+
+ @property
+ def idle_agents(self) -> list[Agent]:
+ """Get all idle agents."""
+ return [a for a in self._agents.values() if a.state.status == AgentStatus.IDLE]
+
+ # =========================================================================
+ # LIFECYCLE
+ # =========================================================================
+
+ async def start(self) -> None:
+ """Start the orchestrator."""
+ if self._running:
+ self.log.warning("Orchestrator already running")
+ return
+
+ self.log.info("Starting orchestrator")
+ self._running = True
+
+ # Start health monitor
+ self._monitor_task = asyncio.create_task(self._health_monitor())
+
+ async def stop(self) -> None:
+ """Stop the orchestrator and all agents."""
+ if not self._running:
+ return
+
+ self.log.info("Stopping orchestrator")
+ self._running = False
+
+ # Stop health monitor
+ if self._monitor_task:
+ self._monitor_task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await self._monitor_task
+
+ # Stop all agents
+ await self.stop_all_agents()
+
+ self.log.info("Orchestrator stopped")
+
+ # =========================================================================
+ # AGENT MANAGEMENT
+ # =========================================================================
+
+ def register_agent(self, agent: Agent) -> None:
+ """
+ Register an agent with the orchestrator.
+
+ Args:
+ agent: The agent to register
+ """
+ if agent.id in self._agents:
+ self.log.warning("Agent already registered", agent_id=str(agent.id))
+ return
+
+ self._agents[agent.id] = agent
+ self.log.info(
+ "Agent registered",
+ agent_id=str(agent.id),
+ agent_name=agent.name,
+ agent_role=agent.role.value,
+ )
+
+ def unregister_agent(self, agent_id: UUID) -> None:
+ """
+ Unregister an agent.
+
+ Args:
+ agent_id: ID of the agent to unregister
+ """
+ if agent_id not in self._agents:
+ return
+
+ agent = self._agents.pop(agent_id)
+ self.log.info(
+ "Agent unregistered", agent_id=str(agent_id), agent_name=agent.name
+ )
+
+ async def spawn_agent(self, agent: Agent) -> None:
+ """
+ Spawn an agent (register and start).
+
+ Args:
+ agent: The agent to spawn
+ """
+ self.register_agent(agent)
+ await agent.start()
+ self.log.info("Agent spawned", agent_id=str(agent.id), agent_name=agent.name)
+
+ async def stop_agent(self, agent_id: UUID) -> None:
+ """
+ Stop a specific agent.
+
+ Args:
+ agent_id: ID of the agent to stop
+ """
+ if agent_id not in self._agents:
+ self.log.warning("Agent not found", agent_id=str(agent_id))
+ return
+
+ agent = self._agents[agent_id]
+ await agent.stop()
+ self.log.info("Agent stopped", agent_id=str(agent_id), agent_name=agent.name)
+
+ async def stop_all_agents(self) -> None:
+ """Stop all registered agents."""
+ self.log.info("Stopping all agents", count=len(self._agents))
+
+ # Stop all agents concurrently
+ await asyncio.gather(
+ *[agent.stop() for agent in self._agents.values()],
+ return_exceptions=True,
+ )
+
+ async def restart_agent(self, agent_id: UUID) -> None:
+ """
+ Restart an agent.
+
+ Args:
+ agent_id: ID of the agent to restart
+ """
+ if agent_id not in self._agents:
+ self.log.warning("Agent not found", agent_id=str(agent_id))
+ return
+
+ agent = self._agents[agent_id]
+ self.log.info("Restarting agent", agent_id=str(agent_id), agent_name=agent.name)
+
+ await agent.stop()
+ await asyncio.sleep(1) # Brief pause
+ await agent.start()
+
+ # =========================================================================
+ # QUERYING
+ # =========================================================================
+
+ def get_agent(self, agent_id: UUID) -> Agent | None:
+ """Get an agent by ID."""
+ return self._agents.get(agent_id)
+
+ def get_agent_by_slug(self, slug: str) -> Agent | None:
+ """Get an agent by slug."""
+ for agent in self._agents.values():
+ if agent.config.slug == slug:
+ return agent
+ return None
+
+ def get_agents_by_role(self, role: AgentRole) -> list[Agent]:
+ """Get all agents with a specific role."""
+ return [a for a in self._agents.values() if a.role == role]
+
+ def get_agents_by_team(self, team: Team) -> list[Agent]:
+ """Get all agents in a specific team."""
+ return [a for a in self._agents.values() if a.team == team]
+
+ def get_cell_agents(self, team: Team) -> dict[str, list[Agent]]:
+ """
+ Get agents organized by role for a team/cell.
+
+ Returns:
+ Dict with keys: developers, qa, pm, documenter
+ """
+ team_agents = self.get_agents_by_team(team)
+ return {
+ "developers": [a for a in team_agents if a.role == AgentRole.DEVELOPER],
+ "qa": [a for a in team_agents if a.role == AgentRole.QA],
+ "pm": [a for a in team_agents if a.role == AgentRole.CELL_PM],
+ "documenter": [a for a in team_agents if a.role == AgentRole.DOCUMENTER],
+ }
+
+ # =========================================================================
+ # HEALTH MONITORING
+ # =========================================================================
+
+ async def _health_monitor(self) -> None:
+ """
+ Monitor agent health periodically.
+
+ Checks for:
+ - Unresponsive agents
+ - Agents with errors
+ - Agents that need restart
+ """
+ while self._running:
+ try:
+ await self._check_agent_health()
+ await asyncio.sleep(30) # Check every 30 seconds
+ except asyncio.CancelledError:
+ break
+ except Exception as e:
+ self.log.error("Error in health monitor", error=str(e))
+ await asyncio.sleep(5)
+
+ async def _check_agent_health(self) -> None:
+ """Check health of all agents."""
+ now = datetime.now(UTC)
+
+ for agent in self._agents.values():
+ # Check for errors
+ if agent.state.error:
+ self.log.warning(
+ "Agent has error",
+ agent_id=str(agent.id),
+ error=agent.state.error,
+ )
+
+ # Check for inactivity (5 minutes)
+ minutes_in_seconds = 300
+ if agent.state.last_activity:
+ inactive_seconds = (now - agent.state.last_activity).total_seconds()
+ if inactive_seconds > minutes_in_seconds and agent.is_running:
+ self.log.warning(
+ "Agent inactive",
+ agent_id=str(agent.id),
+ inactive_seconds=inactive_seconds,
+ )
+
+ def get_health_status(self) -> dict[str, Any]:
+ """
+ Get overall health status of all agents.
+
+ Returns:
+ Health status summary
+ """
+ total = len(self._agents)
+ by_status: dict[str, int] = {}
+ errors: list[dict[str, str]] = []
+
+ for agent in self._agents.values():
+ status = agent.state.status.value
+ by_status[status] = by_status.get(status, 0) + 1
+
+ if agent.state.error:
+ errors.append(
+ {
+ "agent_id": str(agent.id),
+ "agent_name": agent.name,
+ "error": agent.state.error,
+ }
+ )
+
+ return {
+ "total_agents": total,
+ "by_status": by_status,
+ "errors": errors,
+ "healthy": len(errors) == 0,
+ }
+
+ # =========================================================================
+ # CELL MANAGEMENT
+ # =========================================================================
+
+ async def spawn_cell(
+ self,
+ team: Team,
+ agent_factory: Callable[[Team], list[Agent]],
+ ) -> list[Agent]:
+ """
+ Spawn all agents for a cell.
+
+ Args:
+ team: The team/cell to spawn
+ agent_factory: Factory function that creates agents for the team
+
+ Returns:
+ List of spawned agents
+ """
+ self.log.info("Spawning cell", team=team.value)
+
+ agents = agent_factory(team)
+ for agent in agents:
+ await self.spawn_agent(agent)
+
+ return agents
+
+ async def stop_cell(self, team: Team) -> None:
+ """
+ Stop all agents in a cell.
+
+ Args:
+ team: The team/cell to stop
+ """
+ self.log.info("Stopping cell", team=team.value)
+
+ team_agents = self.get_agents_by_team(team)
+ await asyncio.gather(
+ *[self.stop_agent(a.id) for a in team_agents],
+ return_exceptions=True,
+ )
+
+ # =========================================================================
+ # SERIALIZATION
+ # =========================================================================
+
+ def to_dict(self) -> dict[str, Any]:
+ """Get orchestrator status as dictionary."""
+ return {
+ "running": self._running,
+ "total_agents": len(self._agents),
+ "agents": [a.to_dict() for a in self._agents.values()],
+ "health": self.get_health_status(),
+ }
+
+
+# =============================================================================
+# SINGLETON HOLDER
+# =============================================================================
+
+
+class _OrchestratorHolder:
+ """Holder class for singleton orchestrator instance."""
+
+ instance: Orchestrator | None = None
+
+
+def get_orchestrator() -> Orchestrator:
+ """Get or create the global orchestrator instance."""
+ if _OrchestratorHolder.instance is None:
+ _OrchestratorHolder.instance = Orchestrator()
+ return _OrchestratorHolder.instance
+
+
+async def start_orchestrator() -> Orchestrator:
+ """Start the global orchestrator."""
+ orchestrator = get_orchestrator()
+ await orchestrator.start()
+ return orchestrator
+
+
+async def stop_orchestrator() -> None:
+ """Stop the global orchestrator."""
+ if _OrchestratorHolder.instance:
+ await _OrchestratorHolder.instance.stop()
+ _OrchestratorHolder.instance = None
diff --git a/roboco/agents/pm.py b/roboco/agents/pm.py
new file mode 100644
index 00000000..955d9128
--- /dev/null
+++ b/roboco/agents/pm.py
@@ -0,0 +1,1537 @@
+"""
+PM Agents (Cell PM and Main PM)
+
+Implementation of PM workflows from the blueprint.
+Cell PM:
+ MONITOR → TRIAGE → ASSIGN → FACILITATE → ESCALATE → TRACK → REPORT
+Main PM:
+ OVERSEE → RECEIVE → PRIORITIZE → COORDINATE → DISTRIBUTE → REPORT UP → FACILITATE
+"""
+
+from typing import Any
+from uuid import UUID
+
+import structlog
+from roboco.agents.base import Agent, AgentConfig
+from roboco.agents.mixins import CyclicPhaseConfig, CyclicPhaseRunner
+from roboco.models import NotificationType, TaskStatus
+from roboco.models.agents import (
+ CellPMPhase,
+ CellStatus,
+ Escalation,
+ MainPMPhase,
+ TaskAssignment,
+)
+
+logger = structlog.get_logger()
+
+
+class CellPMAgent(Agent, CyclicPhaseRunner[CellPMPhase]):
+ """
+ Cell PM agent that manages a single cell (Backend, Frontend, or UX/UI).
+
+ Workflow:
+ 1. MONITOR - Watch cell channel, track tasks, check health
+ 2. TRIAGE - Assess new tasks, prioritize
+ 3. ASSIGN - Match tasks to devs
+ 4. FACILITATE - Answer questions, remove blockers
+ 5. ESCALATE - Escalate beyond cell's control to Main PM
+ 6. TRACK - Monitor progress, update estimates
+ 7. REPORT - Status to Main PM
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize Cell PM agent."""
+ super().__init__(config)
+ self._current_phase = CellPMPhase.MONITOR
+ self._cell_status = CellStatus(name=self.cell_name)
+ self._pending_tasks: list[UUID] = []
+ self._pending_escalations: list[Escalation] = []
+ self._cell_channel_id: UUID | None = None
+
+ async def _initialize(self) -> None:
+ """Initialize Cell PM-specific resources."""
+ self.log.debug("Cell PM agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup Cell PM-specific resources."""
+ self._pending_tasks.clear()
+ self._pending_escalations.clear()
+ self.log.debug("Cell PM agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # CYCLIC PHASE RUNNER IMPLEMENTATION
+ # =========================================================================
+
+ def _get_cyclic_phase_configs(self) -> list[CyclicPhaseConfig[CellPMPhase]]:
+ """Define the Cell PM workflow phases."""
+ return [
+ CyclicPhaseConfig(
+ CellPMPhase.MONITOR,
+ self._phase_monitor,
+ CellPMPhase.TRIAGE,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.TRIAGE,
+ self._phase_triage,
+ CellPMPhase.ASSIGN,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.ASSIGN,
+ self._phase_assign,
+ CellPMPhase.FACILITATE,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.FACILITATE,
+ self._phase_facilitate,
+ CellPMPhase.ESCALATE,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.ESCALATE,
+ self._phase_escalate,
+ CellPMPhase.TRACK,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.TRACK,
+ self._phase_track,
+ CellPMPhase.REPORT,
+ ),
+ CyclicPhaseConfig(
+ CellPMPhase.REPORT,
+ self._phase_report,
+ CellPMPhase.MONITOR, # Cycle back
+ ),
+ ]
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """
+ Find work for the PM.
+
+ Priority:
+ 1. Paused tasks with all subtasks complete (ready for closure)
+ 2. Assigned tasks in progress
+ 3. Fall back to cyclic management duties (self.id)
+ """
+ # Check for paused tasks ready for closure
+ ready_task = await self._find_paused_task_ready_for_closure()
+ if ready_task:
+ self.log.info(
+ "Found paused task ready for closure", task_id=str(ready_task)
+ )
+ return ready_task
+
+ # Check for assigned in-progress tasks
+ assigned_task = await self._find_assigned_task()
+ if assigned_task:
+ self.log.info("Found assigned task", task_id=str(assigned_task))
+ return assigned_task
+
+ # Fall back to cyclic management duties
+ return self.id
+
+ async def _find_paused_task_ready_for_closure(self) -> UUID | None:
+ """
+ Find own paused tasks where all subtasks are complete.
+
+ This is the key PM workflow: delegate subtasks, pause, get respawned
+ when subtasks complete, then review and close.
+ """
+ try:
+ # Get my paused tasks
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "paused", "assigned_to": str(self.id)},
+ )
+ paused_tasks = (
+ result.get("items", result) if isinstance(result, dict) else result
+ )
+
+ for task in paused_tasks:
+ task_id = task.get("id")
+ if not task_id:
+ continue
+
+ # Check if this task has subtasks
+ subtasks_result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"parent_task_id": task_id},
+ )
+ subtasks = (
+ subtasks_result.get("items", subtasks_result)
+ if isinstance(subtasks_result, dict)
+ else subtasks_result
+ )
+
+ if not subtasks:
+ continue # No subtasks - not a delegation task
+
+ # Check if ALL subtasks are complete
+ all_complete = all(
+ s.get("status") in ("completed", "cancelled") for s in subtasks
+ )
+
+ if all_complete:
+ return UUID(task_id) if isinstance(task_id, str) else task_id
+
+ return None
+
+ except Exception as e:
+ self.log.warning(
+ "Failed to find paused tasks ready for closure", error=str(e)
+ )
+ return None
+
+ async def _find_assigned_task(self) -> UUID | None:
+ """Find tasks assigned to this PM that are in progress."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "in_progress", "assigned_to": str(self.id)},
+ )
+ tasks = result.get("items", result) if isinstance(result, dict) else result
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find assigned task", error=str(e))
+ return None
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute PM work.
+
+ Two modes:
+ 1. task_id == self.id: Run cyclic management duties
+ 2. task_id is real task: Work on specific task (CLAIM → PLAN → START → ...)
+
+ Returns True when task-specific work is complete, False for cyclic duties.
+ """
+ # Cyclic management duties (no specific task)
+ if task_id == self.id:
+ error = await self._run_phase_cycle()
+ if error:
+ self.log.error(
+ "Error in PM phase", phase=self._current_phase.value, error=error
+ )
+ return False # Never complete - continuous duty
+
+ # Task-specific work - delegate to task workflow
+ return await self._execute_pm_task(task_id)
+
+ async def _execute_pm_task(self, task_id: UUID) -> bool:
+ """
+ Execute PM workflow on a specific task.
+
+ PM Workflow: CLAIM → PLAN → START → EXECUTE (delegate) → PAUSE → COMPLETE
+
+ Returns True when PM work is done (delegated or completed).
+ """
+ try:
+ task = await self._api_call("GET", f"/tasks/{task_id}")
+ status = task.get("status")
+ return await self._handle_pm_task_status(task_id, task, status)
+ except Exception as e:
+ self.log.error(
+ "Error in PM task execution", task_id=str(task_id), error=str(e)
+ )
+ return False
+
+ async def _handle_pm_task_status(
+ self, task_id: UUID, task: dict[str, Any], status: str | None
+ ) -> bool:
+ """Handle PM task based on current status."""
+ if status == "pending":
+ await self._mark_claimed(task_id)
+ self.log.info("PM claimed task", task_id=str(task_id))
+ return False
+
+ if status == "claimed":
+ return await self._handle_claimed_task(task_id, task)
+
+ if status == "in_progress":
+ return await self._handle_in_progress_task(task_id, task)
+
+ if status == "paused":
+ await self._mark_completed(task_id)
+ self.log.info("PM completed task", task_id=str(task_id))
+ return True
+
+ return False
+
+ async def _handle_claimed_task(self, task_id: UUID, task: dict[str, Any]) -> bool:
+ """Handle claimed task - plan then start."""
+ if not task.get("plan"):
+ plan = await self._create_pm_plan(task)
+ await self._api_call("PATCH", f"/tasks/{task_id}", json={"plan": plan})
+ self.log.info("PM planned task", task_id=str(task_id))
+ return False
+
+ await self._mark_in_progress(task_id)
+ self.log.info("PM started task", task_id=str(task_id))
+ return False
+
+ async def _handle_in_progress_task(
+ self, task_id: UUID, task: dict[str, Any]
+ ) -> bool:
+ """Handle in_progress task - delegate and pause."""
+ delegated = await self._delegate_task(task_id, task)
+ if delegated:
+ remaining = ["Review subtask completions", "Close task"]
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/pause",
+ json={
+ "reason": "Awaiting subtask completion",
+ "checkpoint_summary": "Delegated to cell agents",
+ "remaining_work": remaining,
+ },
+ )
+ self.log.info("PM delegated and paused", task_id=str(task_id))
+ return True
+
+ async def _create_pm_plan(self, task: dict[str, Any]) -> dict[str, Any]:
+ """Create a PM triage plan for a task."""
+ return {
+ "approach": "Triage and delegate to cell developers",
+ "steps": [
+ "Analyze requirements",
+ "Identify subtasks",
+ "Assign to available developers",
+ "Create work session",
+ "Monitor progress",
+ ],
+ "risks": task.get("acceptance_criteria", [])[:2],
+ "estimated_sessions": 1,
+ }
+
+ async def _delegate_task(self, task_id: UUID, task: dict[str, Any]) -> bool:
+ """Delegate task by creating subtasks for developers.
+
+ Follows blueprint workflow:
+ CREATE (backlog) → SESSION → ACTIVATE (pending) → NOTIFY
+ """
+ # Find available developer
+ best_dev = await self._find_best_dev(task_id)
+ if not best_dev:
+ self.log.warning("No available developer for task", task_id=str(task_id))
+ return False
+
+ team = self.team.value if self.team else "backend"
+
+ # Step 1: Create subtask with status "backlog" (prevents premature pickup)
+ subtask_resp = await self._api_call(
+ "POST",
+ "/tasks",
+ json={
+ "title": f"Implement: {task.get('title', 'Task')}",
+ "description": task.get("description", ""),
+ "team": team,
+ "acceptance_criteria": task.get("acceptance_criteria", []),
+ "parent_task_id": str(task_id),
+ "assigned_to": str(best_dev.agent_id),
+ "status": "backlog", # Backlog until session is ready
+ },
+ )
+ subtask_id = subtask_resp.get("id")
+ if not subtask_id:
+ self.log.error("Failed to create subtask", parent_task_id=str(task_id))
+ return False
+
+ self.log.info(
+ "PM created subtask (backlog)",
+ subtask_id=subtask_id,
+ parent_task_id=str(task_id),
+ assigned_to=str(best_dev.agent_id),
+ )
+
+ # Step 2: Create session for the subtask
+ channel_slug = self._get_team_channel(team)
+ try:
+ await self._api_call(
+ "POST",
+ "/sessions/for-tasks",
+ json={
+ "task_ids": [subtask_id],
+ "channel_slug": channel_slug,
+ "scope": f"Work session for {task.get('title', 'task')}",
+ "relationship_type": "implements",
+ },
+ )
+ self.log.info("Session created for subtask", subtask_id=subtask_id)
+ except Exception as e:
+ self.log.warning(
+ "Session creation failed, activating anyway",
+ subtask_id=subtask_id,
+ error=str(e),
+ )
+
+ # Step 3: Activate subtask (changes status to pending)
+ try:
+ await self._api_call("POST", f"/tasks/{subtask_id}/activate")
+ self.log.info("Subtask activated", subtask_id=subtask_id)
+ except Exception as e:
+ self.log.error(
+ "Failed to activate subtask",
+ subtask_id=subtask_id,
+ error=str(e),
+ )
+ return False
+
+ # Step 4: Notify assigned developer
+ try:
+ await self._notify_developer(best_dev, subtask_id, task)
+ except Exception as e:
+ self.log.warning(
+ "Failed to notify developer (task still assigned)",
+ error=str(e),
+ )
+
+ return True
+
+ def _get_team_channel(self, team: str) -> str:
+ """Get the channel slug for a team."""
+ channel_map = {
+ "backend": "backend-cell",
+ "frontend": "frontend-cell",
+ "ux_ui": "uxui-cell",
+ }
+ return channel_map.get(team, "backend-cell")
+
+ async def _notify_developer(
+ self, dev: Any, subtask_id: str, task: dict[str, Any]
+ ) -> None:
+ """Notify developer of new task assignment."""
+ await self._api_call(
+ "POST",
+ "/notifications",
+ json={
+ "type": "task_assigned",
+ "priority": "normal",
+ "to_agents": [str(dev.agent_id)],
+ "subject": f"New task: {task.get('title', 'Task')}",
+ "body": (
+ f"You've been assigned a new task.\n\n"
+ f"Task ID: {subtask_id}\n"
+ f"Title: {task.get('title', 'Task')}\n\n"
+ f"Use roboco_task_scan to find and claim it."
+ ),
+ "related_task_id": subtask_id,
+ "requires_ack": False,
+ },
+ )
+
+ # =========================================================================
+ # CELL PM PHASES
+ # =========================================================================
+
+ async def _phase_monitor(self) -> None:
+ """
+ MONITOR phase: Watch cell health.
+
+ - Watch cell channel
+ - Track active tasks
+ - Check for blockers
+ """
+ self.log.debug("MONITOR phase")
+
+ # Update cell status
+ self._cell_status.active_tasks = await self._count_active_tasks()
+ self._cell_status.blocked_tasks = await self._count_blocked_tasks()
+ self._cell_status.available_devs = await self._count_available_devs()
+
+ # Check for concerning patterns
+ if self._cell_status.blocked_tasks > 0:
+ self._cell_status.concerns.append(
+ f"{self._cell_status.blocked_tasks} blocked tasks"
+ )
+
+ async def _phase_triage(self) -> None:
+ """
+ TRIAGE phase: Assess and prioritize new tasks.
+ """
+ self.log.debug("TRIAGE phase")
+
+ # Get unassigned tasks
+ new_tasks = await self._get_unassigned_tasks()
+
+ for task_id in new_tasks:
+ # Use TOON for token-efficient context encoding
+ triage_context = self.format_context_labeled(
+ "Task Triage",
+ {"task_id": str(task_id), "cell": self.cell_name},
+ )
+
+ prompt = f"""Assess this task for prioritization:
+
+{triage_context}
+
+Consider:
+1. Complexity (low/medium/high)
+2. Dependencies on other tasks
+3. Priority (P0-P3)
+4. Best dev fit based on skills
+
+Format response as TOON:
+{{complexity,dependencies,priority,dev_fit}}:
+medium,TASK-abc123,P1,backend-dev-1
+"""
+ assessment = await self.think(prompt)
+ self.log.info(
+ "Task assessed", task_id=str(task_id), assessment=assessment[:100]
+ )
+ self._pending_tasks.append(task_id)
+
+ async def _phase_assign(self) -> None:
+ """
+ ASSIGN phase: Match tasks to developers.
+ """
+ self.log.debug("ASSIGN phase")
+
+ while self._pending_tasks and self._cell_status.available_devs > 0:
+ task_id = self._pending_tasks.pop(0)
+
+ # Find best dev
+ assignment = await self._find_best_dev(task_id)
+ if assignment:
+ await self._assign_task(assignment)
+ self._cell_status.available_devs -= 1
+
+ async def _phase_facilitate(self) -> None:
+ """
+ FACILITATE phase: Help cell members.
+
+ - Answer questions
+ - Clarify requirements
+ - Remove small blockers
+ - Unblock blocked tasks when blocker is resolved
+ """
+ self.log.debug("FACILITATE phase")
+
+ # Check for blocked tasks that may be resolvable
+ blocked_tasks = await self._get_blocked_tasks()
+ for task_id in blocked_tasks:
+ resolved = await self._check_blocker_resolved(task_id)
+ if resolved:
+ success = await self._unblock_task(task_id)
+ if success:
+ # Get session from task
+ _, session_id = await self._get_task_info(task_id)
+ await self.send_message(
+ session_id,
+ f"TASK-{str(task_id)[:8]} unblocked - blocker resolved",
+ message_type="action",
+ task_id=task_id,
+ )
+
+ # Check for pending questions in channel
+ questions = await self._get_pending_questions()
+
+ for question_data in questions:
+ question_content = question_data.get("content", "")
+ task_id_raw = question_data.get("task_id")
+ session_id = question_data.get("session_id")
+
+ # Use TOON for token-efficient context encoding
+ question_context = self.format_context_labeled(
+ "Cell Question",
+ {"question": question_content, "cell": self.cell_name},
+ )
+
+ prompt = f"""A cell member needs help:
+
+{question_context}
+
+As the Cell PM, provide:
+1. Answer if you can
+2. Or route to appropriate person
+3. Or escalate if needed
+
+Be helpful and unblock the team.
+"""
+ response = await self.think(prompt)
+
+ # Send response with task_id for proper routing
+ if session_id:
+ # Parse task_id if it's a string
+ parsed_task_id: UUID | None = None
+ if task_id_raw:
+ parsed_task_id = (
+ UUID(task_id_raw)
+ if isinstance(task_id_raw, str)
+ else task_id_raw
+ )
+ await self.send_message(
+ UUID(session_id) if isinstance(session_id, str) else session_id,
+ response,
+ message_type="answer",
+ task_id=parsed_task_id,
+ )
+ self.log.info(
+ "PM responded to question",
+ task_id=str(task_id_raw) if task_id_raw else None,
+ response_preview=response[:100],
+ )
+ else:
+ self.log.info(
+ "PM response (no session - using channel)",
+ task_id=str(task_id_raw) if task_id_raw else None,
+ response_preview=response[:100],
+ )
+
+ async def _phase_escalate(self) -> None:
+ """
+ ESCALATE phase: Handle issues beyond cell control.
+ """
+ self.log.debug("ESCALATE phase")
+
+ for escalation in self._pending_escalations:
+ # Notify Main PM
+ await self._notify_main_pm(escalation)
+
+ self._pending_escalations.clear()
+
+ async def _phase_track(self) -> None:
+ """
+ TRACK phase: Monitor task progress.
+ """
+ self.log.debug("TRACK phase")
+
+ active_tasks = await self._get_active_tasks()
+
+ for task_id in active_tasks:
+ progress = await self._check_task_progress(task_id)
+ if progress.get("at_risk"):
+ self._cell_status.concerns.append(f"Task {str(task_id)[:8]} at risk")
+
+ async def _phase_report(self) -> None:
+ """
+ REPORT phase: Status to Main PM.
+ """
+ self.log.debug("REPORT phase")
+
+ concerns = self._format_concerns()
+ report = f"""
+## {self.cell_name} Status Report
+
+**Active Tasks**: {self._cell_status.active_tasks}
+**Blocked Tasks**: {self._cell_status.blocked_tasks}
+**Completed Today**: {self._cell_status.completed_today}
+**Available Devs**: {self._cell_status.available_devs}
+
+**Concerns**:
+{concerns}
+"""
+ # Would send to #pm-all channel
+ self.log.info("Report generated", report_length=len(report))
+ self._cell_status.concerns.clear()
+
+ def _format_concerns(self) -> str:
+ """Format concerns for report."""
+ if not self._cell_status.concerns:
+ return "- None"
+ return chr(10).join(f"- {c}" for c in self._cell_status.concerns)
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _count_active_tasks(self) -> int:
+ """Count active tasks in cell."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "in_progress", "team": team_param},
+ )
+ return len(result.get("items", []))
+ except Exception as e:
+ self.log.warning("Failed to count active tasks", error=str(e))
+ return 0
+
+ async def _count_blocked_tasks(self) -> int:
+ """Count blocked tasks in cell."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "blocked", "team": team_param},
+ )
+ return len(result.get("items", []))
+ except Exception as e:
+ self.log.warning("Failed to count blocked tasks", error=str(e))
+ return 0
+
+ async def _count_available_devs(self) -> int:
+ """Count available developers."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/agents",
+ params={"role": "developer", "status": "idle", "team": team_param},
+ )
+ return len(result.get("items", []))
+ except Exception as e:
+ self.log.warning("Failed to count available devs", error=str(e))
+ return 0
+
+ async def _get_unassigned_tasks(self) -> list[UUID]:
+ """Get tasks needing assignment."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "pending", "team": team_param, "assigned_to": None},
+ )
+ return [UUID(t["id"]) for t in result.get("items", [])]
+ except Exception as e:
+ self.log.warning("Failed to get unassigned tasks", error=str(e))
+ return []
+
+ async def _find_best_dev(self, task_id: UUID) -> TaskAssignment | None:
+ """Find best developer for a task."""
+ try:
+ # Get available devs
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/agents",
+ params={"role": "developer", "status": "idle", "team": team_param},
+ )
+ agents = result.get("items", [])
+ if not agents:
+ return None
+
+ # For now, assign to first available
+ agent = agents[0]
+ return TaskAssignment(
+ task_id=task_id,
+ agent_id=UUID(agent["id"]),
+ agent_name=agent.get("name", "Unknown"),
+ reason="First available developer",
+ )
+ except Exception as e:
+ self.log.warning("Failed to find best dev", error=str(e))
+ return None
+
+ async def _assign_task(self, assignment: TaskAssignment) -> None:
+ """Assign a task to a developer."""
+ try:
+ await self._api_call(
+ "PUT",
+ f"/tasks/{assignment.task_id}",
+ json={"assigned_to": str(assignment.agent_id)},
+ )
+ self.log.info(
+ "Task assigned",
+ task_id=str(assignment.task_id),
+ agent=assignment.agent_name,
+ )
+ except Exception as e:
+ self.log.error("Failed to assign task", error=str(e))
+
+ async def _get_pending_questions(self) -> list[dict[str, Any]]:
+ """Get unanswered questions from channel.
+
+ Returns full message info including task_id for routing.
+ """
+ try:
+ result = await self._api_call(
+ "GET",
+ "/messages",
+ params={"message_type": "dialogue", "unanswered": True},
+ )
+ return [
+ {
+ "content": m.get("content", ""),
+ "task_id": m.get("task_id"),
+ "session_id": m.get("session_id"),
+ "from_agent": m.get("from_agent"),
+ }
+ for m in result.get("items", [])
+ ]
+ except Exception as e:
+ self.log.warning("Failed to get pending questions", error=str(e))
+ return []
+
+ async def _notify_main_pm(self, escalation: Escalation) -> None:
+ """
+ Notify Main PM of escalation.
+
+ Uses NotificationType.BLOCKER_ESCALATION to formally escalate the issue.
+ """
+ # Build notification content
+ notification_type = NotificationType.BLOCKER_ESCALATION
+ task_ref = str(escalation.task_id)[:8] if escalation.task_id else "N/A"
+ subject = f"Escalation from {self.cell_name}: {escalation.issue[:50]}"
+ body = f"""
+## Escalation from {self.cell_name}
+
+**Issue:** {escalation.issue}
+**Severity:** {escalation.severity}
+**Task:** {task_ref}
+
+**Proposed Solution:**
+{escalation.proposed_solution or "No solution proposed"}
+
+Please review and provide guidance.
+"""
+
+ self.log.info(
+ "Escalation sent to Main PM",
+ subject=subject,
+ body_length=len(body),
+ notification_type=notification_type.value,
+ severity=escalation.severity,
+ )
+
+ async def _get_active_tasks(self) -> list[UUID]:
+ """Get all active tasks in cell."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "in_progress", "team": team_param},
+ )
+ return [UUID(t["id"]) for t in result.get("items", [])]
+ except Exception as e:
+ self.log.warning("Failed to get active tasks", error=str(e))
+ return []
+
+ async def _get_blocked_tasks(self) -> list[UUID]:
+ """Get all blocked tasks in cell."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "blocked", "team": team_param},
+ )
+ return [UUID(t["id"]) for t in result.get("items", [])]
+ except Exception as e:
+ self.log.warning("Failed to get blocked tasks", error=str(e))
+ return []
+
+ async def _check_blocker_resolved(self, task_id: UUID) -> bool:
+ """
+ Check if a task's blocker has been resolved.
+
+ This examines the blocker_reason and checks if conditions are met.
+ For subtask blockers, checks if all subtasks are complete.
+ """
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ blocker_reason = result.get("blocker_reason", "")
+
+ # Check if subtasks are complete (common blocker)
+ subtasks = result.get("subtasks", [])
+ if subtasks:
+ all_complete = all(s.get("status") == "completed" for s in subtasks)
+ if all_complete:
+ return True
+
+ # If no specific logic, return False (needs manual review)
+ return not blocker_reason # Resolved if reason was cleared
+
+ except Exception as e:
+ self.log.warning("Failed to check blocker", error=str(e))
+ return False
+
+ async def _check_task_progress(self, task_id: UUID) -> dict[str, Any]:
+ """
+ Check progress of a task.
+
+ Returns risk assessment based on task status and time in state.
+ """
+ try:
+ result = await self._api_call("GET", f"/tasks/{task_id}")
+ status = result.get("status", "")
+ risk_factors = []
+
+ # Blocked tasks are always at risk
+ if status == TaskStatus.BLOCKED.value:
+ risk_factors.append("Task is blocked")
+
+ return {
+ "at_risk": len(risk_factors) > 0,
+ "status": status,
+ "risk_factors": risk_factors,
+ }
+ except Exception as e:
+ self.log.warning("Failed to check task progress", error=str(e))
+ return {
+ "at_risk": False,
+ "status": TaskStatus.IN_PROGRESS.value,
+ "risk_factors": [],
+ }
+
+
+class MainPMAgent(Agent, CyclicPhaseRunner[MainPMPhase]):
+ """
+ Main PM agent that coordinates all cells.
+
+ Workflow:
+ 1. OVERSEE - Monitor all cells
+ 2. RECEIVE - Get direction from Board, escalations from Cell PMs
+ 3. PRIORITIZE - Translate Board direction to cell priorities
+ 4. COORDINATE - Resolve cross-cell issues
+ 5. DISTRIBUTE - Push tasks/priorities to Cell PMs
+ 6. REPORT UP - Status to Board
+ 7. FACILITATE - All-hands coordination
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize Main PM agent."""
+ super().__init__(config)
+ self._current_phase = MainPMPhase.OVERSEE
+ self._cell_statuses: dict[str, CellStatus] = {}
+ self._board_directives: list[str] = []
+ self._cross_cell_issues: list[dict[str, Any]] = []
+
+ async def _initialize(self) -> None:
+ """Initialize Main PM-specific resources."""
+ self.log.debug("Main PM agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup Main PM-specific resources."""
+ self._cell_statuses.clear()
+ self._board_directives.clear()
+ self._cross_cell_issues.clear()
+ self.log.debug("Main PM agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # CYCLIC PHASE RUNNER IMPLEMENTATION
+ # =========================================================================
+
+ def _get_cyclic_phase_configs(self) -> list[CyclicPhaseConfig[MainPMPhase]]:
+ """Define the Main PM workflow phases."""
+ return [
+ CyclicPhaseConfig(
+ MainPMPhase.OVERSEE,
+ self._phase_oversee,
+ MainPMPhase.RECEIVE,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.RECEIVE,
+ self._phase_receive,
+ MainPMPhase.PRIORITIZE,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.PRIORITIZE,
+ self._phase_prioritize,
+ MainPMPhase.COORDINATE,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.COORDINATE,
+ self._phase_coordinate,
+ MainPMPhase.DISTRIBUTE,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.DISTRIBUTE,
+ self._phase_distribute,
+ MainPMPhase.REPORT_UP,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.REPORT_UP,
+ self._phase_report_up,
+ MainPMPhase.FACILITATE,
+ ),
+ CyclicPhaseConfig(
+ MainPMPhase.FACILITATE,
+ self._phase_facilitate,
+ MainPMPhase.OVERSEE, # Cycle back
+ ),
+ ]
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """
+ Find work for the Main PM.
+
+ Priority:
+ 1. Paused tasks with all subtasks complete (ready for closure)
+ 2. Assigned tasks in progress
+ 3. Fall back to cyclic management duties (self.id)
+ """
+ # Check for paused tasks ready for closure
+ ready_task = await self._find_paused_task_ready_for_closure()
+ if ready_task:
+ self.log.info(
+ "Found paused task ready for closure", task_id=str(ready_task)
+ )
+ return ready_task
+
+ # Check for assigned in-progress tasks
+ assigned_task = await self._find_assigned_task()
+ if assigned_task:
+ self.log.info("Found assigned task", task_id=str(assigned_task))
+ return assigned_task
+
+ # Fall back to cyclic management duties
+ return self.id
+
+ async def _find_paused_task_ready_for_closure(self) -> UUID | None:
+ """
+ Find own paused tasks where all subtasks are complete.
+
+ This is the key PM workflow: delegate subtasks, pause, get respawned
+ when subtasks complete, then review and close.
+ """
+ try:
+ # Get my paused tasks
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "paused", "assigned_to": str(self.id)},
+ )
+ paused_tasks = (
+ result.get("items", result) if isinstance(result, dict) else result
+ )
+
+ for task in paused_tasks:
+ task_id = task.get("id")
+ if not task_id:
+ continue
+
+ # Check if this task has subtasks
+ subtasks_result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"parent_task_id": task_id},
+ )
+ subtasks = (
+ subtasks_result.get("items", subtasks_result)
+ if isinstance(subtasks_result, dict)
+ else subtasks_result
+ )
+
+ if not subtasks:
+ continue # No subtasks - not a delegation task
+
+ # Check if ALL subtasks are complete
+ all_complete = all(
+ s.get("status") in ("completed", "cancelled") for s in subtasks
+ )
+
+ if all_complete:
+ return UUID(task_id) if isinstance(task_id, str) else task_id
+
+ return None
+
+ except Exception as e:
+ self.log.warning(
+ "Failed to find paused tasks ready for closure", error=str(e)
+ )
+ return None
+
+ async def _find_assigned_task(self) -> UUID | None:
+ """Find tasks assigned to this PM that are in progress."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "in_progress", "assigned_to": str(self.id)},
+ )
+ tasks = result.get("items", result) if isinstance(result, dict) else result
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find assigned task", error=str(e))
+ return None
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute Main PM work.
+
+ Two modes:
+ 1. task_id == self.id: Run cyclic coordination duties
+ 2. task_id is real task: Work on specific task (CLAIM → PLAN → START → ...)
+
+ Returns True when task-specific work is complete, False for cyclic duties.
+ """
+ # Cyclic coordination duties (no specific task)
+ if task_id == self.id:
+ error = await self._run_phase_cycle()
+ if error:
+ self.log.error(
+ "Error in Main PM phase",
+ phase=self._current_phase.value,
+ error=error,
+ )
+ return False # Never complete - continuous duty
+
+ # Task-specific work - delegate to task workflow
+ return await self._execute_main_pm_task(task_id)
+
+ async def _execute_main_pm_task(self, task_id: UUID) -> bool:
+ """
+ Execute Main PM workflow on a specific task.
+
+ Main PM Workflow: CLAIM → PLAN → START → DISTRIBUTE → PAUSE → COMPLETE
+
+ Returns True when Main PM work is done (distributed or completed).
+ """
+ try:
+ task = await self._api_call("GET", f"/tasks/{task_id}")
+ status = task.get("status")
+ return await self._handle_main_pm_task_status(task_id, task, status)
+ except Exception as e:
+ self.log.error(
+ "Error in Main PM task execution",
+ task_id=str(task_id),
+ error=str(e),
+ )
+ return False
+
+ async def _handle_main_pm_task_status(
+ self, task_id: UUID, task: dict[str, Any], status: str | None
+ ) -> bool:
+ """Handle Main PM task based on current status."""
+ if status == "pending":
+ await self._mark_claimed(task_id)
+ self.log.info("Main PM claimed task", task_id=str(task_id))
+ return False
+
+ if status == "claimed":
+ return await self._handle_main_pm_claimed(task_id, task)
+
+ if status == "in_progress":
+ return await self._handle_main_pm_in_progress(task_id, task)
+
+ if status == "paused":
+ await self._mark_completed(task_id)
+ self.log.info("Main PM completed task", task_id=str(task_id))
+ return True
+
+ return False
+
+ def _get_team_channel(self, team: str) -> str:
+ """Get the channel slug for a team."""
+ channel_map = {
+ "backend": "backend-cell",
+ "frontend": "frontend-cell",
+ "ux_ui": "uxui-cell",
+ }
+ return channel_map.get(team, "dev-all")
+
+ async def _handle_main_pm_claimed(
+ self, task_id: UUID, task: dict[str, Any]
+ ) -> bool:
+ """Handle claimed task - plan then start."""
+ if not task.get("plan"):
+ plan = await self._create_main_pm_plan(task)
+ await self._api_call("PATCH", f"/tasks/{task_id}", json={"plan": plan})
+ self.log.info("Main PM planned task", task_id=str(task_id))
+ return False
+
+ await self._mark_in_progress(task_id)
+ self.log.info("Main PM started task", task_id=str(task_id))
+ return False
+
+ async def _handle_main_pm_in_progress(
+ self, task_id: UUID, task: dict[str, Any]
+ ) -> bool:
+ """Handle in_progress task - distribute and pause."""
+ distributed = await self._distribute_to_cells(task_id, task)
+ if distributed:
+ remaining = ["Monitor cell progress", "Close initiative"]
+ await self._api_call(
+ "POST",
+ f"/tasks/{task_id}/pause",
+ json={
+ "reason": "Awaiting cell completion",
+ "checkpoint_summary": "Distributed to Cell PMs",
+ "remaining_work": remaining,
+ },
+ )
+ self.log.info("Main PM distributed and paused", task_id=str(task_id))
+ return True
+
+ async def _create_main_pm_plan(self, task: dict[str, Any]) -> dict[str, Any]:
+ """Create a Main PM coordination plan for an initiative."""
+ return {
+ "approach": "Coordinate across cells to deliver initiative",
+ "steps": [
+ "Analyze initiative requirements",
+ "Identify cell responsibilities",
+ "Create tasks for Cell PMs",
+ "Set up cross-cell sessions",
+ "Monitor and coordinate",
+ ],
+ "risks": task.get("acceptance_criteria", [])[:2],
+ "estimated_sessions": 2,
+ }
+
+ async def _distribute_to_cells(self, task_id: UUID, task: dict[str, Any]) -> bool:
+ """Distribute initiative to appropriate Cell PMs."""
+ title = task.get("title", "").lower()
+ description = task.get("description", "").lower()
+ content = title + description
+
+ cells_needed = self._determine_cells_needed(content)
+
+ for team, pm_slug in cells_needed:
+ await self._create_cell_task(task_id, task, team, pm_slug)
+
+ return len(cells_needed) > 0
+
+ def _determine_cells_needed(self, content: str) -> list[tuple[str, str]]:
+ """Determine which cells are needed based on content keywords."""
+ cells = []
+ backend_kw = ["api", "backend", "database", "server"]
+ frontend_kw = ["ui", "frontend", "component", "page"]
+ ux_kw = ["design", "ux", "figma", "mockup"]
+
+ if any(kw in content for kw in backend_kw):
+ cells.append(("backend", "be-pm"))
+ if any(kw in content for kw in frontend_kw):
+ cells.append(("frontend", "fe-pm"))
+ if any(kw in content for kw in ux_kw):
+ cells.append(("ux_ui", "ux-pm"))
+
+ return cells if cells else [("backend", "be-pm")]
+
+ async def _create_cell_task(
+ self, parent_id: UUID, task: dict[str, Any], team: str, pm_slug: str
+ ) -> None:
+ """Create a task for a Cell PM.
+
+ Follows blueprint workflow:
+ CREATE (backlog) → GROUP → SESSION → ACTIVATE (pending) → NOTIFY
+ """
+ # Step 1: Create task with status "backlog"
+ task_resp = await self._api_call(
+ "POST",
+ "/tasks",
+ json={
+ "title": f"[{team.upper()}] {task.get('title', 'Task')}",
+ "description": task.get("description", ""),
+ "team": team,
+ "acceptance_criteria": task.get("acceptance_criteria", []),
+ "parent_task_id": str(parent_id),
+ "assigned_to": pm_slug,
+ "status": "backlog", # Backlog until session is ready
+ },
+ )
+ cell_task_id = task_resp.get("id")
+ if not cell_task_id:
+ self.log.error("Failed to create cell task", parent_id=str(parent_id))
+ return
+
+ self.log.info(
+ "Main PM created cell task (backlog)",
+ cell_task_id=cell_task_id,
+ parent_task_id=str(parent_id),
+ team=team,
+ assigned_to=pm_slug,
+ )
+
+ # Step 2: Create group if needed (cross-cell initiatives)
+ channel_slug = self._get_team_channel(team)
+
+ # Step 3: Create session for the cell task
+ try:
+ await self._api_call(
+ "POST",
+ "/sessions/for-tasks",
+ json={
+ "task_ids": [cell_task_id],
+ "channel_slug": channel_slug,
+ "scope": f"Cell work for {task.get('title', 'initiative')}",
+ "relationship_type": "implements",
+ },
+ )
+ self.log.info("Session created for cell task", cell_task_id=cell_task_id)
+ except Exception as e:
+ self.log.warning(
+ "Session creation failed, activating anyway",
+ cell_task_id=cell_task_id,
+ error=str(e),
+ )
+
+ # Step 4: Activate task (changes status to pending)
+ try:
+ await self._api_call("POST", f"/tasks/{cell_task_id}/activate")
+ self.log.info("Cell task activated", cell_task_id=cell_task_id)
+ except Exception as e:
+ self.log.error(
+ "Failed to activate cell task",
+ cell_task_id=cell_task_id,
+ error=str(e),
+ )
+ return
+
+ # Step 5: Notify Cell PM
+ try:
+ await self._api_call(
+ "POST",
+ "/notifications",
+ json={
+ "type": "task_assigned",
+ "priority": "normal",
+ "to_agents": [pm_slug],
+ "subject": f"New initiative: {task.get('title', 'Task')}",
+ "body": (
+ f"A new initiative has been assigned to your cell.\n\n"
+ f"Task ID: {cell_task_id}\n"
+ f"Title: {task.get('title', 'Task')}\n\n"
+ f"Please triage and delegate to your team."
+ ),
+ "related_task_id": cell_task_id,
+ "requires_ack": True,
+ },
+ )
+ except Exception as e:
+ self.log.warning(
+ "Failed to notify Cell PM (task still assigned)",
+ error=str(e),
+ )
+
+ # =========================================================================
+ # MAIN PM PHASES
+ # =========================================================================
+
+ async def _phase_oversee(self) -> None:
+ """OVERSEE phase: Monitor all cells."""
+ self.log.debug("OVERSEE phase")
+
+ # Collect status from all cells
+ for cell_name in ["backend-cell", "frontend-cell", "uxui-cell"]:
+ status = await self._get_cell_status(cell_name)
+ self._cell_statuses[cell_name] = status
+
+ # Look for cross-cell issues
+ self._cross_cell_issues = await self._detect_cross_cell_issues()
+
+ async def _phase_receive(self) -> None:
+ """RECEIVE phase: Get direction and escalations."""
+ self.log.debug("RECEIVE phase")
+
+ # Check for Board directives
+ self._board_directives = await self._get_board_directives()
+
+ # Check for Cell PM escalations
+ escalations = await self._get_cell_pm_escalations()
+ for esc in escalations:
+ self.log.info("Received escalation", issue=esc.get("issue"))
+
+ async def _phase_prioritize(self) -> None:
+ """PRIORITIZE phase: Set cross-cell priorities."""
+ self.log.debug("PRIORITIZE phase")
+
+ if self._board_directives:
+ # Build status data for TOON encoding
+ cell_status_data = {
+ name: {"active": s.active_tasks, "blocked": s.blocked_tasks}
+ for name, s in self._cell_statuses.items()
+ }
+
+ # Use TOON for token-efficient context encoding
+ priority_context = self.format_context_labeled(
+ "Prioritization Context",
+ {
+ "directives": self._board_directives,
+ "cell_status": cell_status_data,
+ },
+ )
+
+ prompt = f"""Translate these Board directives into cell priorities:
+
+{priority_context}
+
+Format response as TOON tabular:
+[N,]{{cell,priority,task_description}}:
+backend-cell,P0,Implement critical auth fix
+frontend-cell,P1,Update dashboard layout
+"""
+ priorities = await self.think(prompt)
+ self.log.info("Priorities set", priorities=priorities[:200])
+
+ async def _phase_coordinate(self) -> None:
+ """COORDINATE phase: Resolve cross-cell issues."""
+ self.log.debug("COORDINATE phase")
+
+ for issue in self._cross_cell_issues:
+ # Use TOON for token-efficient context encoding
+ issue_context = self.format_context_labeled(
+ "Cross-Cell Issue",
+ {
+ "description": issue.get("description"),
+ "cells": issue.get("cells"),
+ "task_id": issue.get("task_id"),
+ },
+ )
+
+ prompt = f"""Resolve this cross-cell issue:
+
+{issue_context}
+
+Propose a resolution that unblocks all parties.
+"""
+ resolution = await self.think(prompt)
+ await self._apply_resolution(issue, resolution)
+
+ self._cross_cell_issues.clear()
+
+ async def _phase_distribute(self) -> None:
+ """DISTRIBUTE phase: Push priorities to Cell PMs."""
+ self.log.debug("DISTRIBUTE phase")
+
+ for directive in self._board_directives:
+ # Determine which cell(s) need this
+ cell = self._route_directive(directive)
+ if cell:
+ await self._notify_cell_pm(cell, directive)
+
+ self._board_directives.clear()
+
+ async def _phase_report_up(self) -> None:
+ """REPORT UP phase: Status to Board."""
+ self.log.debug("REPORT UP phase")
+
+ report = """
+## Organization Status Report
+
+### Cell Summary
+"""
+ for cell_name, status in self._cell_statuses.items():
+ report += f"""
+**{cell_name}**:
+- Active: {status.active_tasks}
+- Blocked: {status.blocked_tasks}
+- Available: {status.available_devs}
+"""
+
+ # Would send to #main-pm-board
+ self.log.info("Board report generated", report_length=len(report))
+
+ async def _phase_facilitate(self) -> None:
+ """FACILITATE phase: All-hands coordination."""
+ self.log.debug("FACILITATE phase")
+
+ # Check for all-hands items
+ # Process improvements, announcements, etc.
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _get_cell_status(self, cell_name: str) -> CellStatus:
+ """Get status of a cell."""
+ try:
+ # Get task counts per status for this cell
+ team = cell_name.replace("-cell", "")
+ active_result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "in_progress", "team": team},
+ )
+ blocked_result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "blocked", "team": team},
+ )
+ devs_result = await self._api_call(
+ "GET",
+ "/agents",
+ params={"role": "developer", "status": "idle", "team": team},
+ )
+ return CellStatus(
+ name=cell_name,
+ active_tasks=len(active_result.get("items", [])),
+ blocked_tasks=len(blocked_result.get("items", [])),
+ available_devs=len(devs_result.get("items", [])),
+ )
+ except Exception as e:
+ self.log.warning("Failed to get cell status", cell=cell_name, error=str(e))
+ return CellStatus(name=cell_name)
+
+ async def _detect_cross_cell_issues(self) -> list[dict[str, Any]]:
+ """Detect cross-cell dependencies and issues."""
+ try:
+ # Look for tasks blocked by other cells
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "blocked"},
+ )
+ issues = []
+ for task in result.get("items", []):
+ blocker = task.get("blocker_reason", "")
+ if "frontend" in blocker.lower() or "backend" in blocker.lower():
+ issues.append(
+ {
+ "description": f"Cross-cell blocker: {blocker}",
+ "cells": [task.get("team", "unknown")],
+ "task_id": task.get("id"),
+ }
+ )
+ return issues
+ except Exception as e:
+ self.log.warning("Failed to detect cross-cell issues", error=str(e))
+ return []
+
+ async def _get_board_directives(self) -> list[str]:
+ """Get directives from Board."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/messages",
+ params={"channel": "main-pm-board", "message_type": "action"},
+ )
+ return [m.get("content", "") for m in result.get("items", [])]
+ except Exception as e:
+ self.log.warning("Failed to get board directives", error=str(e))
+ return []
+
+ async def _get_cell_pm_escalations(self) -> list[dict[str, Any]]:
+ """Get escalations from Cell PMs."""
+ try:
+ result = await self._api_call(
+ "GET",
+ "/notifications",
+ params={"type": "escalation", "status": "pending"},
+ )
+ items: list[dict[str, Any]] = result.get("items", [])
+ return items
+ except Exception as e:
+ self.log.warning("Failed to get escalations", error=str(e))
+ return []
+
+ async def _apply_resolution(
+ self,
+ issue: dict[str, Any],
+ resolution: str,
+ ) -> None:
+ """Apply a cross-cell resolution."""
+ self.log.info(
+ "Resolution applied",
+ issue=issue.get("description"),
+ resolution_length=len(resolution),
+ )
+
+ def _route_directive(self, directive: str) -> str | None:
+ """Route a directive to appropriate cell."""
+ directive_lower = directive.lower()
+ if "backend" in directive_lower or "api" in directive_lower:
+ return "backend-cell"
+ elif "frontend" in directive_lower or "ui" in directive_lower:
+ return "frontend-cell"
+ elif "ux" in directive_lower or "design" in directive_lower:
+ return "uxui-cell"
+ return None
+
+ async def _notify_cell_pm(self, cell: str, directive: str) -> None:
+ """Notify a Cell PM of a directive."""
+ self.log.info("Directive sent", cell=cell, directive=directive[:50])
diff --git a/roboco/agents/qa.py b/roboco/agents/qa.py
new file mode 100644
index 00000000..35fa4fd7
--- /dev/null
+++ b/roboco/agents/qa.py
@@ -0,0 +1,492 @@
+"""
+QA Agent
+
+Implementation of the QA workflow from the blueprint.
+Handles review lifecycle:
+ MONITOR → RECEIVE → UNDERSTAND → TEST → VERDICT → DOCUMENT → RETURN
+"""
+
+from datetime import UTC, datetime
+from uuid import UUID
+
+import structlog
+from roboco.agents.base import Agent, AgentConfig
+from roboco.agents.mixins import PhaseConfig, PhaseEngine
+from roboco.models.agents import (
+ QATaskPhase,
+ ReviewContext,
+ TestCase,
+ TestResult,
+)
+
+logger = structlog.get_logger()
+
+
+class QAAgent(Agent, PhaseEngine[QATaskPhase, ReviewContext]):
+ """
+ QA agent that follows the QA Lifecycle.
+
+ Workflow:
+ 1. MONITOR - Watch cell channel, track tasks approaching completion
+ 2. RECEIVE - Dev flags ready, claim review task
+ 3. UNDERSTAND - Read requirements, dev notes, commits
+ 4. TEST - Execute test scenarios, edge cases
+ 5. VERDICT - PASS or FAIL with clear feedback
+ 6. DOCUMENT - Add QA notes, test coverage
+ 7. RETURN - Back to monitoring
+ """
+
+ def __init__(self, config: AgentConfig) -> None:
+ """Initialize QA agent."""
+ super().__init__(config)
+ self._review_context: ReviewContext | None = None
+ self._cell_channel_id: UUID | None = None
+ self._pending_reviews: list[UUID] = []
+
+ async def _initialize(self) -> None:
+ """Initialize QA-specific resources."""
+ self.log.debug("QA agent initialized", agent_id=str(self.id))
+
+ async def _cleanup(self) -> None:
+ """Cleanup QA-specific resources."""
+ self._review_context = None
+ self._pending_reviews.clear()
+ self.log.debug("QA agent cleanup complete", agent_id=str(self.id))
+
+ # =========================================================================
+ # PHASE ENGINE IMPLEMENTATION
+ # =========================================================================
+
+ def _get_phase_configs(self) -> list[PhaseConfig[QATaskPhase]]:
+ """Define the QA workflow phases."""
+ return [
+ PhaseConfig(
+ QATaskPhase.RECEIVE,
+ self._phase_receive,
+ next_phase=QATaskPhase.UNDERSTAND,
+ ),
+ PhaseConfig(
+ QATaskPhase.UNDERSTAND,
+ self._phase_understand,
+ next_phase=QATaskPhase.TEST,
+ ),
+ PhaseConfig(
+ QATaskPhase.TEST,
+ self._phase_test,
+ next_phase=QATaskPhase.VERDICT,
+ requires_completion=True,
+ ),
+ PhaseConfig(
+ QATaskPhase.VERDICT,
+ self._phase_verdict,
+ next_phase=QATaskPhase.DOCUMENT,
+ ),
+ PhaseConfig(
+ QATaskPhase.DOCUMENT,
+ self._phase_document,
+ next_phase=QATaskPhase.RETURN,
+ ),
+ PhaseConfig(
+ QATaskPhase.RETURN,
+ self._phase_return,
+ next_phase=None, # Terminal
+ ),
+ ]
+
+ def _get_current_phase(self, ctx: ReviewContext) -> QATaskPhase:
+ """Get the current phase from context."""
+ return ctx.phase
+
+ def _set_current_phase(self, ctx: ReviewContext, phase: QATaskPhase) -> None:
+ """Set the current phase in context."""
+ ctx.phase = phase
+
+ # =========================================================================
+ # LIFECYCLE IMPLEMENTATION
+ # =========================================================================
+
+ async def find_work(self) -> UUID | None:
+ """
+ MONITOR phase: Watch for tasks ready for review.
+
+ - Check for tasks flagged as awaiting_qa
+ - Check for PM notifications
+ """
+ self.log.info("Monitoring for reviews")
+
+ # Check pending reviews queue
+ if self._pending_reviews:
+ return self._pending_reviews.pop(0)
+
+ # Query for tasks awaiting QA
+ task_id = await self._find_awaiting_qa()
+ if task_id:
+ return task_id
+
+ return None
+
+ async def execute_task(self, task_id: UUID) -> bool:
+ """
+ Execute review through QA lifecycle phases.
+
+ Returns True when review is complete.
+ """
+ if self._review_context is None or self._review_context.task_id != task_id:
+ title, session_id = await self._get_task_info(task_id)
+ self._review_context = ReviewContext(
+ task_id=task_id,
+ title=title,
+ session_id=session_id,
+ )
+
+ ctx = self._review_context
+
+ try:
+ result = await self._run_phase_engine(ctx)
+
+ if result.error:
+ self.log.error(
+ "Error in review phase",
+ phase=ctx.phase.value,
+ error=result.error,
+ )
+ ctx.findings.append(f"Error during review: {result.error}")
+ return False
+
+ if result.completed:
+ self._review_context = None
+ return True
+
+ return False
+
+ except Exception as e:
+ self.log.error(
+ "Error in review phase",
+ phase=ctx.phase.value,
+ error=str(e),
+ )
+ ctx.findings.append(f"Error during review: {e}")
+ return False
+
+ # =========================================================================
+ # PHASE IMPLEMENTATIONS
+ # =========================================================================
+
+ async def _phase_receive(self, ctx: ReviewContext) -> None:
+ """
+ RECEIVE phase: Claim the review task.
+
+ - Claim task via /claim endpoint
+ - Acknowledge receipt
+ - Announce review started
+ """
+ self.log.info("RECEIVE phase", task_id=str(ctx.task_id))
+
+ # CLAIM: Transition from awaiting_qa to claimed
+ await self._mark_claimed(ctx.task_id)
+
+ await self.send_message(
+ ctx.session_id,
+ f"Starting review of TASK-{str(ctx.task_id)[:8]}: {ctx.title}",
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ ctx.notes.append(f"[{datetime.now(UTC).isoformat()}] Review started")
+
+ async def _phase_understand(self, ctx: ReviewContext) -> None:
+ """
+ UNDERSTAND phase: Read requirements and dev notes.
+
+ - Read task requirements and acceptance criteria
+ - Read dev's handoff notes (from task's dev_notes field)
+ - Review commits
+ - Check conversation history
+ - Read developer's journal entries for this task (if needed)
+ """
+ self.log.info("UNDERSTAND phase", task_id=str(ctx.task_id))
+
+ # Read task context
+ requirements = await self._read_task_requirements(ctx.task_id)
+ dev_notes = await self._read_dev_notes(ctx.task_id)
+ commits = await self._get_task_commits_formatted(ctx.task_id)
+
+ # Read developer journal entries for this task (cell members can read)
+ dev_journal = await self._read_team_journal_for_task(ctx.task_id)
+
+ # Use TOON for token-efficient context encoding
+ task_context = self._format_review_context(
+ ctx.title, requirements, dev_notes, commits
+ )
+
+ # Include journal context if available
+ journal_context = ""
+ if dev_journal:
+ journal_context = f"\n\nDeveloper Journal Entries:\n{dev_journal}"
+
+ prompt = f"""You are a QA engineer reviewing a completed task.
+
+{task_context}{journal_context}
+
+Based on this, create test cases to verify the implementation.
+
+Focus on:
+- Acceptance criteria verification
+- Edge cases
+- Integration points
+- Error handling
+
+If acceptance criteria mentions journaling requirements, verify them against
+the developer journal entries provided above.
+
+Format response as TOON tabular:
+[N,]{{name,description,steps,expected}}:
+Acceptance Criteria,Verify all criteria met,Review implementation|Check each criterion,All criteria satisfied
+""" # noqa: E501
+ _response = await self.think(prompt)
+
+ # Create test cases (simplified parsing)
+ ctx.test_cases = [
+ TestCase(
+ name="Acceptance Criteria",
+ description="Verify all acceptance criteria are met",
+ steps=["Review implementation", "Check each criterion"],
+ expected="All criteria satisfied",
+ ),
+ TestCase(
+ name="Edge Cases",
+ description="Test edge cases and error handling",
+ steps=["Test with invalid input", "Test boundary conditions"],
+ expected="Graceful handling of edge cases",
+ ),
+ TestCase(
+ name="Integration",
+ description="Verify integration with existing code",
+ steps=["Run integration tests", "Check API compatibility"],
+ expected="No breaking changes",
+ ),
+ ]
+
+ # PLAN: Save test plan to task API (required before start)
+ plan_data = {
+ "approach": f"QA review of {ctx.title}",
+ "sub_tasks": [
+ {
+ "id": f"test-{i}",
+ "title": tc.name,
+ "description": tc.description,
+ "completed": False,
+ "order": i,
+ }
+ for i, tc in enumerate(ctx.test_cases)
+ ],
+ "risks": [],
+ }
+ await self._api_call("PATCH", f"/tasks/{ctx.task_id}", json={"plan": plan_data})
+
+ ts = datetime.now(UTC).isoformat()
+ ctx.notes.append(f"[{ts}] Created {len(ctx.test_cases)} test cases")
+
+ async def _phase_test(self, ctx: ReviewContext) -> bool:
+ """
+ TEST phase: Execute test scenarios.
+
+ - START: Transition to in_progress on first test
+ - Run through each test case
+ - Document findings
+
+ Returns True when all tests complete.
+ """
+ self.log.info(
+ "TEST phase",
+ task_id=str(ctx.task_id),
+ test=ctx.current_test,
+ total=len(ctx.test_cases),
+ )
+
+ # START: Transition to in_progress on first test
+ if ctx.current_test == 0:
+ await self._mark_in_progress(ctx.task_id)
+ self.log.info("QA review started (in_progress)", task_id=str(ctx.task_id))
+
+ if ctx.current_test >= len(ctx.test_cases):
+ return True
+
+ test_case = ctx.test_cases[ctx.current_test]
+
+ # Use TOON for token-efficient context encoding
+ test_context = self._format_test_context(
+ test_case.name,
+ test_case.description,
+ test_case.steps,
+ test_case.expected,
+ )
+
+ prompt = f"""Execute this test case:
+
+{test_context}
+
+Simulate executing this test and provide results.
+
+Format response as TOON:
+{{result,actual,notes}}:
+PASS,All criteria verified successfully,No issues found
+"""
+ response = await self.think(prompt)
+
+ # Parse result (simplified)
+ if "PASS" in response.upper():
+ test_case.result = TestResult.PASS
+ else:
+ test_case.result = TestResult.FAIL
+ ctx.findings.append(f"FAIL: {test_case.name}")
+
+ test_case.actual = response
+ ctx.current_test += 1
+
+ # Progress update
+ progress = f"{ctx.current_test}/{len(ctx.test_cases)}"
+ result_str = test_case.result.value.upper()
+ task_ref = str(ctx.task_id)[:8]
+ msg = f"TASK-{task_ref} test {progress}: {test_case.name} - {result_str}"
+ await self.send_message(
+ ctx.session_id,
+ msg,
+ message_type="action",
+ task_id=ctx.task_id,
+ )
+
+ return ctx.current_test >= len(ctx.test_cases)
+
+ async def _phase_verdict(self, ctx: ReviewContext) -> None:
+ """
+ VERDICT phase: Determine overall pass/fail.
+
+ - Analyze all test results
+ - Communicate clear verdict
+ - If fail, provide specific feedback
+ """
+ self.log.info("VERDICT phase", task_id=str(ctx.task_id))
+
+ # Determine verdict
+ failed_tests = [t for t in ctx.test_cases if t.result == TestResult.FAIL]
+
+ if failed_tests:
+ ctx.verdict = TestResult.FAIL
+
+ # Communicate failure with specifics
+ failure_summary = "\n".join(
+ [f"- {t.name}: {t.actual or 'No details'}" for t in failed_tests]
+ )
+
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} QA FAILED\n\n"
+ f"Issues found:\n{failure_summary}\n\n"
+ f"Task returned to developer for fixes.",
+ message_type="decision",
+ task_id=ctx.task_id,
+ )
+
+ # Use proper QA fail endpoint (handles notes, status, reassignment)
+ issue_list = [t.actual or t.name for t in failed_tests]
+ await self._qa_fail(
+ ctx.task_id,
+ "Found issues that need fixing before approval.",
+ issue_list,
+ )
+
+ else:
+ ctx.verdict = TestResult.PASS
+
+ await self.send_message(
+ ctx.session_id,
+ f"TASK-{str(ctx.task_id)[:8]} QA APPROVED\n\n"
+ f"All {len(ctx.test_cases)} tests passed.\n"
+ f"Ready for documentation.",
+ message_type="decision",
+ task_id=ctx.task_id,
+ )
+
+ # Use proper QA pass endpoint (handles notes, status)
+ await self._qa_pass(
+ ctx.task_id,
+ f"All {len(ctx.test_cases)} tests passed. Ready for documentation.",
+ )
+
+ ctx.notes.append(
+ f"[{datetime.now(UTC).isoformat()}] Verdict: {ctx.verdict.value.upper()}"
+ )
+
+ async def _phase_document(self, ctx: ReviewContext) -> None:
+ """
+ DOCUMENT phase: Add QA notes to task.
+
+ - Document test coverage
+ - Add handoff notes for documenter
+ """
+ self.log.info("DOCUMENT phase", task_id=str(ctx.task_id))
+
+ # Generate QA report
+ test_summary = "\n".join(
+ [
+ f"- {t.name}: {t.result.value.upper() if t.result else 'NOT RUN'}"
+ for t in ctx.test_cases
+ ]
+ )
+
+ qa_report = f"""
+## QA Review Summary
+
+**Task**: {ctx.title}
+**Verdict**: {ctx.verdict.value.upper() if ctx.verdict else "UNKNOWN"}
+**Reviewed**: {datetime.now(UTC).isoformat()}
+
+### Tests Executed
+
+{test_summary}
+
+### Findings
+
+{chr(10).join(ctx.findings) if ctx.findings else "No issues found"}
+
+### Notes
+
+{chr(10).join(ctx.notes)}
+"""
+
+ # Would save to task record
+ self.log.info("QA report generated", report_length=len(qa_report))
+
+ ctx.notes.append(f"[{datetime.now(UTC).isoformat()}] QA documentation complete")
+
+ async def _phase_return(self, ctx: ReviewContext) -> None:
+ """
+ RETURN phase: Clean up and return to monitoring.
+ """
+ self.log.info("RETURN phase", task_id=str(ctx.task_id))
+ # Context will be cleared by execute_task on completion
+
+ # =========================================================================
+ # HELPER METHODS
+ # =========================================================================
+
+ async def _find_awaiting_qa(self) -> UUID | None:
+ """Find tasks awaiting QA review."""
+ try:
+ team_param = self.team.value if self.team else None
+ result = await self._api_call(
+ "GET",
+ "/tasks",
+ params={"status": "awaiting_qa", "team": team_param},
+ )
+ tasks = result.get("items", [])
+ return UUID(tasks[0]["id"]) if tasks else None
+ except Exception as e:
+ self.log.warning("Failed to find awaiting QA task", error=str(e))
+ return None
+
+ async def _get_task_commits_formatted(self, task_id: UUID) -> str:
+ """Get commits for the task as formatted string."""
+ commits = await self._get_task_commits(task_id)
+ return "\n".join(commits) if commits else "No commits recorded"
diff --git a/roboco/mcp/tasks/handlers/claim.py b/roboco/mcp/tasks/handlers/claim.py
index 51d5d668..1e3ee0b2 100644
--- a/roboco/mcp/tasks/handlers/claim.py
+++ b/roboco/mcp/tasks/handlers/claim.py
@@ -12,26 +12,50 @@ from roboco.mcp.tasks.handlers._helpers import (
check_blocking_tasks,
fetch_task_or_error,
get_project_context,
+ resolve_agent_uuid_cached,
validate_task_claimable,
)
from roboco.mcp.utils import ApiClient, format_error_response
-async def _check_active_tasks(client: ApiClient) -> dict[str, Any] | None:
+async def _check_active_tasks(
+ client: ApiClient, exclude_task_id: str | None = None
+) -> dict[str, Any] | None:
"""Check for blocking tasks. Returns error or None.
- Note: Paused tasks no longer block claiming. Agents can verify why
- a task is paused (via roboco_task_scan) and decide to resume it
- or claim new work if it's legitimately waiting on something.
+ Args:
+ client: API client
+ exclude_task_id: Task ID to exclude from blocking check. Used when
+ the agent is claiming a task already assigned to them.
"""
active_resp = await client.get("/tasks/my")
if not active_resp.ok:
return None
active_tasks = active_resp.json()
- # Only block on in_progress tasks, not paused ones
+
+ # Exclude the task being claimed if specified
+ if exclude_task_id:
+ active_tasks = [t for t in active_tasks if str(t.get("id")) != exclude_task_id]
+
return check_blocking_tasks(active_tasks)
+async def _is_pre_assigned_to_agent(
+ task: dict[str, Any], agent_id: str, client: ApiClient
+) -> bool:
+ """Check if task is pre-assigned to this agent (PM assigned before claim)."""
+ assigned_to = task.get("assigned_to")
+ if not assigned_to:
+ return False
+
+ # Task must be pending (not yet claimed)
+ if task.get("status") != "pending":
+ return False
+
+ agent_uuid = await resolve_agent_uuid_cached(agent_id, client)
+ return agent_uuid is not None and str(assigned_to) == agent_uuid
+
+
async def _execute_claim(
client: ApiClient, task_id: str, agent_id: str
) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
@@ -51,19 +75,36 @@ async def _execute_claim(
async def handle_task_claim(
client: ApiClient, task_id: str, agent_id: str
) -> dict[str, Any]:
- """Handle task claiming."""
- if error := await _check_active_tasks(client):
- return error
+ """Handle task claiming.
+ Flow:
+ 1. Fetch the task first
+ 2. Check if it's pre-assigned to this agent (PM assigned directly)
+ 3. If pre-assigned, skip blocking check for THIS task
+ 4. Otherwise, run full blocking check
+ 5. Validate task is claimable for this role
+ 6. Execute claim
+ """
+ # Fetch task first - we need to check if it's pre-assigned
task, error = await fetch_task_or_error(client, task_id)
if error:
return error
assert task is not None
+ # Check if this task is pre-assigned to the agent
+ is_pre_assigned = await _is_pre_assigned_to_agent(task, agent_id, client)
+
+ # Check for blocking tasks (exclude this task if pre-assigned)
+ exclude_id = task_id if is_pre_assigned else None
+ if error := await _check_active_tasks(client, exclude_task_id=exclude_id):
+ return error
+
+ # Validate task can be claimed by this role
agent_role = get_agent_role(agent_id)
if error := await validate_task_claimable(task, agent_role, agent_id, client):
return error
+ # Execute the claim
claimed_task, error = await _execute_claim(client, task_id, agent_id)
if error:
return error