# DevClaw **Every group chat becomes an autonomous development team.** Add the agent to a Telegram group, point it at a GitLab repo — that group now has an **orchestrator** managing the backlog, a **DEV** worker session writing code, and a **QA** worker session reviewing it. All autonomous. Add another group, get another team. Each project runs in complete isolation with its own task queue, workers, and session state. DevClaw is the [OpenClaw](https://openclaw.ai) plugin that makes this work. ## Why [OpenClaw](https://openclaw.ai) is great at giving AI agents the ability to develop software — spawn worker sessions, manage sessions, work with code. But running a real multi-project development pipeline exposes a gap: the orchestration layer between "agent can write code" and "agent reliably manages multiple projects" is brittle. Every task involves 10+ coordinated steps across GitLab labels, session state, model selection, and audit logging. Agents forget steps, corrupt state, null out session IDs they should preserve, or pick the wrong model for the job. DevClaw fills that gap with guardrails. It gives the orchestrator atomic tools that make it impossible to forget a label transition, lose a session reference, or skip an audit log entry. The complexity of multi-project orchestration moves from agent instructions (that LLMs follow imperfectly) into deterministic code (that runs the same way every time). ## The idea One orchestrator agent manages all your projects. It reads task backlogs, creates issues, decides priorities, and delegates work. For each task, DevClaw creates (or reuses) a **DEV** worker session to write code or a **QA** worker session to review it. Every Telegram group is a separate project — the orchestrator keeps them completely isolated while managing them all from a single process. DevClaw gives the orchestrator six tools that replace hundreds of lines of manual orchestration logic. Instead of following a 10-step checklist per task (fetch issue, check labels, pick model, check for existing session, transition label, dispatch task, update state, log audit event...), it calls `task_pickup` and the plugin handles everything atomically — including session dispatch. Workers call `task_complete` themselves for atomic state updates, and can file follow-up issues via `task_create`. ## How it works ```mermaid graph TB subgraph "Group Chat A" direction TB A_O["🎯 Orchestrator"] A_GL[GitLab Issues] A_DEV["🔧 DEV (worker session)"] A_QA["🔍 QA (worker session)"] A_O -->|task_pickup| A_GL A_O -->|task_pickup dispatches| A_DEV A_O -->|task_pickup dispatches| A_QA end subgraph "Group Chat B" direction TB B_O["🎯 Orchestrator"] B_GL[GitLab Issues] B_DEV["🔧 DEV (worker session)"] B_QA["🔍 QA (worker session)"] B_O -->|task_pickup| B_GL B_O -->|task_pickup dispatches| B_DEV B_O -->|task_pickup dispatches| B_QA end subgraph "Group Chat C" direction TB C_O["🎯 Orchestrator"] C_GL[GitLab Issues] C_DEV["🔧 DEV (worker session)"] C_QA["🔍 QA (worker session)"] C_O -->|task_pickup| C_GL C_O -->|task_pickup dispatches| C_DEV C_O -->|task_pickup dispatches| C_QA end AGENT["Single OpenClaw Agent"] AGENT --- A_O AGENT --- B_O AGENT --- C_O ``` It's the same agent process — but each group chat gives it a different project context. The orchestrator role, the workers, the task queue, and all state are fully isolated per group. ## Task lifecycle Every task (GitLab issue) moves through a fixed pipeline of label states. Issues are created by the orchestrator agent or by worker sessions — not manually. DevClaw tools handle every transition atomically — label change, state update, audit log, and session management in a single call. ```mermaid stateDiagram-v2 [*] --> Planning Planning --> ToDo: Ready for development ToDo --> Doing: task_pickup (DEV) Doing --> ToTest: task_complete (DEV done) ToTest --> Testing: task_pickup (QA) or auto-chain Testing --> Done: task_complete (QA pass) Testing --> ToImprove: task_complete (QA fail) Testing --> Refining: task_complete (QA refine) ToImprove --> Doing: task_pickup (DEV fix) or auto-chain Refining --> ToDo: Human decision Done --> [*] ``` ### Worker self-reporting Workers (DEV/QA sub-agent sessions) call `task_complete` directly when they finish — no orchestrator involvement needed for the state transition. Workers can also call `task_create` to file follow-up issues they discover during work. ### Auto-chaining When a project has `autoChain: true`, `task_complete` automatically dispatches the next step: - **DEV "done"** → QA is dispatched immediately (default model: grok) - **QA "fail"** → DEV fix is dispatched immediately (reuses previous DEV model) - **QA "pass" / "refine"** → no chaining (pipeline done or needs human input) When `autoChain` is false, `task_complete` returns a `nextAction` hint for the orchestrator to act on. ## Session reuse Worker sessions are expensive to start — each new spawn requires the session to read the full codebase (~50K tokens). DevClaw maintains **separate sessions per model per role** (session-per-model design). When a DEV finishes task A and picks up task B on the same project with the same model, the plugin detects the existing session and sends the task directly — no new session needed. The plugin handles session dispatch internally via OpenClaw CLI. The orchestrator agent never calls `sessions_spawn` or `sessions_send` — it just calls `task_pickup` and the plugin does the rest. ```mermaid sequenceDiagram participant O as Orchestrator participant DC as DevClaw Plugin participant GL as GitLab participant S as Worker Session O->>DC: task_pickup({ issueId: 42, role: "dev" }) DC->>GL: Fetch issue, verify label DC->>DC: Select model (haiku/sonnet/opus) DC->>DC: Check existing session for selected model DC->>GL: Transition label (To Do → Doing) DC->>S: Dispatch task via CLI (create or reuse session) DC->>DC: Update projects.json, write audit log DC-->>O: { success: true, announcement: "🔧 DEV (sonnet) picking up #42" } ``` ## Model selection The orchestrator LLM analyzes each issue's title, description, and labels to choose the appropriate model tier, then passes it to `task_pickup` via the `model` parameter. This gives the LLM full context for the decision — it can weigh factors like codebase familiarity, task dependencies, and recent failure history that keyword matching would miss. The keyword heuristic in `model-selector.ts` serves as a **fallback only**, used when the orchestrator omits the `model` parameter. | Complexity | Model | When | |------------|-------|------| | Simple | Haiku | Typos, CSS, renames, copy changes | | Standard | Sonnet | Features, bug fixes, multi-file changes | | Complex | Opus | Architecture, migrations, security, system-wide refactoring | | QA | Grok | All QA tasks (code review, test validation) | ## State management All project state lives in a single `memory/projects.json` file in the orchestrator's workspace, keyed by Telegram group ID: ```json { "projects": { "-1234567890": { "name": "my-webapp", "repo": "~/git/my-webapp", "groupName": "Dev - My Webapp", "baseBranch": "development", "autoChain": true, "dev": { "active": false, "issueId": null, "model": "haiku", "sessions": { "haiku": "agent:orchestrator:subagent:a9e4d078-...", "sonnet": "agent:orchestrator:subagent:b3f5c912-...", "opus": null } }, "qa": { "active": false, "issueId": null, "model": "grok", "sessions": { "grok": "agent:orchestrator:subagent:18707821-..." } } } } } ``` Key design decisions: - **Session-per-model** — each model gets its own worker session, accumulating context independently. Model selection maps directly to a session key. - **Sessions preserved on completion** — when a worker completes a task, `sessions` map is **preserved** (only `active` and `issueId` are cleared). This enables session reuse on the next pickup. - **Plugin-controlled dispatch** — the plugin creates and dispatches to sessions via OpenClaw CLI (`sessions.patch` + `openclaw agent`). The orchestrator agent never calls `sessions_spawn` or `sessions_send`. - **Sessions persist indefinitely** — no auto-cleanup. `session_health` handles manual cleanup when needed. All writes go through atomic temp-file-then-rename to prevent corruption. ## Tools ### `task_pickup` Pick up a task from the GitLab queue for a DEV or QA worker. **Parameters:** - `issueId` (number, required) — GitLab issue ID - `role` ("dev" | "qa", required) — Worker role - `projectGroupId` (string, required) — Telegram group ID - `model` (string, optional) — Model alias to use (e.g. haiku, sonnet, opus, grok). The orchestrator should analyze the issue complexity and choose. Falls back to keyword heuristic if omitted. **What it does atomically:** 1. Resolves project from `projects.json` 2. Validates no active worker for this role 3. Fetches issue from issue tracker, verifies correct label state 4. Selects model (LLM-chosen via `model` param, keyword heuristic fallback) 5. Loads role instructions from `roles//.md` (fallback: `roles/default/.md`) 6. Looks up existing session for selected model (session-per-model) 7. Transitions label (e.g. `To Do` → `Doing`) 8. Creates session via Gateway RPC if new (`sessions.patch`) 9. Dispatches task to worker session via CLI (`openclaw agent`) with role instructions appended 10. Updates `projects.json` state (active, issueId, model, session key) 11. Writes audit log entry 12. Returns announcement text for the orchestrator to post ### `task_complete` Complete a task with one of four results. Called by workers (DEV/QA sub-agent sessions) directly, or by the orchestrator. **Parameters:** - `role` ("dev" | "qa", required) - `result` ("done" | "pass" | "fail" | "refine", required) - `projectGroupId` (string, required) - `summary` (string, optional) — For the Telegram announcement **Results:** - **DEV "done"** — Pulls latest code, moves label `Doing` → `To Test`, deactivates worker. If `autoChain` enabled, automatically dispatches QA (grok). - **QA "pass"** — Moves label `Testing` → `Done`, closes issue, deactivates worker - **QA "fail"** — Moves label `Testing` → `To Improve`, reopens issue. If `autoChain` enabled, automatically dispatches DEV fix (reuses previous model). - **QA "refine"** — Moves label `Testing` → `Refining`, awaits human decision ### `task_create` Create a new issue in the project's issue tracker. Used by workers to file follow-up bugs, or by the orchestrator to create tasks from chat. **Parameters:** - `projectGroupId` (string, required) — Telegram group ID - `title` (string, required) — Issue title - `description` (string, optional) — Full issue body in markdown - `label` (string, optional) — State label (defaults to "Planning") - `assignees` (string[], optional) — Usernames to assign - `pickup` (boolean, optional) — If true, immediately pick up for DEV after creation ### `queue_status` Returns task queue counts and worker status across all projects (or a specific one). **Parameters:** - `projectGroupId` (string, optional) — Omit for all projects ### `session_health` Detects and optionally fixes state inconsistencies. **Parameters:** - `autoFix` (boolean, optional) — Auto-fix zombies and stale state **What it does:** - Queries live sessions via Gateway RPC (`sessions.list`) - Cross-references with `projects.json` worker state **Checks:** - Active worker with no session key (critical) - Active worker whose session is dead — zombie (critical) - Worker active for >2 hours (warning) - Inactive worker with lingering issue ID (warning) ### `project_register` Register a new project with DevClaw. Creates all required issue tracker labels (idempotent), scaffolds role instruction files, and adds the project to `projects.json`. One-time setup per project. Auto-detects GitHub/GitLab from git remote. **Parameters:** - `projectGroupId` (string, required) — Telegram group ID (key in projects.json) - `name` (string, required) — Short project name - `repo` (string, required) — Path to git repo (e.g. `~/git/my-project`) - `groupName` (string, required) — Telegram group display name - `baseBranch` (string, required) — Base branch for development - `deployBranch` (string, optional) — Defaults to baseBranch - `deployUrl` (string, optional) — Deployment URL **What it does atomically:** 1. Validates project not already registered 2. Resolves repo path, auto-detects GitHub/GitLab, and verifies access 3. Creates all 8 state labels (idempotent — safe to run on existing projects) 4. Adds project entry to `projects.json` with empty worker state and `autoChain: false` 5. Scaffolds role instruction files: `roles//dev.md` and `roles//qa.md` (copied from `roles/default/`) 6. Writes audit log entry 7. Returns announcement text ## Audit logging Every tool call automatically appends an NDJSON entry to `memory/audit.log`. No manual logging required from the orchestrator agent. ```jsonl {"ts":"2026-02-08T10:30:00Z","event":"task_pickup","project":"my-webapp","issue":42,"role":"dev","model":"sonnet","sessionAction":"send"} {"ts":"2026-02-08T10:30:01Z","event":"model_selection","issue":42,"role":"dev","selected":"sonnet","reason":"Standard dev task"} {"ts":"2026-02-08T10:45:00Z","event":"task_complete","project":"my-webapp","issue":42,"role":"dev","result":"done"} ``` ## Installation ```bash # Local (place in extensions directory — auto-discovered) cp -r devclaw ~/.openclaw/extensions/ # From npm (future) openclaw plugins install @openclaw/devclaw ``` ## Configuration Optional config in `openclaw.json`: ```json { "plugins": { "entries": { "devclaw": { "config": { "glabPath": "/usr/local/bin/glab" } } } } } ``` Restrict tools to your orchestrator agent only: ```json { "agents": { "list": [{ "id": "my-orchestrator", "tools": { "allow": ["task_pickup", "task_complete", "task_create", "queue_status", "session_health", "project_register"] } }] } } ``` > DevClaw uses an `IssueProvider` interface to abstract issue tracker operations. GitLab (via `glab` CLI) and GitHub (via `gh` CLI) are supported — the provider is auto-detected from the git remote URL. Jira is planned. ## Role instructions Workers receive role-specific instructions appended to their task message. `project_register` scaffolds editable files: ``` workspace/ ├── roles/ │ ├── default/ ← sensible defaults (created once) │ │ ├── dev.md │ │ └── qa.md │ ├── my-webapp/ ← per-project overrides (edit to customize) │ │ ├── dev.md │ │ └── qa.md │ └── another-project/ │ ├── dev.md │ └── qa.md ``` `task_pickup` loads `roles//.md` with fallback to `roles/default/.md`. Edit the per-project files to customize worker behavior — for example, adding project-specific deployment steps or test commands. ## Requirements - [OpenClaw](https://openclaw.ai) - Node.js >= 20 - [`glab`](https://gitlab.com/gitlab-org/cli) CLI installed and authenticated (GitLab provider), or [`gh`](https://cli.github.com) CLI (GitHub provider) - A `memory/projects.json` in the orchestrator agent's workspace ## License MIT