Skip to content
Back to skills

Coordinator Rolling Out To Nodes

ASecurity

> This skill is the ORCHESTRATION layer. It does not replace > `coordinator-deploying-a-node` — it calls it, once per node.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
devopsgoshellnode

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add thrum-team/thrum-pro --skill coordinator-rolling-out-to-nodes --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Coordinator Rolling Out To Nodes?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Coordinator Rolling Out To Nodes
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/thrum-team-coordinator-rolling-out-to-nodes-thrum-pro/badge)](https://www.skillsdirectory.com/skills/thrum-team-coordinator-rolling-out-to-nodes-thrum-pro)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: coordinator-rolling-out-to-nodes
description:
  "Use when rolling a build across all nodes — fleet deploy, rolling the nodes,
  deploy everywhere, ship to every node, canary then the rest, promote the
  canary, staged rollout, deploying after a merge batch. Orchestrates the staged
  order and the promotion gate between stages, and delegates each node to the
  coordinator-deploying-a-node runbook. Load this BEFORE dispatching any node."
# source: claude-plugin/skills/coordinator-rolling-out-to-nodes/SKILL.md
---

## Coordinator: Rolling Out a Build Across All Nodes — staged order, canary, promotion gate

> This skill is the ORCHESTRATION layer. It does not replace
> `coordinator-deploying-a-node` — it calls it, once per node.

### 0. Universal invariants (read first — these outrank everything below)

These hold for every deployment regardless of project or topology. The adapter
supplies _how_, these define _what must be true_.

1. **Build once; distribute identical bytes.** One pinned immutable artifact
   (SHA/version/checksum triple) is built once at one SHA on the build host;
   every node installs from that same artifact. No node re-derives it. Pin exact
   SHA, never tip. Adapter: `fleet.deployment.thrum.artifact` or
   `fleet.deployment.products.<id>.artifact`.
2. **Target-local mutation.** The node-local coordinator performs the mutation
   (install/migrate/restart) locally on its own host. Never drive a node's
   build/install/restart from another host. The orchestrator dispatches and
   gates; the node executes.
3. **Immutable identifier + checksum.** Artifact is identified by SHA +
   version + checksum; verify BEFORE install that the delivered bundle is the
   one the orchestrator built. Adapter: `artifact.checksum`,
   `artifact.version_file`, `artifact.commit_file`.
4. **Canary evidence does not transfer.** A canary pass proves the _build_ is
   sound; it does not prove any other node's _state_ is sound. Every node takes
   its own BEFORE readings.
5. **Verify by observed effect, never exit code.** Install/restart success is
   proven by post-mutation observation (version equals pin, NEW PID, service
   status reports pin, migration moved), not by installer exit 0. Adapter:
   `install.verify_by_effect`, `health.probes`.
6. **Capture before/after.** For every node, capture BEFORE state (current
   version/state, health counters) before mutation and AFTER state after;
   compare.
7. **Account for every target.** Roster completeness: enumerate topology from
   config, disposition each node (rolled on pin / deferred with owner sign-off +
   notify local coordinator), and never normalize unmeasured rows.

Additional parameterized invariants (skill-owned, adapter-valued):

- Pull-current first (ff-only) before reasoning about state. Adapter:
  `preflight.pull_command`.
- Backup before touching anything; sizing via adapter formula. Adapter:
  `backup.command`, `backup.sizing_formula`.
- Never bypass the storage engine's file ownership. Adapter:
  `storage_access.via`, `storage_access.forbidden`.
- Install-before-restart ordering; re-render between install and restart.
  Adapter: `templates.order`.
- Convergence wait from mechanism, not impatience. Adapter:
  `health.convergence_wait`.

### 1. Enumerate topology (from adapter — never hard-code)

Read the project's deployment adapter inside `.thrum/config.json`
(`fleet.deployment`; human-readable reference at the deployment-adapter
reference). Never hard-code topology or commands in the dispatch or in this
skill — read them from that single source.

- For Thrum fleet plus connected daemons: `fleet.deployment.thrum.topology`
  (`canary`, `waves`, `excluded`, `connected_daemons`, `identity_resolution`).
- For a customer product `<id>`: `fleet.deployment.products.<id>.topology` (same
  shape, per-product `canary`/`nodes`/`waves`).

Resolve each node's `daemon_id` / `ssh_alias` / `coordinator` via the adapter's
`identity_resolution` method (daemon_id join, not name). If topology is empty,
STOP — do not invent nodes.

**Why every node carries an `ssh_alias`:** so the rolling coordinator can reach
the node directly when the messaging or pane-mirror path fails. The main case is
an agent stuck on a permission prompt or dialog. Read the pane with
`ssh <alias> tmux capture-pane -p -t <session>`, then answer a prompt you
recognise as safe with `tmux send-keys`. Never approve blindly, and never pick a
"don't ask again" option. Re-capture afterwards to confirm the pane moved on. A
node flagged `blocked-on-human` gets unblocked this way in the same turn; don't
wait on it or reorder the roll around it. The alias is for pane interaction and
artifact distribution only. The node still builds, installs and restarts itself,
through its own coordinator.

> Example — Thrum project: topology canary and waves are defined in
> `topology.canary` / `topology.waves` (seven boxes, one excluded), connected
> daemons per `topology.connected_daemons`. Build host and build command are
> adapter values, not skill literals.

### 2. Pin the artifact (build once at SHA/version/checksum)

Derive the immutable pin on the build host (adapter `artifact.build_host`):
current `HEAD` SHA, version, and checksum. When multiple related artifacts
(e.g., core plus connected-repo bundles) are involved, pin each immutably and
distribute the same set to every relevant node. Announce the pin in the dispatch
so every node can verify it.

Artifact identity lives in `artifact.output`, `artifact.version_file`,
`artifact.commit_file`, `artifact.checksum`. Never use a floating tag like tip;
always the pinned SHA.

### 3. Preflight (control-plane current, forward-only, backup sizing via adapter)

- Verify control-plane checkout is current: `preflight.pull_command` (must be
  ff-only; if not fast-forwardable, STOP and surface).
- Forward-only check per node: `preflight.forward_only_check` — prove the pin is
  an ancestor of the node's current SHA (plus a control that must FAIL, e.g., an
  invented SHA, to prove the check discriminates).
