agent-teams 0.1.0

Generic Rust agent teams framework replicating Claude Code Agent Teams architecture with pluggable backends for Claude Code, Codex, and Gemini CLI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# Claude Code Agent Teams: Reverse Engineering Report

> Based on forensic analysis of real production data at `~/.claude/teams/` and `~/.claude/tasks/` across 4 teams and 55+ task directories. Every field documented here was verified against actual files on disk. No speculation without evidence.
>
> **Date**: 2026-02-12
> **Claude Code version observed**: 2.1.37
> **Methodology**: Direct file inspection, GitHub issue verification, inbox/task/config file parsing

---

## 1. Filesystem Layout

```
~/.claude/
├── teams/
│   └── {team-name}/
│       ├── config.json              # Team configuration
│       ├── config.json.lock         # Advisory lock for config writes
│       └── inboxes/
│           ├── {agent-name}.json    # Agent inbox (JSON array)
│           └── {agent-name}.lock    # Advisory lock for inbox writes
├── tasks/
│   ├── {team-name}/                 # Named team task directories
│   │   ├── .lock                    # Advisory lock for task operations
│   │   ├── 1.json
│   │   ├── 2.json
│   │   └── ...
│   └── {session-uuid}/             # Session-scoped task directories (non-team)
│       ├── .lock
│       └── *.json
└── projects/
    └── {hash}/
        └── {session-id}.jsonl       # Session transcript (tool calls, messages)
```

**Observed data**:
- 4 team directories: `code-review-agent-teams`, `macos-cleaner-chat`, `macos-cleaner-dev`, `proxy-analysis`
- 55+ task directories (mix of UUID session dirs and named team dirs)
- Lock files are **separate `.lock` companion files**, NOT flock on the main file
- Inbox files: up to 90KB observed (`macos-cleaner-dev/inboxes/team-lead.json`)

---

## 2. Team Configuration (`config.json`)

### 2.1 Full Native Format (TeamCreate)

