feat: refactor model selection to use developer tiers

- Replaced raw model aliases with developer tiers (junior, medior, senior, qa) in dispatch and model selection logic.
- Updated `dispatchTask` to resolve models based on tiers and plugin configuration.
- Modified `selectModel` to return tier names instead of model aliases based on task description.
- Implemented migration logic for transitioning from old model aliases to new tier names in worker state.
- Added setup logic for agent creation and model configuration in `setup.ts`.
- Created shared templates for workspace files and instructions for DEV/QA workers.
- Enhanced project registration to scaffold role files based on developer tiers.
- Updated task management tools to reflect changes in model selection and tier assignment.
- Introduced a new `devclaw_setup` tool for agent-driven setup and configuration.
- Updated plugin configuration schema to support model mapping per developer tier.
This commit is contained in:
Lauren ten Hoor
2026-02-09 13:41:22 +08:00
parent 8a79755e4c
commit aa8e8dbd1b
16 changed files with 1162 additions and 257 deletions

View File

@@ -8,28 +8,28 @@ Understanding the OpenClaw model is key to understanding how DevClaw works:
- **Session** — A runtime conversation instance. Each session has its own context window and conversation history, stored as a `.jsonl` transcript file.
- **Sub-agent session** — A session created under the orchestrator agent for a specific worker role. NOT a separate agent — it's a child session running under the same agent, with its own isolated context. Format: `agent:<parent>:subagent:<uuid>`.
### Session-per-model design
### Session-per-tier design
Each project maintains **separate sessions per model per role**. A project's DEV might have a Haiku session, a Sonnet session, and an Opus session — each accumulating its own codebase context over time.
Each project maintains **separate sessions per developer tier per role**. A project's DEV might have a junior session, a medior session, and a senior session — each accumulating its own codebase context over time.
```
Orchestrator Agent (configured in openclaw.json)
└─ Main session (long-lived, handles all projects)
├─ Project A
│ ├─ DEV sessions: { haiku: <uuid>, sonnet: <uuid>, opus: null }
│ └─ QA sessions: { grok: <uuid> }
│ ├─ DEV sessions: { junior: <uuid>, medior: <uuid>, senior: null }
│ └─ QA sessions: { qa: <uuid> }
└─ Project B
├─ DEV sessions: { haiku: null, sonnet: <uuid>, opus: null }
└─ QA sessions: { grok: <uuid> }
├─ DEV sessions: { junior: null, medior: <uuid>, senior: null }
└─ QA sessions: { qa: <uuid> }
```
Why per-model instead of switching models on one session:
Why per-tier instead of switching models on one session:
- **No model switching overhead** — each session always uses the same model
- **Accumulated context** — a Haiku session that's done 20 typo fixes knows the project well; a Sonnet session that's done 5 features knows it differently
- **Accumulated context** — a junior session that's done 20 typo fixes knows the project well; a medior session that's done 5 features knows it differently
- **No cross-model confusion** — conversation history stays with the model that generated it
- **Deterministic reuse** — model selection directly maps to a session key, no patching needed
- **Deterministic reuse** — tier selection directly maps to a session key, no patching needed
### Plugin-controlled session lifecycle
@@ -37,14 +37,14 @@ DevClaw controls the **full** session lifecycle end-to-end. The orchestrator age
```
Plugin dispatch (inside task_pickup):
1. Select model, look up session, decide spawn vs send
1. Assign tier, look up session, decide spawn vs send
2. New session: openclaw gateway call sessions.patch → create entry + set model
openclaw agent --session-id <key> --message "task..."
3. Existing: openclaw agent --session-id <key> --message "task..."
4. Return result to orchestrator (announcement text, no session instructions)
```
The agent's only job after `task_pickup` returns is to post the announcement to Telegram. Everything else — model selection, session creation, task dispatch, state update, audit logging — is deterministic plugin code.
The agent's only job after `task_pickup` returns is to post the announcement to Telegram. Everything else — tier assignment, session creation, task dispatch, state update, audit logging — is deterministic plugin code.
**Why this matters:** Previously the plugin returned instructions like `{ sessionAction: "spawn", model: "sonnet" }` and the agent had to correctly call `sessions_spawn` with the right params. This was the fragile handoff point where agents would forget `cleanup: "keep"`, use wrong models, or corrupt session state. Moving dispatch into the plugin eliminates that entire class of errors.
@@ -75,10 +75,10 @@ graph TB
MS[Main Session<br/>orchestrator agent]
GW[Gateway RPC<br/>sessions.patch / sessions.list]
CLI[openclaw agent CLI]
DEV_H[DEV session<br/>haiku]
DEV_S[DEV session<br/>sonnet]
DEV_O[DEV session<br/>opus]
QA_G[QA session<br/>grok]
DEV_J[DEV session<br/>junior]
DEV_M[DEV session<br/>medior]
DEV_S[DEV session<br/>senior]
QA_E[QA session<br/>qa]
end
subgraph "DevClaw Plugin"
@@ -88,13 +88,14 @@ graph TB
QS[queue_status]
SH[session_health]
PR[project_register]
MS_SEL[Model Selector]
DS[devclaw_setup]
TIER[Tier Resolver]
PJ[projects.json]
AL[audit.log]
end
subgraph "External"
GL[GitLab]
GL[Issue Tracker]
REPO[Git Repository]
end
@@ -108,8 +109,9 @@ graph TB
MS -->|calls| QS
MS -->|calls| SH
MS -->|calls| PR
MS -->|calls| DS
TP -->|selects model| MS_SEL
TP -->|resolves tier| TIER
TP -->|transitions labels| GL
TP -->|reads/writes| PJ
TP -->|appends| AL
@@ -139,15 +141,15 @@ graph TB
PR -->|writes entry| PJ
PR -->|appends| AL
CLI -->|sends task| DEV_H
CLI -->|sends task| DEV_J
CLI -->|sends task| DEV_M
CLI -->|sends task| DEV_S
CLI -->|sends task| DEV_O
CLI -->|sends task| QA_G
CLI -->|sends task| QA_E
DEV_H -->|writes code, creates MRs| REPO
DEV_J -->|writes code, creates MRs| REPO
DEV_M -->|writes code, creates MRs| REPO
DEV_S -->|writes code, creates MRs| REPO
DEV_O -->|writes code, creates MRs| REPO
QA_G -->|reviews code, tests| REPO
QA_E -->|reviews code, tests| REPO
```
## End-to-end flow: human to sub-agent
@@ -162,8 +164,8 @@ sequenceDiagram
participant DC as DevClaw Plugin
participant GW as Gateway RPC
participant CLI as openclaw agent CLI
participant DEV as DEV Session<br/>(sonnet)
participant GL as GitLab
participant DEV as DEV Session<br/>(medior)
participant GL as Issue Tracker
Note over H,GL: Issue exists in queue (To Do)
@@ -173,49 +175,49 @@ sequenceDiagram
DC->>GL: glab issue list --label "To Do"
DC-->>MS: { toDo: [#42], dev: idle }
Note over MS: Decides to pick up #42 for DEV
Note over MS: Decides to pick up #42 for DEV as medior
MS->>DC: task_pickup({ issueId: 42, role: "dev", ... })
DC->>DC: selectModel → "sonnet"
DC->>DC: lookup dev.sessions.sonnet → null (first time)
MS->>DC: task_pickup({ issueId: 42, role: "dev", model: "medior", ... })
DC->>DC: resolve tier "medior" → model ID
DC->>DC: lookup dev.sessions.medior → null (first time)
DC->>GL: glab issue update 42 --unlabel "To Do" --label "Doing"
DC->>GW: sessions.patch({ key: new-session-key, model: "sonnet" })
DC->>GW: sessions.patch({ key: new-session-key, model: "anthropic/claude-sonnet-4-5" })
DC->>CLI: openclaw agent --session-id <key> --message "Build login page for #42..."
CLI->>DEV: creates session, delivers task
DC->>DC: store session key in projects.json + append audit.log
DC-->>MS: { success: true, announcement: "🔧 DEV (sonnet) picking up #42" }
DC-->>MS: { success: true, announcement: "🔧 DEV (medior) picking up #42" }
MS->>TG: "🔧 DEV (sonnet) picking up #42: Add login page"
MS->>TG: "🔧 DEV (medior) picking up #42: Add login page"
TG->>H: sees announcement
Note over DEV: Works autonomously — reads code, writes code, creates MR
Note over MS: Heartbeat detects DEV session idle → triggers task_complete
Note over DEV: Calls task_complete when done
MS->>DC: task_complete({ role: "dev", result: "done", ... })
DEV->>DC: task_complete({ role: "dev", result: "done", ... })
DC->>GL: glab issue update 42 --unlabel "Doing" --label "To Test"
DC->>DC: deactivate worker (sessions preserved)
DC-->>MS: { announcement: "✅ DEV done #42" }
DC-->>DEV: { announcement: "✅ DEV done #42" }
MS->>TG: "✅ DEV done #42 — moved to QA queue"
TG->>H: sees announcement
```
On the **next DEV task** for this project that also selects Sonnet:
On the **next DEV task** for this project that also assigns medior:
```mermaid
sequenceDiagram
participant MS as Main Session
participant DC as DevClaw Plugin
participant CLI as openclaw agent CLI
participant DEV as DEV Session<br/>(sonnet, existing)
participant DEV as DEV Session<br/>(medior, existing)
MS->>DC: task_pickup({ issueId: 57, role: "dev", ... })
DC->>DC: selectModel → "sonnet"
DC->>DC: lookup dev.sessions.sonnet → existing key!
MS->>DC: task_pickup({ issueId: 57, role: "dev", model: "medior", ... })
DC->>DC: resolve tier "medior" → model ID
DC->>DC: lookup dev.sessions.medior → existing key!
Note over DC: No sessions.patch needed — session already exists
DC->>CLI: openclaw agent --session-id <key> --message "Fix validation for #57..."
CLI->>DEV: delivers task to existing session (has full codebase context)
DC-->>MS: { success: true, announcement: "⚡ DEV (sonnet) picking up #57" }
DC-->>MS: { success: true, announcement: "⚡ DEV (medior) picking up #57" }
```
Session reuse saves ~50K tokens per task by not re-reading the codebase.
@@ -229,10 +231,10 @@ This traces a single issue from creation to completion, showing every component
Issues are created by the orchestrator agent or by sub-agent sessions via `glab`. The orchestrator can create issues based on user requests in Telegram, backlog planning, or QA feedback. Sub-agents can also create issues when they discover bugs or related work during development.
```
Orchestrator Agent → GitLab: creates issue #42 with label "To Do"
Orchestrator Agent → Issue Tracker: creates issue #42 with label "To Do"
```
**State:** GitLab has issue #42 labeled "To Do". Nothing in DevClaw yet.
**State:** Issue tracker has issue #42 labeled "To Do". Nothing in DevClaw yet.
### Phase 2: Heartbeat detects work
@@ -244,7 +246,7 @@ Heartbeat triggers → Orchestrator calls queue_status()
sequenceDiagram
participant A as Orchestrator
participant QS as queue_status
participant GL as GitLab
participant GL as Issue Tracker
participant PJ as projects.json
participant AL as audit.log
@@ -261,33 +263,33 @@ sequenceDiagram
QS-->>A: { dev: idle, queue: { toDo: [#42] } }
```
**Orchestrator decides:** DEV is idle, issue #42 is in To Do → pick it up.
**Orchestrator decides:** DEV is idle, issue #42 is in To Do → pick it up. Evaluates complexity → assigns medior tier.
### Phase 3: DEV pickup
The plugin handles everything end-to-end — model selection, session lookup, label transition, state update, **and** task dispatch to the worker session. The agent's only job after is to post the announcement.
The plugin handles everything end-to-end — tier resolution, session lookup, label transition, state update, **and** task dispatch to the worker session. The agent's only job after is to post the announcement.
```mermaid
sequenceDiagram
participant A as Orchestrator
participant TP as task_pickup
participant GL as GitLab
participant MS as Model Selector
participant GL as Issue Tracker
participant TIER as Tier Resolver
participant GW as Gateway RPC
participant CLI as openclaw agent CLI
participant PJ as projects.json
participant AL as audit.log
A->>TP: task_pickup({ issueId: 42, role: "dev", projectGroupId: "-123" })
A->>TP: task_pickup({ issueId: 42, role: "dev", projectGroupId: "-123", model: "medior" })
TP->>PJ: readProjects()
TP->>GL: glab issue view 42 --output json
GL-->>TP: { title: "Add login page", labels: ["To Do"] }
TP->>TP: Verify label is "To Do" ✓
TP->>TP: model from agent param (LLM-selected) or fallback heuristic
TP->>PJ: lookup dev.sessions.sonnet
TP->>TIER: resolve "medior" → "anthropic/claude-sonnet-4-5"
TP->>PJ: lookup dev.sessions.medior
TP->>GL: glab issue update 42 --unlabel "To Do" --label "Doing"
alt New session
TP->>GW: sessions.patch({ key: new-key, model: "sonnet" })
TP->>GW: sessions.patch({ key: new-key, model: "anthropic/claude-sonnet-4-5" })
end
TP->>CLI: openclaw agent --session-id <key> --message "task..."
TP->>PJ: activateWorker + store session key
@@ -296,8 +298,8 @@ sequenceDiagram
```
**Writes:**
- `GitLab`: label "To Do" → "Doing"
- `projects.json`: dev.active=true, dev.issueId="42", dev.model="sonnet", dev.sessions.sonnet=key
- `Issue Tracker`: label "To Do" → "Doing"
- `projects.json`: dev.active=true, dev.issueId="42", dev.model="medior", dev.sessions.medior=key
- `audit.log`: 2 entries (task_pickup, model_selection)
- `Session`: task message delivered to worker session via CLI
@@ -316,7 +318,7 @@ This happens inside the OpenClaw session. The worker calls `task_complete` direc
sequenceDiagram
participant DEV as DEV Session
participant TC as task_complete
participant GL as GitLab
participant GL as Issue Tracker
participant PJ as projects.json
participant AL as audit.log
participant REPO as Git Repo
@@ -333,7 +335,7 @@ sequenceDiagram
alt autoChain enabled
TC->>GL: transition label "To Test" → "Testing"
TC->>QA: dispatchTask(role: "qa", model: "grok")
TC->>QA: dispatchTask(role: "qa", tier: "qa")
TC->>PJ: activateWorker(-123, qa)
TC-->>DEV: { announcement: "✅ DEV done #42", autoChain: { dispatched: true, role: "qa" } }
else autoChain disabled
@@ -344,12 +346,12 @@ sequenceDiagram
**Writes:**
- `Git repo`: pulled latest (has DEV's merged code)
- `projects.json`: dev.active=false, dev.issueId=null (sessions map preserved for reuse)
- `GitLab`: label "Doing" → "To Test" (+ "To Test" → "Testing" if auto-chain)
- `Issue Tracker`: label "Doing" → "To Test" (+ "To Test" → "Testing" if auto-chain)
- `audit.log`: 1 entry (task_complete) + optional auto-chain entries
### Phase 6: QA pickup
Same as Phase 3, but with `role: "qa"`. Label transitions "To Test" → "Testing". Model defaults to Grok for QA.
Same as Phase 3, but with `role: "qa"`. Label transitions "To Test" → "Testing". Uses the qa tier.
### Phase 7: QA result (3 possible outcomes)
@@ -359,7 +361,7 @@ Same as Phase 3, but with `role: "qa"`. Label transitions "To Test" → "Testing
sequenceDiagram
participant A as Orchestrator
participant TC as task_complete
participant GL as GitLab
participant GL as Issue Tracker
participant PJ as projects.json
participant AL as audit.log
@@ -379,8 +381,7 @@ sequenceDiagram
sequenceDiagram
participant A as Orchestrator
participant TC as task_complete
participant GL as GitLab
participant MS as Model Selector
participant GL as Issue Tracker
participant PJ as projects.json
participant AL as audit.log
@@ -421,12 +422,12 @@ sequenceDiagram
A->>QS: queue_status()
QS-->>A: { projects: [{ dev: idle, queue: { toDo: [#43], toTest: [#44] } }] }
Note over A: DEV idle + To Do #43 → pick up
A->>TP: task_pickup({ issueId: 43, role: "dev", ... })
Note over TP: Plugin handles everything:<br/>model select → session lookup →<br/>label transition → dispatch task →<br/>state update → audit log
Note over A: DEV idle + To Do #43 → assign medior
A->>TP: task_pickup({ issueId: 43, role: "dev", model: "medior", ... })
Note over TP: Plugin handles everything:<br/>tier resolve → session lookup →<br/>label transition → dispatch task →<br/>state update → audit log
Note over A: QA idle + To Test #44 → pick up
A->>TP: task_pickup({ issueId: 44, role: "qa", ... })
Note over A: QA idle + To Test #44 → assign qa
A->>TP: task_pickup({ issueId: 44, role: "qa", model: "qa", ... })
```
## Data flow map
@@ -447,7 +448,8 @@ Every piece of data and where it lives:
┌─────────────────────────────────────────────────────────────────┐
│ DevClaw Plugin (orchestration logic) │
│ │
task_pickup model + label + dispatch + role instr (e2e)
devclaw_setup → agent creation + workspace + model config
│ task_pickup → tier + label + dispatch + role instr (e2e) │
│ task_complete → label + state + git pull + auto-chain │
│ task_create → create issue in tracker │
│ queue_status → read labels + read state │
@@ -462,13 +464,13 @@ Every piece of data and where it lives:
│ dev: │ │ openclaw gateway call │
│ active, issueId, model │ │ sessions.patch → create │
│ sessions: │ │ sessions.list → health │
haiku: <key> │ │ sessions.delete → cleanup │
sonnet: <key> │ │ │
opus: <key> │ │ openclaw agent │
junior: <key> │ │ sessions.delete → cleanup │
medior: <key> │ │ │
senior: <key> │ │ openclaw agent │
│ qa: │ │ --session-id <key> │
│ active, issueId, model │ │ --message "task..." │
│ sessions: │ │ → dispatches to session │
grok: <key> │ │ │
qa: <key> │ │ │
└────────────────────────────────┘ └──────────────────────────────┘
↕ append-only
┌─────────────────────────────────────────────────────────────────┐
@@ -477,7 +479,7 @@ Every piece of data and where it lives:
│ NDJSON, one line per event: │
│ task_pickup, task_complete, model_selection, │
│ queue_status, health_check, session_spawn, session_reuse, │
│ project_register
│ project_register, devclaw_setup
│ │
│ Query with: cat audit.log | jq 'select(.event=="task_pickup")' │
└─────────────────────────────────────────────────────────────────┘
@@ -486,8 +488,8 @@ Every piece of data and where it lives:
│ Telegram (user-facing messages) │
│ │
│ Per group chat: │
│ "🔧 Spawning DEV (sonnet) for #42: Add login page" │
│ "⚡ Sending DEV (sonnet) for #57: Fix validation" │
│ "🔧 Spawning DEV (medior) for #42: Add login page" │
│ "⚡ Sending DEV (medior) for #57: Fix validation" │
│ "✅ DEV done #42 — Login page with OAuth. Moved to QA queue."│
│ "🎉 QA PASS #42. Issue closed." │
│ "❌ QA FAIL #42 — OAuth redirect broken. Sent back to DEV." │
@@ -512,6 +514,7 @@ graph LR
L[Label transitions]
S[Worker state]
PR[Project registration]
SETUP[Agent + workspace setup]
SD[Session dispatch<br/>create + send via CLI]
AC[Auto-chaining<br/>DEV→QA, QA fail→DEV]
RI[Role instructions<br/>loaded per project]
@@ -523,7 +526,7 @@ graph LR
MSG[Telegram announcements]
HB[Heartbeat scheduling]
DEC[Task prioritization]
M[Model selection]
M[Developer assignment<br/>junior/medior/senior]
end
subgraph "Sub-agent sessions handle"
@@ -565,7 +568,7 @@ Provider selection is handled by `createProvider()` in `lib/providers/index.ts`.
| Failure | Detection | Recovery |
|---|---|---|
| Session dies mid-task | `session_health` checks via `sessions.list` Gateway RPC | `autoFix`: reverts label, clears active state, removes dead session from sessions map. Next heartbeat picks up task again (creates fresh session for that model). |
| Session dies mid-task | `session_health` checks via `sessions.list` Gateway RPC | `autoFix`: reverts label, clears active state, removes dead session from sessions map. Next heartbeat picks up task again (creates fresh session for that tier). |
| glab command fails | Plugin tool throws error, returns to agent | Agent retries or reports to Telegram group |
| `openclaw agent` CLI fails | Plugin catches error during dispatch | Plugin rolls back: reverts label, clears active state. Returns error to agent for reporting. |
| `sessions.patch` fails | Plugin catches error during session creation | Plugin rolls back label transition. Returns error. No orphaned state. |
@@ -581,7 +584,7 @@ Provider selection is handled by `createProvider()` in `lib/providers/index.ts`.
|---|---|---|
| Plugin source | `~/.openclaw/extensions/devclaw/` | Plugin code |
| Plugin manifest | `~/.openclaw/extensions/devclaw/openclaw.plugin.json` | Plugin registration |
| Agent config | `~/.openclaw/openclaw.json` | Agent definition + tool permissions |
| Agent config | `~/.openclaw/openclaw.json` | Agent definition + tool permissions + tier config |
| Worker state | `~/.openclaw/workspace-<agent>/memory/projects.json` | Per-project DEV/QA state |
| Audit log | `~/.openclaw/workspace-<agent>/memory/audit.log` | NDJSON event log |
| Session transcripts | `~/.openclaw/agents/<agent>/sessions/<uuid>.jsonl` | Conversation history per session |

View File

@@ -9,9 +9,8 @@
| [`glab`](https://gitlab.com/gitlab-org/cli) or [`gh`](https://cli.github.com) CLI | Issue tracker provider (auto-detected from remote) | `glab --version` or `gh --version` |
| CLI authenticated | Plugin calls glab/gh for every label transition | `glab auth status` or `gh auth status` |
| A GitLab/GitHub repo with issues | The task backlog lives in the issue tracker | `glab issue list` or `gh issue list` from your repo |
| An OpenClaw agent with Telegram | The orchestrator agent that will manage projects | Agent defined in `openclaw.json` |
## Setup steps
## Setup
### 1. Install the plugin
@@ -26,35 +25,38 @@ openclaw plugins list
# Should show: DevClaw | devclaw | loaded
```
### 2. Configure your orchestrator agent
### 2. Run setup
In `openclaw.json`, your orchestrator agent needs access to the DevClaw tools:
```json
{
"agents": {
"list": [{
"id": "my-orchestrator",
"name": "Dev Orchestrator",
"model": "anthropic/claude-sonnet-4-5",
"tools": {
"allow": [
"task_pickup",
"task_complete",
"task_create",
"queue_status",
"session_health",
"project_register"
]
}
}]
}
}
```bash
openclaw devclaw setup
```
The agent needs the six DevClaw tools. Session management (`sessions_spawn`, `sessions_send`) is **not needed** — the plugin handles session creation and task dispatch internally via OpenClaw CLI. Workers (DEV/QA sub-agent sessions) also use `task_complete` and `task_create` directly for atomic self-reporting.
The setup wizard walks you through:
### 3. Register your project
1. **Agent** — Create a new orchestrator agent or configure an existing one
2. **Developer team** — Choose which LLM model powers each developer tier:
- **Junior** (fast, cheap tasks) — default: `anthropic/claude-haiku-4-5`
- **Medior** (standard tasks) — default: `anthropic/claude-sonnet-4-5`
- **Senior** (complex tasks) — default: `anthropic/claude-opus-4-5`
- **QA** (code review) — default: `anthropic/claude-sonnet-4-5`
3. **Workspace** — Writes AGENTS.md, HEARTBEAT.md, role templates, and initializes memory
Non-interactive mode:
```bash
# Create new agent with default models
openclaw devclaw setup --new-agent "My Dev Orchestrator" --non-interactive
# Configure existing agent with custom models
openclaw devclaw setup --agent my-orchestrator \
--junior "anthropic/claude-haiku-4-5" \
--senior "anthropic/claude-opus-4-5"
```
### 3. Add the agent to the Telegram group
Add your orchestrator bot to the Telegram group for the project. The agent will now receive messages from this group and can operate on the linked project.
### 4. Register your project
Tell the orchestrator agent to register a new project:
@@ -83,14 +85,14 @@ The agent calls `project_register`, which atomically:
"issueId": null,
"startTime": null,
"model": null,
"sessions": { "haiku": null, "sonnet": null, "opus": null }
"sessions": { "junior": null, "medior": null, "senior": null }
},
"qa": {
"active": false,
"issueId": null,
"startTime": null,
"model": null,
"sessions": { "grok": null }
"sessions": { "qa": null }
}
}
}
@@ -101,10 +103,6 @@ The agent calls `project_register`, which atomically:
**Finding the Telegram group ID:** The group ID is the numeric ID of your Telegram supergroup (a negative number like `-1234567890`). You can find it via the Telegram bot API or from message metadata in OpenClaw logs.
### 4. Add the agent to the Telegram group
Add your orchestrator bot to the Telegram group for the project. The agent will now receive messages from this group and can operate on the linked project.
### 5. Create your first issue
Issues can be created in multiple ways:
@@ -123,7 +121,7 @@ The agent should call `queue_status` and report the "To Do" issue. Then:
> "Pick up issue #1 for DEV"
The agent calls `task_pickup`, which selects a model, transitions the label to "Doing", creates or reuses a worker session, and dispatches the task — all in one call. The agent just posts the announcement.
The agent calls `task_pickup`, which assigns a developer tier, transitions the label to "Doing", creates or reuses a worker session, and dispatches the task — all in one call. The agent just posts the announcement.
## Adding more projects
@@ -131,18 +129,50 @@ Tell the agent to register a new project (step 3) and add the bot to the new Tel
Each project is fully isolated — separate queue, separate workers, separate state.
## Developer tiers
DevClaw assigns tasks to developer tiers instead of raw model names. This makes the system intuitive — you're assigning a "junior dev" to fix a typo, not configuring model parameters.
| Tier | Role | Default model | When to assign |
|------|------|---------------|----------------|
| **junior** | Junior developer | `anthropic/claude-haiku-4-5` | Typos, single-file fixes, CSS changes |
| **medior** | Mid-level developer | `anthropic/claude-sonnet-4-5` | Features, bug fixes, multi-file changes |
| **senior** | Senior developer | `anthropic/claude-opus-4-5` | Architecture, migrations, system-wide refactoring |
| **qa** | QA engineer | `anthropic/claude-sonnet-4-5` | Code review, test validation |
Change which model powers each tier in `openclaw.json`:
```json
{
"plugins": {
"entries": {
"devclaw": {
"config": {
"models": {
"junior": "anthropic/claude-haiku-4-5",
"medior": "anthropic/claude-sonnet-4-5",
"senior": "anthropic/claude-opus-4-5",
"qa": "anthropic/claude-sonnet-4-5"
}
}
}
}
}
}
```
## What the plugin handles vs. what you handle
| Responsibility | Who | Details |
|---|---|---|
| Plugin installation | You (once) | `cp -r devclaw ~/.openclaw/extensions/` |
| Agent + workspace setup | Plugin (`devclaw setup`) | Creates agent, configures models, writes workspace files |
| Label setup | Plugin (`project_register`) | 8 labels, created idempotently via `IssueProvider` |
| Role file scaffolding | Plugin (`project_register`) | Creates `roles/<project>/dev.md` and `qa.md` from defaults |
| Project registration | Plugin (`project_register`) | Entry in `projects.json` with empty worker state |
| Agent definition | You (once) | Agent in `openclaw.json` with tool permissions |
| Telegram group setup | You (once per project) | Add bot to group |
| Issue creation | Plugin (`task_create`) | Orchestrator or workers create issues from chat |
| Label transitions | Plugin | Atomic label transitions via issue tracker CLI |
| Model selection | Plugin | LLM-selected by orchestrator, keyword heuristic fallback |
| Developer assignment | Plugin | LLM-selected tier by orchestrator, keyword heuristic fallback |
| State management | Plugin | Atomic read/write to `projects.json` |
| Session management | Plugin | Creates, reuses, and dispatches to sessions via CLI. Agent never touches session tools. |
| Task completion | Plugin (`task_complete`) | Workers self-report. Auto-chains if enabled. |