- Backup sizing via adapter: `backup.sizing_formula` — compute
  `free_at_completion` per the formula before running `backup.command`; if the
  projected peak exceeds the safe threshold, STOP and escalate.

### 4. Build once → distribute identical artifact (mechanism via adapter)

Build once per artifact on its `artifact.build_host` via
`artifact.build_command` → verify the produced bundle's version/checksum/commit
sidecar → distribute the identical bytes to every node's inbound drop path
(`artifact.distribution.drop_path`) via the adapter's
`artifact.distribution.mechanisms` (e.g., drop-folder or copy). No node
re-derives or rebuilds. Verify BEFORE unpack that the delivered bundle matches
the pin.

> Example — Thrum project: build once via the adapter's `artifact.build_command`
> on the adapter's `artifact.build_host`, distribute via the adapter's
> `artifact.distribution.mechanisms` to `artifact.distribution.drop_path`,
> bundle carries sidecars verified per adapter.

### 5. The staged order — canary, waves, prepare-and-hold, promotion gate

> 🔴 **If any node fails during the rollout — canary or any wave — STOP THE
> ROLLOUT.** Do not roll, restart, or recommend restarting any further node
> (including the build host) until the failure is root-caused and fixed or the
> owner explicitly decides to continue. A node that went down and was recovered
> by hand is a FAILURE, not a green.

- **Canary first, alone.** One node (adapter `topology.canary`) rolls first. Its
  health must be observed green before any wave proceeds.
- **Waves with coordinated dependency/order/rollback.** Subsequent waves follow
  `topology.waves`. When related daemons share ordering (e.g., connected-repo
  daemons that must restart together), respect the adapter-defined dependency
  and restart order; a failure in one dependent daemon triggers coordinated
  rollback per adapter `rollback` / `backup.restore_command`.
- **Prepare-and-hold + release BY NAME.** If the mechanism supports it, prepare
  all nodes in a wave (acquire + verify artifact, take BEFORE snapshot, but do
  not restart) then release by explicit node name, not by broadcast.
- **Promotion gate.** After each stage, verify by observed effect on that node
  (see node skill) before promoting. On no-go, halt the rollout and roll back
  the affected node(s) per adapter.

### 6. Dispatching a node — what the message must carry

Dispatch each node to its own local coordinator (resolved via
`identity_resolution`), never via remote shell for the mutation itself. The
dispatch message must carry:

- Pinned artifact identifier (SHA/version/checksum) and drop-path location.
- Which adapter section to use (`fleet.deployment.thrum` vs
  `fleet.deployment.products.<id>`).
- Expected pin for verification and the BEFORE-snapshot request.
- Hold/release semantics if using prepare-and-hold.

The node's local runbook is `coordinator-deploying-a-node` — reference that
skill by name in the dispatch.

### 7. After the last node — roster completeness, phase reconciliation, test gate

- Enumerate the topology again from the same adapter source and disposition
  every entry (rolled on pin / deferred with owner sign-off + notify local
  coordinator). Missing or unmeasured rows are not normalized.
- Reconcile registry/phase: verify every daemon reports the pin (adapter
  `health.probes` + `install.verify_by_effect`), and that agent phases have
  converged.
- Run the project's gate that is appropriate for the change (e.g., `make gate`
  or equivalent) before declaring success.

### 8. Holds and red flags

- If `fleet.deployment` is absent or `fleet.deployment.products` and
  `fleet.deployment.thrum` are both empty, STOP — no topology is defined.
- If the control-plane pull is not fast-forwardable, STOP.
- If `fleet.deployment.thrum.products` exists (vestigial), STOP — products live
  only at `fleet.deployment.products`.
- If a node reports success by exit code alone without observed-effect
  verification, treat as not-verified.
- Never wrap a service restart in a blind timeout on the node (see node skill
  `service.never_timeout` and `migration.signal_rules`).
- 🔴 A **macOS** node restarted with the plain `service.restart_command` instead
  of `service.mac_restart_command` (Example — Thrum adapter:
  `service.mac_restart_command`). The daemon loses its Local Network permission
  and its LAN peers go dark while it looks healthy. Put the mac command in every
  macOS node's dispatch, and use it yourself when you roll the build host last.

Files in this skill

  • SKILL.md10.4 KB
  • agents/openai.yaml873 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.