From spacedock
Provides a legacy worker back-channel using TeamCreate for runtimes exposing the native team registry. Overrides the normal dispatch contract and includes sunset instructions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/spacedock:using-legacy-claude-teamThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A live worker needs a back-channel to talk to `main` while it runs. When the runtime provides that back-channel through named background subagents, it is the normal dispatch contract — a named background `Agent` whose `SendMessage(to="main")` reaches the lead — and it needs no team-creation step; see `claude-fo-dispatch.md`. This skill provides the SAME back-channel the LEGACY way, through the ...
A live worker needs a back-channel to talk to main while it runs. When the runtime provides that back-channel through named background subagents, it is the normal dispatch contract — a named background Agent whose SendMessage(to="main") reaches the lead — and it needs no team-creation step; see claude-fo-dispatch.md. This skill provides the SAME back-channel the LEGACY way, through the native TeamCreate team registry, for a runtime that still exposes it.
Read this skill only when the capability probe ToolSearch(query="select:TeamCreate", max_results=1) matches a TeamCreate tool (the runtime exposes the native team registry). When the probe returns no match, the FO never reads this file, and the back-channel comes from the named-background-Agent shape inline in claude-fo-dispatch.md.
Sunset is a one-line removal. This skill is reached through a SINGLE line in claude-fo-dispatch.md — the one labelled **Legacy override (delete this line to sunset legacy mode):**. Delete that one line and the entire legacy path is gone from the live contract: no probe, no skill load, no override. Then this skill directory and the TeamCreate/TeamDelete branches in internal/dispatch/build.go + internal/dispatch/standing.go are dead code, removable at leisure.
Removal trigger (externally checkable): do the above when no runtime the FO targets still exposes TeamCreate AND no live lane drives the legacy branch. The externally-checkable proxy for that condition is the CI pin SPACEDOCK_PINNED_CLAUDE_VERSION in .github/workflows/runtime-live-e2e.yml: that lane exists only to keep a TeamCreate-exposing runtime under live test (the single live consumer of this path), so when the pin no longer covers a TeamCreate-capable runtime — it moves forward or is dropped — the legacy branch has lost its last live consumer and is dead code. The AC-5 layering test binds to that pin's literal value, so a pin bump trips the test as the removal signal.
claude-fo-dispatch.mdThis skill is read in place of the current-runtime contract: where it conflicts with claude-fo-dispatch.md, this skill wins. The overrides:
Agent-only model becomes TeamCreate-first (## Team Creation below) — TeamCreate MUST run before any Agent/SendMessage.spacedock dispatch build: emits team_name (the name from the prior TeamCreate) alongside name; map it to Agent(team_name=…) instead of the no-team_name shape.spacedock dispatch spawn-standing-all: run it --team {team_name} (the claude-fo-dispatch.md form omits --team). The call is idempotent (already-alive members omitted, deduped against the team config), each spec carries team_name, and the standing teammate is team-scoped (dies with the team at TeamDelete). It MUST NOT precede a successful TeamCreate.spacedock dispatch reconcile: pass --team-name {team_name} (the claude-fo-dispatch.md form omits it and auto-discovers by leadSessionId); the explicit name is the team identity here.## Team Creation below) governs Agent()/team-registry errors before claude-fo-dispatch.md's ## Degraded Mode fires.TeamDelete procedure (## Terminal Team Teardown below) replaces the per-name ## Terminal Worker Teardown.The legacy Claude Code team tools (TeamCreate, TeamDelete, SendMessage) are deferred — their schemas are not loaded at session start, so calling one directly fails until its schema is fetched. Before the first call to any team tool, run ToolSearch(query="select:{ToolName}", max_results=1) to fetch its schema (e.g. ToolSearch(query="select:TeamCreate", max_results=1) before the first TeamCreate, ToolSearch(query="select:SendMessage", max_results=1) before the first SendMessage). Once a tool's schema appears in the ToolSearch result, it is callable exactly like a normal tool. Agent is not deferred. Address an agent by its declared name via SendMessage; your plain text output is NOT visible to other agents.
At startup (before dispatch):
TeamCreate MUST be the first team-mode tool call in every session, before ANY Agent or SendMessage invocation. Run ToolSearch(query="select:TeamCreate", max_results=1). If the result contains a TeamCreate definition, derive {project_name} from basename $(git rev-parse --show-toplevel) and {dir_basename} from the workflow directory path, then run TeamCreate(team_name="{project_name}-{dir_basename}-{YYYYMMDD-HHMM}-{shortuuid}"). The timestamp token must be lowercase and hyphen-separated — no uppercase, no colons — to stay compatible with Claude Code's NAME_PATTERN and the claude-team helper. {shortuuid} is eight lowercase alphanumeric characters.
team_name than requested. Always store the returned value and use it for all subsequent calls.rm -rf ~/.claude/teams/...) — stale directories belong to other sessions.Diagnostic-only startup probe: At startup the FO MAY inspect ~/.claude/teams/ with ls or test -f ~/.claude/teams/{project_name}-{dir_basename}*/config.json to REPORT stale on-disk team directories from prior sessions in the boot summary. This probe is DIAGNOSTIC-ONLY. Its result does NOT gate or skip TeamCreate — TeamCreate always runs with the fresh-suffixed name regardless of what the probe reports. On-disk state is not evidence of team health (Claude Code bug anthropics/claude-code#36806 leaves config files on disk after the in-memory registry desyncs). Deletion remains forbidden per the NEVER-delete constraint above — the probe surfaces stale directories; it does not authorize removal. No such probe belongs in the pre-dispatch path.
Once the team exists, the back-channel is the same as the current host's: lead→worker is SendMessage(to={name}), worker→lead is SendMessage(to="main"). The legacy difference is only the team-creation step above and the teardown below; the dispatch shape carries a team_name from the prior TeamCreate, and spacedock dispatch spawn-standing-all requires that team_name (it MUST NOT precede a successful TeamCreate).
TeamCreate recovery procedure: Call TeamDelete in its own message (no other tool calls). Wait for the result. Then call TeamCreate in a subsequent message. Store the returned team_name. Do NOT combine TeamDelete, TeamCreate, or Agent dispatch in the same message — Claude Code executes tool calls in a message in parallel, and dependent calls will race. This procedure applies ONLY to the narrow "Already leading team" case at startup (where Claude Code's in-memory slot holds a team the FO wants to replace cleanly). It is NOT a mid-session failure recovery and MUST NOT be invoked in response to "Team does not exist" or any other registry-desync signal — see the recovery ladder below.
TeamCreate failure recovery (priority-ordered ladder): If TeamCreate or any subsequent Agent() dispatch surfaces "Team does not exist" or any equivalent registry-desync signal mid-session, follow this ladder in order — do NOT retry within the same tier:
TeamCreate with a fresh name {project_name}-{dir_basename}-{YYYYMMDD-HHMM}-{shortuuid} computed at call time (new timestamp, new shortuuid, distinct from any name used earlier this session). Retry to the same team name is banned. Do NOT call TeamDelete on the failed team — the registry is already desynced and another TeamDelete → TeamCreate cycle will re-contaminate the same slot per anthropics/claude-code#36806. Store the returned team_name. All prior agent names are presumed zombified — do not SendMessage them; re-dispatch from entity frontmatter.## Degraded Mode in claude-fo-dispatch.md. A second dispatch failure (including failure of the tier-1 fresh-suffixed TeamCreate, or a second "Team does not exist" at any point in the session) trips Degraded Mode immediately.Agent itself is unavailable). Do not silently retry. Do not block indefinitely — report the failure, name the tiers attempted, and wait for captain direction.Block all Agent dispatch until team setup resolves (tier-1 fresh-suffixed TeamCreate succeeds or Degraded Mode is entered). Never dispatch while team state is uncertain.
This governs the TERMINAL phase only — AFTER the entity reached its terminal stage and the FO is dismantling the team. It is a DIFFERENT phase from ## Awaiting Completion in claude-fo-dispatch.md: that section bans emitting TeamDelete before a completion signal (the premature-teardown bug); this section attempts TeamDelete as a bounded best-effort after terminal cleanup has begun. Do not conflate the two — the ban is pre-completion, the bounded attempt is terminal.
When tearing down the team at the terminal boundary:
SendMessage({"type":"shutdown_request"}) to every roster member in the entity's cohort. This is cooperative — the member acknowledges and leaves the roster asynchronously.TeamDelete. The cooperative shutdown and TeamDelete race: the first TeamDelete can fail with Cannot cleanup team with N active member(s): {name} because a member you just signalled is still settling out of the team registry.TeamDelete. The member is leaving the roster asynchronously, so a retry fired in the same instant just re-loses the same race — this is exactly how #275 failed (it "retried 3× but all 3 raced before the registry settled, then stopped"). Instead, between attempts you MUST let the registry settle: re-send SendMessage({"type":"shutdown_request"}) to each still-named active member from the failure message, then wait for the settle before the next TeamDelete — Bash("sleep 2") is the settle (this is the one place a deliberate inter-attempt wait is correct; it is NOT the banned idle-poll sleep of ## Awaiting Completion, which fires with no pending teardown). Then call TeamDelete again. Repeat the settle-then-attempt serially up to a small attempt cap (≈5 attempts, each preceded by the ~2s settle). Attempts MUST be serial with a settle between them, never fired back-to-back in one message. In an interactive session the roster clears as the member's session-end propagates, so a TeamDelete lands cleanly on an early attempt and the loop exits there — the cap is never reached.members[] indefinitely (an upstream Claude Code defect: the dead member is never cleared from the roster), so TeamDelete keeps failing active member(s) against a member that has already terminated. A claude -p FO CANNOT exit while members[] is non-empty (the harness re-invokes it on end_turn), so an FO-driven process exit is impossible, and retry to success is unreachable — both re-hang the subprocess exactly like #275. So once the attempt cap is reached, the FO STOPS calling TeamDelete and emits the verbatim terminal-status marker — TERMINAL_TEARDOWN_BOUNDED: best-effort teardown exhausted; member(s) stuck in registry; holding for launcher.. The PROCESS EXIT is the launcher's responsibility — the live-e2e cycle's kill(), or a real automation's launcher-side timeout — never an FO self-exit. On a subsequent harness re-invocation with the roster still non-empty the FO again runs the bounded best-effort and re-emits the marker; a bounded resume that re-emits the marker is acceptable (the launcher ends the subprocess) — what is forbidden is an UNBOUNDED retry loop that never reaches the marker. In an interactive session the cap is not reached (step 3); if it ever were, also surface the still-active member(s) to the captain alongside the marker.Emitting the marker at the cap IS the fix: a teardown that gives up silently on the first active member(s) failure with NO marker (#275), or one that retries TeamDelete unboundedly past the cap and never reaches the marker (#282's live failure), both strand the claude -p FO subprocess waiting on an exit the FO cannot cause. The inter-attempt settle is what gives an interactive roster time to clear so TeamDelete lands; the attempt cap bounds the FAST back-to-back attempts so the non-interactive dead-but-listed member does not spin an unbounded retry loop; the marker is the deliberate terminal status the launcher's kill() ends the subprocess on. A failed TeamDelete here is the EXPECTED first step of a settling race, not a reason to give up silently; this is the one place the FO actively attempts TeamDelete (everywhere else — see ## Awaiting Completion in claude-fo-dispatch.md — emitting it is the bug).
npx claudepluginhub spacedock-dev/spacedock --plugin spacedockCoordinates multiple Claude Code instances as agent teams for workflows needing inter-agent communication. Covers TeamCreate, SendMessage types, task coordination, hooks, and orchestration patterns.
Provides advanced patterns for Claude Code Agent Teams: topology designs (hub-and-spoke, mesh network, pipeline), cross-team communication, worktree coordination, failure handling, and cost management.