Skip to content

Spawn SOP

Full SOP for sessions_spawn, sessions_send, and Claude Code CLI. See the Agent Roster for the quick reference.

  • Non-blocking: “accepted” means queued, not finished.
  • Always include: label, runTimeoutSeconds, and cleanup="keep".
  • Write chunked task briefs with exact paths + success criteria. Avoid giant one-shot requests.
  • Do not busy-poll. If no completion by timeout + 2m, check once: subagents list or sessions_history(sessionKey=childKey, includeTools=true, limit=5).
  • If stalled: steer once (subagents steer), then kill + respawn with a tighter brief.
  • Treat child transcript + artifacts as source of truth; completion announce is best-effort.

Sentinel Pattern — Auto-Wake on Completion

Section titled “Sentinel Pattern — Auto-Wake on Completion”

Problem: Final sub-agent completion uses passive delivery (method: "send") — doesn’t wake the parent. Parent goes silent until a human intervenes.

Fix: Spawn a dummy “sentinel” alongside real workers. The sentinel keeps activeDescendantRuns > 0, forcing worker completions to wake the parent via method: "agent".

# 1. Spawn sentinel (MiniMax, free — zero intelligence needed)
sessions_spawn(
task="Run: exec sleep 3600. Do nothing else.",
label="sentinel",
model="minimax",
runTimeoutSeconds=3600,
cleanup="keep"
)
# 2. Spawn real worker(s)
sessions_spawn(task="Actual work...", label="worker-1", ...)
# 3. Worker completes → parent auto-wakes (sentinel still active)
# 4. Parent processes result, then kills sentinel:
subagents(action="kill", target="sentinel")

When to use: Any spawn where you need the parent to automatically act on the result. When to skip: Fire-and-forget tasks where you’ll check manually later.

sessions_send — Fire-and-Forget (CRITICAL)

Section titled “sessions_send — Fire-and-Forget (CRITICAL)”

Always pass timeoutSeconds=0:

sessions_send(sessionKey="agent:main:main", message="...", timeoutSeconds=0)

Returns { status: "accepted" } immediately. The message IS delivered and queued.

Why: Cross-agent runs share a single-concurrency nested lane (hardcoded concurrency=1, no config knob — v2026.2.19-2). Default 30s timeout fails whenever another agent-to-agent run is in progress. Fire-and-forget avoids this entirely.

To check results later: sessions_history(sessionKey="agent:<id>:main", limit=5)

sessions_send vs sessions_spawn — Routing

Section titled “sessions_send vs sessions_spawn — Routing”
Use CaseTool
Named persistent agents: Winnie, PMs, Sentry, Pipesessions_send (retains memory + context)
Spawn-only templates: dev-backend, dev-frontend, researchsessions_spawn (disposable, auto-announce)

Spawn-only templates always use sessions_spawn. Never sessions_send for them.

# Target agent's main session
sessions_send(sessionKey="agent:main:main", message="...", timeoutSeconds=0)
sessions_send(sessionKey="agent:pm-frankel:main", message="...", timeoutSeconds=0)
# Target specific HQ topic
sessions_send(sessionKey="agent:pm-frankel:telegram:group:-1003848100060:topic:4", message="...", timeoutSeconds=0)
sessions_send(sessionKey="agent:main:telegram:group:-1003848100060:topic:2", message="...", timeoutSeconds=0)
  • sessionKey="agent:main:main" — correct
  • sessionKey="agent:pm-frankel:telegram:group:-1003848100060:topic:4" — correct
  • agentId="main" alone — FAILS (“Either sessionKey or label is required”)
  • label="main" — FAILS (looks for labeled session, won’t find one)

Before every sessions_spawn or Codex/Claude Code invocation:

  1. Read HANDOVER.md — check “Dev Model” or tool preference field
  2. Use whatever it specifies (Codex, Claude Code, etc.)
  3. Do NOT default to Claude Code if HANDOVER says Codex, or vice versa

Standard pattern:

Terminal window
cd /project && git init 2>/dev/null
claude -p --dangerously-skip-permissions --model sonnet --max-budget-usd 3 \
"Your task. Run tests. Fix errors. Commit. Do NOT ask questions."

Stream-JSON (real-time monitoring):

Terminal window
claude --input-format stream-json --output-format stream-json --verbose \
--dangerously-skip-permissions --model sonnet

Agent teams (parallel workers):

Terminal window
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 claude --teammate-mode tmux \
--dangerously-skip-permissions --model sonnet

Key flags: -p --dangerously-skip-permissions --model sonnet --max-budget-usd 3

Always include in prompt: “Do NOT ask questions. Run build. Fix errors. Commit.”

Pattern: Plan → Execute in batches of 5–7 items

What: AST-indexed symbol retrieval via MCP. Replaces brute-force file reads with O(1) byte-offset lookup. 90–99% token savings on code navigation.

Binary: /home/winnie/.mcp-venv/bin/jcodemunch-mcp

For headless spawns (any project):

Terminal window
cd /path/to/project && claude -p \
--mcp-config '{"jcodemunch":{"type":"stdio","command":"/home/winnie/.mcp-venv/bin/jcodemunch-mcp"}}' \
"Task brief. Use jCodeMunch for code navigation (search_symbols, get_symbol). Do not read entire files."

For project-configured repos (Frankel): Just cd into the dir — MCP auto-loads.

Include in all briefs: “Use jCodeMunch MCP (search_symbols, get_symbol, get_file_outline) for code navigation. Index with index_folder first if not already indexed.”

Pre-indexed projects: Frankel (7,644 symbols), mission-control (277), R6_Records (26), crate (97). Index cache: /home/winnie/.code-index/

Task: <what to build/do>
Paths: <exact file paths>
Success criteria: <how to verify it worked>
Output: Write summary to /home/winnie/.openclaw/workspace/checkpoints/subagent-<label>.md
Include: status (done|error), files_changed, summary, next_action (if any)
Do NOT ask questions. Execute and commit.