Spoke dressing sync (multi-coder)¶
Part of the spoke lifecycle — see spoke-lifecycle.md for the end-to-end map (scaffold → register → dress → watch). This page is stage 3 in detail.
One-way sync of the multi-coder agent helper surface from this hub to the spoke repos. This is the "dressing" routine: it turns a bare spoke into a fully-dressed AgentArmy template for every supported agentic coder — Claude Code, OpenAI Codex, and Gemini Antigravity — so whichever coder opens the repo finds its native agent roster, config, and the shared behavioral law already present. Spoke microVM agents run in isolated environments and can't reach the hub, so without this they're "blind". This pushes repo-local copies into each spoke.
Hub is the source of truth. Edit the helpers in the hub; never hand-edit the synced copies in a spoke (they get overwritten on the next sync).
What syncs¶
Declared in scripts/spoke_sync.config.json,
grouped by coder:
Shared (all coders)
- AGENTS.md — the shared, fleet-wide agent guidance (roster, routing, skills,
chaining). Authoritative; the spoke's CLAUDE.md imports it via @AGENTS.md. This
is also OpenAI Codex's primary instruction file.
- CONSTITUTION.md — the supreme behavioral law; a seeded CLAUDE.md imports it via
@CONSTITUTION.md.
Claude Code
- .claude/agents/, .claude/commands/, .claude/agent-schema.json
- .claude/settings.json + the scripts its hooks call (scripts/mempalace_hook.py
and the .ps1 variant). MicroVMs won't have MemPalace installed — those hooks
fail gracefully, so syncing the settings is safe.
OpenAI Codex
- .codex/agents/ (TOML agent profiles), .codex/config.toml (shell + MCP policy,
env-var indirected — no secrets), .codex/hooks.json (session lifecycle).
- scripts/sync_agents_to_codex.py rides along so .codex/hooks.json is
self-consistent and the spoke can regenerate its Codex pack from
.claude/agents/categories/.
Gemini Antigravity
- .agents/plugins/ (per-category agent plugins) + scripts/sync_agents_to_antigravity.py.
PR-event automation (hub-authoritative)
- .github/workflows/claude.yml (@claude responder), copilot-review.yml
(auto-request Copilot review), review-loop.yml (autonomous review-fix loop). These
are portable (${{ github.repository }} / ${{ github.repository_owner }}), so the hub
version runs verbatim in any spoke — every spoke gets the same review automation.
- Dressing also seeds the review-loop bootstrap labels (review-loop,
review-loop:done, review-loop:escalated) in each spoke. The synced review-loop.yml
only self-creates these from inside its own label-gated job, so a fresh spoke couldn't
otherwise opt in (you can't add a label that doesn't exist). sync_helpers_to_spokes.py
creates them up front (ensure_review_loop_labels).
- ⚠️ Files synced ≠ functioning. Each spoke still needs its own secrets set for
these to run: CLAUDE_CODE_OAUTH_TOKEN, PROJECT_TOKEN, and (optional, for fleet MCP)
LOCAL_FLEET_MCP_URL / CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET. The fleet
heartbeat checks file presence (pr-subscription-missing), not secrets.
Other helpers
- scripts/board_commands.py, scripts/onboarding-check.ps1, tools/status.mjs,
tools/fleet-heartbeat.mjs, .mcp.json, .agent/*, notify-hub*.yml (PR artifacts → hub).
Directories are mirrored (deletions in the hub propagate), and synced paths are
force-added so a spoke's .gitignore can never silently drop them. A provenance
stamp is written to .claude/.agentarmy-sync.json recording the hub commit.
CLAUDE.md is seeded only if the spoke lacks one (it imports @AGENTS.md and
leaves room for spoke-specific notes) and is on the deny list — the sync never
overwrites a spoke's own CLAUDE.md, since that is repo-specific.
Never synced (hard denylist, enforced even if added to the manifest):
CLAUDE.md, settings.local.json, *.local.json, .claude/worktrees/, .env, credentials.
Adding a spoke¶
Add the repo name to spokes in scripts/spoke_sync.config.json,
then run the sync. That's it — the next run clones the spoke, mirrors every pack, and
opens a dressing PR. The current spokes are frontend-core, backend-core,
middle-core, and agentarmy-forge.
Adding a coder (dressing for a new agentic coder)¶
The routine is config-driven and easy to extend — no code changes needed for a new coder:
- Generate the coder's agent pack in the hub (e.g. a
sync_agents_to_<coder>.pythat renders.claude/agents/categories/into the coder's format), and run it so the pack dir exists. - Add the pack dir(s) — and any regenerator script the coder's hooks call — to
pathsin the config, under a new "coder" group (see the$pack_note). - Run the sync. Every spoke now ships that coder's dressing too.
validate_paths() refuses to run if any listed path is missing from the hub, so a
typo fails fast instead of half-dressing a spoke.
Running it¶
Locally (uses your gh auth):
python scripts/sync_helpers_to_spokes.py --dry-run # clone + diff, no push
python scripts/sync_helpers_to_spokes.py # PR per spoke + auto-merge into main
python scripts/sync_helpers_to_spokes.py --no-merge # open the PR, leave it for review
python scripts/sync_helpers_to_spokes.py --spoke backend-core
By default the sync auto-merges (squash) the PR into each spoke's default branch —
an unmerged PR means the helpers never reach the spoke, which is the whole point. Use
--no-merge (or set "auto_merge": false in the config) to leave PRs open for review.
In CI — .github/workflows/sync-helpers-to-spokes.yml:
workflow_dispatch(optionalspoke/dry_runinputs), and- automatically on push to
mainthat touches any synced path.
Auth: the workflow uses PROJECT_TOKEN (classic PAT, already scoped for this
org's repos). The built-in GITHUB_TOKEN can't write to other repositories. If
PROJECT_TOKEN can't yet write to a spoke, grant the token access to that repo.