Observed in `proxy-analysis` and `macos-cleaner-dev` (teams created via Claude Code's native `TeamCreate` tool):

```json
{
  "name": "proxy-analysis",
  "description": "Three @teammates: CC writer + Codex proxy + Gemini proxy",
  "createdAt": 1770836587799,
  "leadAgentId": "team-lead@proxy-analysis",
  "leadSessionId": "a2485d01-5a05-4089-9dd4-32a061a1a1c8",
  "members": [
    {
      "agentId": "team-lead@proxy-analysis",
      "name": "team-lead",
      "agentType": "team-lead",
      "model": "claude-opus-4-6",
      "joinedAt": 1770836587799,
      "tmuxPaneId": "",
      "cwd": "/Users/zhangalex/Work/Projects/FW/rust-claude-code-api",
      "subscriptions": [],
      "backendType": "in-process"
    },
    {
      "agentId": "cc-writer@proxy-analysis",
      "name": "cc-writer",
      "agentType": "general-purpose",
      "model": "claude-opus-4-6",
      "prompt": "你是 @cc-writer...(详细系统指令)",
      "color": "blue",
      "planModeRequired": false,
      "joinedAt": 1770836740207,
      "tmuxPaneId": "in-process",
      "cwd": "/Users/zhangalex/Work/Projects/FW/rust-claude-code-api",
      "subscriptions": [],
      "backendType": "in-process"
    }
  ]
}
```

### 2.2 Complete Field Reference

| Field | Type | Present On | Description |
|-------|------|-----------|-------------|
| `name` | string | team | Team identifier (ASCII, kebab-case) |
| `description` | string | team | Human-readable purpose |
| `createdAt` | number | team | Unix timestamp (milliseconds) |
| `leadAgentId` | string | team | Fully qualified: `"{name}@{team}"` |
| `leadSessionId` | string (UUID) | team | Session UUID of the lead agent |
| `members` | array | team | All members including lead |
| `agentId` | string | member | Fully qualified: `"{name}@{team}"` |
| `name` | string | member | Short name (used in SendMessage, TaskUpdate) |
| `agentType` | string | member | `"team-lead"` or `"general-purpose"` |
| `model` | string | member | e.g. `"claude-opus-4-6"` |
| `prompt` | string | teammate only | System instruction (absent on lead) |
| `color` | string | teammate only | UI color hint: `"blue"`, `"green"`, `"yellow"` |
| `planModeRequired` | boolean | teammate only | Whether agent needs plan approval before execution |
| `joinedAt` | number | member | Unix timestamp (milliseconds) |
| `tmuxPaneId` | string | member | `""` for lead, `"in-process"` for in-process agents |
| `cwd` | string | member | Working directory path |
| `subscriptions` | array | member | Event subscriptions (always `[]` in observed data) |
| `backendType` | string | teammate | `"in-process"` observed; empty/absent on lead |

### 2.3 Simplified Format Variant

Observed in `macos-cleaner-chat` — a team with a different creation path:

```json
{
  "teamName": "macos-cleaner-chat",
  "description": "macOS Cleaner AI Chat",
  "members": [
    {
      "name": "assistant",
      "agentId": "assistant@macos-cleaner-chat",
      "agentType": "claude-code",
      "prompt": "You are the AI assistant for macOS Cleaner..."
    }
  ]
}
```

**Critical difference**: Uses `"teamName"` instead of `"name"`, and lacks `createdAt`, `leadAgentId`, `leadSessionId`, `joinedAt`, `tmuxPaneId`, `cwd`, `subscriptions`, `backendType`, `planModeRequired`, `color` fields.

**Possible explanations**:
1. Created by a different code path (not the native `TeamCreate` tool)
2. An older Claude Code version that used a simpler schema
3. A lightweight "chat team" mode distinct from full agent teams

### 2.4 Agent Identification Convention

```
agentId = "{short-name}@{team-name}"

Examples:
  team-lead@proxy-analysis
  cc-writer@proxy-analysis
  codex-proxy@proxy-analysis
  gemini-proxy@proxy-analysis
```

All inter-agent communication (SendMessage, TaskUpdate owner) uses the **short name** only.

---

## 3. Inbox Message System

### 3.1 File Format

Each agent has an inbox file at `~/.claude/teams/{team}/inboxes/{agent-name}.json` containing a JSON array of messages.

```json
[
  {
    "from": "team-lead",
    "text": "Hi! I see you're working on Task #1...",
    "timestamp": "2026-02-11T08:27:54.622Z",
    "read": true,
    "summary": "Task sequence guidance for setup",
    "color": "blue"
  }
]
```

### 3.2 Message Field Reference

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `from` | string | Yes | Sender short name |
| `text` | string | Yes | Content (plain text or serialized JSON) |
| `timestamp` | string (ISO-8601) | Yes | e.g. `"2026-02-11T19:06:06.012Z"` |
| `read` | boolean | Yes | Whether recipient has processed this message |
| `summary` | string | No | 5-10 word UI preview |
| `color` | string | No | Sender's color from config (`"blue"`, `"green"`, `"yellow"`) |

**Key observation**: There is NO `to` field, NO `id` field, and NO `message_id`. The recipient is implicit from the inbox file name. Messages have no unique identifier for deduplication.

### 3.3 Protocol Messages (JSON-in-JSON)

When the `text` field contains a serialized JSON string, it's a **protocol message**. The outer message is a regular inbox entry; the inner JSON carries structured data.

**Observed protocol types**:

#### `task_assignment`
```json
{
  "type": "task_assignment",
  "taskId": "1",
  "subject": "Set up project structure and Cargo.toml",
  "description": "Create all directories and module files...",
  "assignedBy": "team-lead",
  "timestamp": "2026-02-11T08:27:04.754Z"
}
```

#### `idle_notification`
```json
{
  "type": "idle_notification",
  "from": "cc-writer",
  "timestamp": "2026-02-11T19:08:12.345Z",
  "idleReason": "available"
}
```

**How to distinguish**: Parse `text` as JSON. If it has a `"type"` field, it's a protocol message. Otherwise it's plain text/markdown.

### 3.4 Lazy Inbox Creation

Inbox files are created on first message delivery, NOT pre-allocated at team creation time. In `code-review-agent-teams`, the team config has 1 member (lead only) and the inboxes directory may not exist at all.

### 3.5 Delivery Model

- **No heartbeat**: No periodic keepalive mechanism
- **No ACK**: Only `read: boolean`, no delivery confirmation
- **No message ordering guarantee**: Timestamp is the only ordering hint
- **No message ID**: No deduplication mechanism
- **File-level locking**: `{agent-name}.lock` companion file for concurrent access

---

## 4. Task Files

### 4.1 File Format

Each task is stored as `~/.claude/tasks/{team-or-session}/{id}.json`:

```json
{
  "id": "1",
  "subject": "Set up project structure and Cargo.toml",
  "description": "Create all directories and module files for the project...",
  "activeForm": "Setting up project structure",
  "status": "completed",
  "owner": "setup-specialist",
  "blocks": ["2", "3"],
  "blockedBy": []
}
```

### 4.2 Task Field Reference

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | Numeric string: `"1"`, `"2"`, etc. |
| `subject` | string | Yes | Imperative form title |
| `description` | string | No | Detailed requirements |
| `activeForm` | string | No | Present continuous for spinner: `"Running tests"` |
| `status` | string | Yes | `"pending"`, `"in_progress"`, `"completed"`, `"deleted"` |
| `owner` | string | No | Agent short name |
| `blocks` | array[string] | No | Task IDs this task blocks (forward references) |
| `blockedBy` | array[string] | No | Task IDs that must complete first |
| `metadata` | object | No | Arbitrary key-value pairs |

### 4.3 Task ID Assignment

IDs are monotonically increasing numeric strings starting from `"1"`. The `.lock` file in the task directory serializes creation operations to avoid ID conflicts.

### 4.4 Status Transitions

Observed transitions: `pending → in_progress → completed`, and `any → deleted`. No evidence of `completed → pending` regression.

### 4.5 Directory Duality

`~/.claude/tasks/` contains BOTH:
- **Named team directories** (e.g., `proxy-analysis/`, `macos-cleaner-dev/`) — tasks belonging to agent teams
- **UUID session directories** (e.g., `04318e4f-5c02-4319-90b5-b24b27ea6cea/`) — tasks from standalone Claude Code sessions (no team)

Both use the same task file format. This confirms that Claude Code's task system is session-scoped by default and team-scoped when a team exists.

---

## 5. Lock File Analysis

### 5.1 Observed Lock Files

| Path | Purpose |
|------|---------|
| `~/.claude/teams/{team}/config.json.lock` | Serialize config.json modifications |
| `~/.claude/teams/{team}/inboxes/{agent}.lock` | Serialize inbox message append/read |
| `~/.claude/tasks/{team}/.lock` | Serialize task creation/modification |

### 5.2 Lock Mechanism

Lock files are **0-byte companion files** used as advisory locks (likely via `flock(2)` or Node.js `fs-ext` equivalent). They are NOT the actual data files being locked.

Evidence from `macos-cleaner-chat`:
```
-rw-r--r--  0B  config.json.lock
-rw-r--r--  0B  inboxes/assistant.lock
```

### 5.3 Known Concurrency Issues

- **MEMORY.md corruption** (Issue #24130): Multiple agents writing `~/.claude/projects/{hash}/MEMORY.md` concurrently, no lock file for this path
- **Task state desync** (Issue #23629): TaskUpdate status not reliably synced between team and session task lists, suggesting the lock mechanism has gaps

---

## 6. Agent Backend Types

### 6.1 `backendType: "in-process"`

Observed on all teammates in `proxy-analysis` and `macos-cleaner-dev`. This means:
- The agent runs **in the same process** as Claude Code (not in a separate tmux pane)
- `tmuxPaneId` is set to `"in-process"` (not a real tmux pane ID)
- Communication is via the file-based inbox (same as tmux-based agents)

### 6.2 `tmuxPaneId: ""`

Observed on team leads. The lead is the main Claude Code session itself — it doesn't need a separate pane.

### 6.3 Absence of tmux-based agents in observed data

All observed teammates use `backendType: "in-process"`. No tmux-based teams were found in the local data. This may indicate that in-process is now the default/preferred mode, or that tmux mode requires explicit opt-in.

---

## 7. Execution Flow Reconstruction

Based on inbox message timestamps and task state analysis across `macos-cleaner-dev`:

```
Timeline (2026-02-11):
08:27:04  task_assignment → setup-specialist (Task #1)
08:27:54  team-lead → setup-specialist: guidance message
08:28:51  team-lead → setup-specialist: status check
08:29:08  team-lead → ALL: CRITICAL - wrong framework detected
08:29:13  task_assignment → setup-specialist (Task #2, after correction)
08:31:40  task_assignment → setup-specialist (Task #3)
08:32:38  team-lead → ALL: foundation complete, work begins

[~4 hours of parallel execution]

16:27    Team config last modified (5 members)
16:40    runtime-developer, setup-specialist inboxes last modified
16:44    core-developer inbox last modified
16:49    ui-developer inbox last modified
16:50    team-lead inbox last modified (90KB — heaviest)
```

**Key observations**:
1. Team lead is the **most active communicator** — its inbox (90KB) is 4x larger than any teammate
2. Task assignments flow in BOTH directions: lead → teammate AND teammate → self (self-assignment)
3. Team lead sends broadcast-like messages manually (same message to each inbox)
4. Error recovery (wrong framework) was handled by team lead broadcasting a STOP message

---

## 8. GitHub Issue Verification

Six issues from the article were verified on 2026-02-12:

| Issue | Title | Article Claims | Verified Status | Verdict |
|-------|-------|---------------|----------------|---------|
| [#23620]https://github.com/anthropics/claude-code/issues/23620 | Agent team lost when lead's context gets compacted | OPEN | **OPEN** | Correct |
| [#25131]https://github.com/anthropics/claude-code/issues/25131 | Catastrophic agent lifecycle failures, duplicate spawning | OPEN | **OPEN** | Correct |
| [#24108]https://github.com/anthropics/claude-code/issues/24108 | Teammates stuck at idle prompt in tmux split-pane mode | OPEN | **CLOSED** | **WRONG** |
| [#24130]https://github.com/anthropics/claude-code/issues/24130 | Auto memory file not safe for concurrent agent teams | OPEN | **OPEN** | Correct |
| [#24977]https://github.com/anthropics/claude-code/issues/24977 | Task completion updates flood context window | OPEN | **OPEN** | Correct |
| [#23629]https://github.com/anthropics/claude-code/issues/23629 | TaskUpdate status not synced between team and session | OPEN | **OPEN** | Correct |

**Article accuracy**: 5/6 correct (83%). Issue #24108 was CLOSED at time of verification.

---

## 9. What the Article Got Right

1. **Filesystem-based coordination** — Accurate. Teams, tasks, and messages are all plain JSON files.
2. **JSON-in-JSON protocol messages** — Accurate. `task_assignment` and `idle_notification` types confirmed.
3. **No heartbeat, no ACK** — Accurate. The `read` boolean is the only state tracking.
4. **File locking via companion files** — Accurate. `.lock` files observed at all three levels.
5. **Task DAG with `blocks`/`blockedBy`** — Accurate. Exact field names confirmed.
6. **5 of 6 GitHub issues** — Accurate status at time of writing.

## 10. What the Article Got Wrong or Missed

### 10.1 Incorrect
- **Issue #24108 status**: Article says OPEN, actually CLOSED
- **Simplified config.json example**: Article's example omits critical fields (`createdAt`, `leadSessionId`, `tmuxPaneId`, `cwd`, `subscriptions`, `backendType`, `planModeRequired`, `color`, `joinedAt`)

### 10.2 Not mentioned
- **`planModeRequired` field**: Controls whether a teammate must have its plan approved before executing. This is a significant architectural feature enabling supervised vs autonomous agent modes.
- **`backendType: "in-process"`**: Article focuses on tmux split-pane model, but real data shows all modern teammates run in-process.
- **`color` field**: On both member config and inbox messages, enabling UI differentiation.
- **`summary` field**: On inbox messages, providing UI preview text.
- **`subscriptions` array**: Empty in all observed data but present in schema — suggests planned event-based messaging.
- **`leadSessionId`**: Ties the team to a specific Claude Code session.
- **Two config format variants**: `"name"` vs `"teamName"` key, full vs minimal member schemas.
- **Self-assignment**: Agents can send `task_assignment` to themselves (observed in `setup-specialist`).
- **Team lead inbox is the heaviest**: 90KB vs ~15-25KB for teammates — the lead receives all reports and coordination messages.

---

## 11. Architectural Insights

### 11.1 The "In-Process" Shift

The article's narrative centers on tmux-based agent spawning. But the real data tells a different story: **every observed teammate uses `backendType: "in-process"`**. This suggests Claude Code has moved (or is moving) from tmux subprocess agents to in-process agents, which:
- Eliminates tmux dependency and pane management overhead
- Enables tighter memory sharing (same Node.js process)
- Explains why Issue #24108 (tmux polling bug) was CLOSED — the tmux codepath may be deprecated

### 11.2 Plan Mode as Quality Gate

`planModeRequired: false/true` on teammates is an underappreciated feature. It enables a **two-tier execution model**:
- **Autonomous agents** (`planModeRequired: false`): Execute immediately, report results
- **Supervised agents** (`planModeRequired: true`): Must submit a plan, wait for lead approval, then execute

This is essentially a pull-request-like workflow for agent actions.

### 11.3 The Inbox Bottleneck

Team lead's inbox (90KB) is 4x the size of any teammate's. All coordination flows through the lead. This is a single-point-of-bottleneck architecture:
- Lead context compaction (Issue #23620) kills the entire team
- Lead must parse all teammate reports serially
- No direct peer-to-peer coordination observed (all messages route through lead)

### 11.4 Missing: Message Deduplication

No `message_id` on inbox messages means:
- Duplicate deliveries cannot be detected
- Exactly-once processing is not guaranteed
- Crash recovery may replay already-processed messages

### 11.5 Missing: Agent Health Monitoring

No heartbeat, no process health check, no timeout. If an agent silently dies:
- Its inbox keeps accepting messages (file system doesn't care)
- Team lead has no way to detect the failure except timeout heuristics
- Issue #25131 (duplicate spawning) may be a symptom of this gap

---

## 12. Data Sizes and Performance Characteristics

| Metric | Observed |
|--------|----------|
| Config file size (full team) | 585B – 5.9KB |
| Inbox file size (small team) | 100B – 1KB |
| Inbox file size (active team) | 15KB – 90KB |
| Task file size | ~200B – 1KB each |
| Task count per team | 1 – 23 |
| Lock file size | 0 bytes (advisory only) |
| Task directories total | 55+ |

---

## Appendix A: Comparison with agent-teams Library Models

| Claude Code Native | agent-teams Library | Gap |
|-------------------|---------------------|-----|
| Config `"name"` key | `"teamName"` key | Library matches simplified format only |
| `createdAt` (ms timestamp) | Not present | Missing field |
| `leadAgentId`, `leadSessionId` | Not present | Missing fields |
| `joinedAt` (ms timestamp) | Not present | Missing field |
| `tmuxPaneId` | Not present | Missing field |
| `cwd` | Not present | Missing field |
| `subscriptions` | Not present | Missing field |
| `backendType` | Not present | Missing field |
| `planModeRequired` | Not present | Missing field |
| `color` on member | Not present | Missing field |
| `color` on inbox message | Not present | Missing field |
| `summary` on inbox message | Present | Aligned |
| No `id` on inbox message | Has `id` (UUID) | Library extends the format |
| No `to` on inbox message | Has `to` | Library extends the format |
| `from`, `text`, `timestamp`, `read` | `from`, `content`(not `text`), `timestamp`, `read` | Key name mismatch: `text` vs `content` |
| Task format | Task format | Fully aligned |

**Priority gaps for compatibility**:
1. Config `"name"` vs `"teamName"` — need to accept both
2. Inbox `"text"` vs `"content"` — need to accept both
3. Missing member fields (11 fields) — need to add as optional