name: mailbox description: Use when sending tasks to other AI providers (codex/gemini), reading task results, or listing inbox. The mailbox is the preferred provider-to-provider path and avoids async-guardrail issues.
Mailbox
Send and receive tasks via the CCB Redis mailbox protocol. The mailbox-watcher handles recipient notification automatically — you never need a separate wake-up command.
Non-Negotiable Rules
- Use
node ~/.claude/scripts/mailbox schema [task_type]when unsure; it is the live contract. - Build JSON with Bash heredocs (
cat > /tmp/mailbox-task.json <<'JSON') and pass"$(cat /tmp/mailbox-task.json)"to the CLI. Do not inline large JSON in shell arguments. - Use the mailbox CLI, not raw Redis writes to
ccb:task:*/ccb:inbox:*. - Incoming task notifications must be answered by claiming and completing/failing the same task id. Do not use
mailbox sendas a reply unless creating a brand-new follow-up task. - Keep envelope fields top-level. Put run linkage in allowed fields like
inputs,base_context, orimplementation_notes; do not use stale fields such asreply_toor nestedbody.run_id.
Commands
| Command | Usage | What it does |
|---|---|---|
/mailbox send <to> <objective> |
Send a task | Build envelope, validate, dispatch |
/mailbox wait <task-id> [timeout] |
Wait for result | Block until done, return result (subagent-friendly) |
/mailbox read <task-id> |
Get result | Fetch result, clear inbox |
/mailbox peek <task-id> |
Check status | Read without side effects |
/mailbox list <provider> |
List inbox | Show queued tasks |
node ~/.claude/scripts/mailbox schema [task_type] |
Show schema | Print required fields, enums, and task-type extras |
How It Works
Sender Redis Recipient
│ │ │
├─ mailbox send ─────────►│ task → inbox │
│ │ │
│ │ mailbox-watcher polls │
│ │ detects new task │
│ │ │
│ │ ◄── tmux notification ──►│
│ │ │
│ │ claim + execute │
│ │ │
│ │ ◄── mailbox done ────────┤
│ │ result → sender inbox │
│ │ │
├─ mailbox wait ─────────►│ polls until done │
│◄─ result ──────────────┤│ │
Key: the mailbox-watcher (background process) handles notifying the recipient via tmux. You do NOT need a separate wake-up command.
Subdirectory sends are supported: the CLI resolves upward to the nearest .ccb root before starting the watcher, so sending from repo/subproject still wakes the mounted provider pane registered at repo/.ccb. If a task remains queued, check the watcher log's work_dir before resending.
Subagent Pattern (Primary Use Case)
When a subagent needs to dispatch work to Codex and get results back:
# 1. Build the task with a single-quoted heredoc so quotes/newlines stay valid JSON.
cat > /tmp/mailbox-task.json <<'JSON'
{
"task_type": "investigate",
"objective": "One clear WHAT outcome",
"why": "Why this matters",
"deliverable": "findings",
"evidence": "Required for investigate/fix_bug; include concrete logs or observations",
"acceptance_criteria": [
{"id":"AC-1","requirement":"Return usable findings","verification_method":"Result JSON includes findings"}
],
"scope_in": ["/path/or/context"],
"scope_out": [],
"authority_flags": {
"can_create_files": false,
"can_delete_files": false,
"can_modify_deps": false,
"can_edit_dirty_files": false
}
}
JSON
# 2. Send task (mailbox-watcher notifies Codex automatically).
TASK_ID=$(node __YAO_HOME__/.claude/scripts/mailbox send codex "$(cat /tmp/mailbox-task.json)" | jq -r '.id')
# 3. Wait for result (blocks until Codex completes, auto-cleans inbox).
RESULT=$(node __YAO_HOME__/.claude/scripts/mailbox wait "$TASK_ID" 300)
# 4. Process result.
echo "$RESULT" | jq '.result'
That's it. Two commands. No extra wake-up step. The watcher handles notification, wait handles getting the result.
What NOT to do
| Anti-pattern | Why it's wrong |
|---|---|
| Direct Codex wake-up through an async provider command | Triggers async guardrail, kills your turn, dumps CCB noise to main session |
| Legacy dedicated Codex wake-up command | Unnecessary — mailbox-watcher already notifies Codex via tmux |
Polling with repeated mailbox peek calls |
Wastes context window — use mailbox wait instead (single blocking call) |
Calling mailbox read from subagent |
Use mailbox wait — it reads AND cleans up in one call |
Recipient / Worker Pattern
When a mailbox-watcher notification says a queued task is for Claude, complete that original task instead of sending a new one:
node ~/.claude/scripts/mailbox claim <task-id>
cat > /tmp/mailbox-result.json <<'JSON'
{
"changedFiles": [],
"diffSummary": "...",
"commands": [],
"notes": []
}
JSON
CCB_CALLER=claude node ~/.claude/scripts/mailbox done <task-id> "$(cat /tmp/mailbox-result.json)"
Use CCB_CALLER=claude node ~/.claude/scripts/mailbox fail <task-id> "$(cat /tmp/mailbox-error.json)" if execution fails. Never answer an inbound task with mailbox send; that creates a brand-new task and leaves the original task running.
Main Session Pattern
From the main (Supervisor) session, the mailbox-watcher sends a tmux notification when results arrive. Then:
# Read result (clears from inbox)
node ~/.claude/scripts/mailbox read <task-id>
Send Flow
When /mailbox send <to> <objective> is invoked:
- Build the envelope from context — fill all 8 required fields:
{
"task_type": "implement|investigate|fix_bug|review|refactor|test|document",
"objective": "<WHAT not HOW — from args>",
"why": "<business/technical reason>",
"deliverable": "code_change|findings|root_cause|test_only|recommendation|documentation",
"acceptance_criteria": [
{ "id": "AC-1", "requirement": "...", "verification_method": "..." }
],
"scope_in": ["<files/dirs>"],
"scope_out": ["<files/dirs>"],
"authority_flags": {
"can_create_files": false,
"can_delete_files": false,
"can_modify_deps": false,
"can_edit_dirty_files": false
}
}
- Write to temp file with a Bash heredoc (avoids shell escaping and avoids relying on the Write tool):
cat > /tmp/mailbox-task.json <<'JSON'
{
"task_type": "document",
"objective": "WHAT outcome",
"why": "Why this matters",
"deliverable": "documentation",
"acceptance_criteria": [
{"id":"AC-1","requirement":"Task is valid","verification_method":"mailbox send accepts it"}
],
"scope_in": ["/path/or/context"],
"scope_out": [],
"authority_flags": {
"can_create_files": false,
"can_delete_files": false,
"can_modify_deps": false,
"can_edit_dirty_files": false
}
}
JSON
node ~/.claude/scripts/mailbox send <to> "$(cat /tmp/mailbox-task.json)"
- Report task ID from output. Do NOT send any wake-up message.
Required Fields Quick Reference
| Field | Type | Notes |
|---|---|---|
task_type |
enum | implement, investigate, fix_bug, review, refactor, test, document |
objective |
string | WHAT not HOW. No code snippets or line numbers |
why |
string | Business or technical reason |
deliverable |
enum | code_change, findings, root_cause, test_only, recommendation, documentation |
acceptance_criteria |
array | Each needs id, requirement, verification_method — all three required |
scope_in |
string[] | Non-empty array of file paths/dirs |
scope_out |
string[] | Array (can be empty) |
authority_flags |
object | All 4 boolean flags required |
Task-Type Specific Requirements
| task_type | Extra required field |
|---|---|
fix_bug |
evidence (error messages, logs) |
investigate |
evidence (observations, logs, reproduction context) |
review |
inputs (comparison target) |
Optional Fields
repo_root, workdir, base_context, workspace_state, relevant_files_or_symbols, evidence, constraints, inputs, timebox, implementation_notes, verification_commands, blocker_policy (ask_supervisor|skip_and_note|fail_fast), batch_context
Wait
mailbox wait blocks until a task reaches terminal state (done or failed), then returns the result and auto-cleans the sender's inbox.
# Default timeout: 300s, polls every 3s
node ~/.claude/scripts/mailbox wait <task-id>
# Custom timeout
node ~/.claude/scripts/mailbox wait <task-id> 120
Returns JSON: { "id", "status" ("done"|"failed"|"timeout"), "result", "error" }
Peek / List
# Peek without side effects
node ~/.claude/scripts/mailbox peek <task-id>
# List inbox
node ~/.claude/scripts/mailbox list <provider>
Common Mistakes
| Mistake | Fix |
|---|---|
command not found: mailbox |
Use full path: node ~/.claude/scripts/mailbox |
| JSON escaping errors in shell | Use a single-quoted Bash heredoc: cat > /tmp/mailbox-task.json <<'JSON', then pass "$(cat /tmp/mailbox-task.json)" |
acceptance_criteria missing verification_method |
Every AC item needs all 3 fields: id, requirement, verification_method |
unknown field: task_id |
Don't include task_id — the system generates id automatically |
| Passing inline JSON with nested quotes | Always use the temp file approach |
| Calling extra wake-up commands for the recipient | Don't — mailbox-watcher handles notification automatically |
Subagent calling mailbox read |
Use mailbox wait instead — blocks, returns, and cleans in one call |
Inbound task answered with mailbox send |
Claim and complete/fail the same task id with `CCB_CALLER=claude ... done |
Raw Redis task docs with reply_to / body.run_id |
Use the CLI top-level envelope; put linkage under allowed fields like inputs |