Observed by: observer agent Date: 2026-02-09 Team: moltbook-tui (1 lead + 4 agents, observed over ~10 minutes of activity) Snapshots taken: 7 (see snapshot-1.md through snapshot-7.md for raw data)
Claude Code teams use a filesystem-based coordination system. There are no sockets, databases, or message queues - everything is stored as JSON files on disk. Agents coordinate through:
- Team config file - Central registry of team members
- Inbox files - Per-agent message queues (JSON arrays)
- Task files - Individual JSON files per task
~/.claude/
├── teams/
│ └── {team-name}/
│ ├── config.json # Team registry
│ └── inboxes/
│ ├── {agent-name}.json # Per-agent inbox
│ └── ...
└── tasks/
└── {task-list-id}/
├── .lock # Filesystem lock
├── 1.json # Task 1
├── 2.json # Task 2
└── ...
The central registry file. Contains team metadata and the members array.
{
"name": "moltbook-tui",
"description": "Building a CLI-based TUI app...",
"createdAt": 1770674853809,
"leadAgentId": "team-lead@moltbook-tui",
"leadSessionId": "<session-uuid>",
"members": [/* array of member objects */]
}{
"agentId": "api-builder@moltbook-tui", // {name}@{team-name}
"name": "api-builder", // Human-readable name
"agentType": "team-lead" | "general-purpose",
"model": "claude-opus-4-6",
"prompt": "...", // Full system prompt (only for non-leads)
"color": "blue", // Visual identity color
"planModeRequired": false, // If true, agent must get plan approved
"joinedAt": 1770675032956, // Unix timestamp in ms
"tmuxPaneId": "in-process" | "", // Process management
"cwd": "<project-root>", // Working directory
"subscriptions": [], // Unused in this team
"backendType": "in-process" // Only for spawned agents
}- agentId:
{name}@{team-name}format (e.g.,api-builder@moltbook-tui) - name: Short identifier used in messaging (e.g.,
api-builder) - color: Assigned at creation; travels with messages for visual identity
- The team lead has no
color,prompt, orbackendTypefields
- Member added: New entry appended to
membersarray when agent spawns - Member removed: Entry removed from
membersarray when agent shuts down (inbox file persists)
Each agent has a dedicated inbox file at ~/.claude/teams/{team}/inboxes/{name}.json. The inbox is a JSON array of message objects.
Sending a message: The sender's Claude Code runtime writes to the RECIPIENT's inbox file. The entire JSON array is read, parsed, a new message is appended, and the entire file is rewritten.
Receiving a message: The recipient agent periodically reads its own inbox file. Messages appear in the agent's conversation context.
No outbox exists. There is no record in the sender's inbox of messages they sent. The only trace is in the recipient's inbox.
{
"from": "observer", // Sender's name
"text": "Hello!", // Message content (plain text)
"summary": "Greeting", // Short preview (for UI display)
"timestamp": "2026-02-09T22:12:33.435Z",
"color": "purple", // Sender's color
"read": false // Updated to true when processed
}{
"from": "api-builder", // Agent's OWN name (self-notification)
"text": "{\"type\":\"task_assignment\",\"taskId\":\"2\",\"subject\":\"...\",\"description\":\"...\",\"assignedBy\":\"api-builder\",\"timestamp\":\"...\"}",
"timestamp": "2026-02-09T22:10:36.246Z",
"color": "blue",
"read": false
}Note: The text field contains a JSON string (double-encoded). Task assignments are self-addressed - the from field is the agent itself.
{
"from": "api-builder",
"text": "{\"type\":\"idle_notification\",\"from\":\"api-builder\",\"timestamp\":\"...\",\"idleReason\":\"available\",\"summary\":\"[to spec-writer] API client changes for spec documentation\"}",
"timestamp": "2026-02-09T22:13:21.533Z",
"color": "blue",
"read": true
}These are automatically sent to the team-lead when an agent finishes an action and is waiting. The summary field shows what the agent just did (including who they just messaged).
{
"from": "team-lead",
"text": "{\"type\":\"shutdown_request\",\"requestId\":\"shutdown-1770675240738@api-builder\",\"from\":\"team-lead\",\"reason\":\"...\",\"timestamp\":\"...\"}",
"timestamp": "2026-02-09T22:14:00.738Z",
"read": true
}The requestId format is: shutdown-{epoch-ms}@{target-agent}
{
"from": "api-builder",
"text": "{\"type\":\"shutdown_approved\",\"requestId\":\"shutdown-1770675240738@api-builder\",\"from\":\"api-builder\",\"timestamp\":\"...\",\"paneId\":\"in-process\",\"backendType\":\"in-process\"}",
"timestamp": "2026-02-09T22:14:04.583Z",
"color": "blue",
"read": false
}- Messages start as
read: false - When an agent processes (reads) a message, it's updated to
read: true - The team-lead's messages are often
read: truebecause the lead processes messages actively - A shut-down agent leaves all its unread messages as
read: falsepermanently - Messages sent to dead agents will never be read
- File-based queue: Messages are stored as a JSON array in a flat file
- No delivery guarantee to dead agents: Messages to terminated agents are still written to the inbox file
- No deduplication: An agent can send the same information multiple times (observed: api-builder sent demo mode info twice to tui-builder)
- Ordered by timestamp: Messages are appended chronologically
- Inbox files persist: Even after an agent shuts down, its inbox file remains on disk
Tasks are stored in ~/.claude/tasks/{task-list-id}/ as individual numbered JSON files.
The task list ID can be a UUID (for auto-generated lists) or a human-readable string (e.g., moltbook-tui when set via CLAUDE_CODE_TASK_LIST_ID).
{
"id": "2",
"subject": "Build Moltbook API client (api.py)",
"description": "...",
"activeForm": "Building Moltbook API client",
"status": "in_progress", // "pending" | "in_progress" | "completed"
"blocks": ["3", "4"], // Task IDs this task blocks
"blockedBy": ["1"], // Task IDs blocking this task
"owner": "api-builder" // Agent name (not agentId)
}Tasks 7-10 in this team are "shadow tasks" with metadata: {"_internal": true}. Each corresponds to an agent and tracks the agent process itself:
{
"id": "7",
"subject": "api-builder",
"description": "You are \"api-builder\", a team member...", // Truncated prompt
"status": "in_progress",
"metadata": {"_internal": true}
}These are not work items - they track agent lifecycle. They remain in_progress even after the agent's real task is completed and the agent is shut down.
- Created with
status: "pending", noowner - Claimed by an agent:
ownerset,status->"in_progress" - Completed:
status->"completed",ownerpreserved - When an agent claims a task, a task_assignment message is auto-generated in the agent's own inbox
blocksandblockedByare advisory, not enforced- Agents CAN work on blocked tasks (observed: spec-writer claimed task #4 while task #3 was still in_progress, despite
blockedBy: ["2", "3"]) - Dependencies are bidirectional: if task A
blocks: ["B"], then task B should haveblockedBy: ["A"]
A .lock file (0 bytes) exists in the task directory. This is a filesystem lock to prevent concurrent writes from multiple agents operating on the same task list.
Here's the full lifecycle observed during this team's operation:
1. Team Creation (by human via Claude Code)
-> config.json created with team-lead member
-> Task list created in ~/.claude/tasks/moltbook-tui/
2. Team Lead Creates Tasks
-> Individual .json files created in tasks directory
-> Task assignment message sent to team-lead's own inbox
3. Agent Spawning (by team-lead)
-> New member entry added to config.json
-> Agent inbox file created at inboxes/{name}.json
-> Internal shadow task created (metadata._internal: true)
-> Agent begins executing its prompt
4. Agent Claims Task
-> Task .json file updated (owner, status)
-> Task assignment message auto-generated in agent's own inbox
5. Agents Communicate
-> Sender writes to recipient's inbox file
-> Recipient reads own inbox file periodically
-> idle_notification messages sent to team-lead automatically
6. Agent Completes Task
-> Task .json file updated (status: completed)
-> Agent messages team-lead with summary
-> Agent goes idle (sends idle_notifications)
7. Agent Shutdown
-> team-lead sends shutdown_request to agent's inbox
-> Agent sends shutdown_approved to team-lead's inbox
-> Agent entry removed from config.json members
-> Agent inbox file persists on disk
-> Internal shadow task remains as in_progress
Messages can be sent to agents that have already been shut down and removed from config.json. The message is still written to the inbox file. No error or warning is raised.
When an agent claims a task via TaskUpdate, a task_assignment message appears in the agent's own inbox with from set to the agent's own name. This is a notification mechanism, not a real incoming message.
The team-lead receives idle_notification messages that serve as a real-time activity log. Each notification includes a summary showing what the agent just did (e.g., [to spec-writer] API client changes). This gives the lead visibility without polling.
Multiple agents share the same git branch with no merge resolution mechanism beyond git pull. If two agents modify the same file simultaneously, one will get a merge conflict. In practice, the task dependency system and prompt-based guidance prevent most conflicts.
Inbox files grow monotonically. There is no garbage collection, archiving, or truncation of old messages. For long-running teams, this could become a concern.
The config.json file is modified during operation:
- Members are added when agents spawn
- Members are removed when agents shut down
- Multiple processes could theoretically race on this file
The blockedBy/blocks system provides information to agents but does not prevent them from claiming or working on blocked tasks. Agents must check these dependencies themselves.
Each agent is assigned a color (blue, green, yellow, purple). This color is included in every message the agent sends. It's used for visual differentiation in the team-lead's UI. The team-lead itself has no color.
| Type | Direction | Purpose | Key Fields |
|---|---|---|---|
| Direct message | Agent -> Agent | Coordination | from, text, summary, color |
| task_assignment | System -> Self | Track task claims | type, taskId, subject |
| idle_notification | Agent -> Lead | Activity tracking | type, idleReason, summary |
| shutdown_request | Lead -> Agent | Initiate shutdown | type, requestId, reason |
| shutdown_approved | Agent -> Lead | Confirm shutdown | type, requestId, paneId |
All message types use the same inbox file and JSON array structure. System messages (task_assignment, idle_notification, shutdown_*) are encoded as JSON strings within the text field.
Over the team's ~10-minute lifespan:
| Metric | Value |
|---|---|
| Team lifespan | ~10 minutes |
| Agents spawned | 4 (api-builder, tui-builder, spec-writer, observer) |
| Agents shut down | 3 (observer still active at end) |
| Total messages across all inboxes | 38 |
| Direct messages | 14 (37%) |
| Idle notifications | 12 (32%) |
| Task assignments | 7 (18%) |
| Shutdown requests | 3 (8%) |
| Shutdown approvals | 3 (8%) |
| Git commits | 11 |
| Work tasks completed | 6 of 6 |
| Tests written | 102 |
| config.json modifications | ~7 (1 create + 4 member adds + 3 member removes) |
| Unread messages on dead agents | 4 (permanently unread) |
| team-lead inbox size at end | 10,222 bytes (20 messages) |
The team-lead's inbox was 60% system messages (idle_notification + shutdown). Each agent's wind-down followed the same pattern:
Agent: "Task complete" (direct message)
Agent: idle_notification (3-7 second intervals, 2-5 times)
Lead: shutdown_request
Agent: shutdown_approved
[agent removed from config.json members]
The number of idle notifications before shutdown depended on how quickly the team-lead processed the completion message and sent a shutdown request.
Not all messages were read by agents. Observed patterns:
- Agents read task_assignment and shutdown_request messages (essential for operation)
- Agents sometimes skipped informational direct messages (tui-builder never read observer's greeting or api-builder's duplicate demo mode messages)
- This suggests agents may prioritize messages or read them in batches during natural pauses
- Simplicity: Pure filesystem, no external dependencies
- Debuggability: Every message and task state is a readable JSON file
- Persistence: Full audit trail preserved on disk
- Independence: No central server; agents can operate with partial connectivity
- No garbage collection: Inboxes grow unbounded
- Advisory blocking: No enforcement of task dependencies
- Dead letter delivery: Messages to terminated agents are silently delivered
- Race conditions: Multiple agents writing to same file (mitigated by
.lock) - Shadow task cleanup: Internal tasks for dead agents remain
in_progressforever - Idle notification noise: ~1/3 of all team-lead messages are idle pings