Skip to content
Back to skills

Keel Swarm

ASecurity

EXPERIMENTAL — a multi-agent swarm coordinator that clusters backlog issues, executes parallel waves in isolated worktrees, and lands them under a single-writer merge lock. A live run implements each cluster and opens one PR per cluster; swarm-land merges each through keel merge once its review verdicts are posted — the swarm reviews them only with swarm-review, which is opt-in and has run once, on a sandbox repository, with single-vendor review seats (#1423); one live landing has run, on a s...

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 28, 2026
ai-agentsgobashawsgit

Works with

  • terminal
  • cli

Security analysis

A100/100

Scanned October 7, 2026

npx -y skills add berkayturanci/keel --skill keel-swarm --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Keel Swarm?

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

Security grade badge for Keel Swarm
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/berkayturanci-keel-swarm/badge)](https://www.skillsdirectory.com/skills/berkayturanci-keel-swarm)

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

SKILL.md
---
name: keel-swarm
description: EXPERIMENTAL — a multi-agent swarm coordinator that clusters backlog issues, executes parallel waves in isolated worktrees, and lands them under a single-writer merge lock. A live run implements each cluster and opens one PR per cluster; swarm-land merges each through keel merge once its review verdicts are posted — the swarm reviews them only with swarm-review, which is opt-in and has run once, on a sandbox repository, with single-vendor review seats (#1423); one live landing has run, on a sandbox repository (#1281), reviewed outside the swarm before swarm-review existed. Use /keel:ship for an issue that must merge today.
---

# keel-swarm

Use this skill when the user asks to run the keel command `swarm` (e.g. `keel swarm ...`, `swarm <args>`, or `/keel:swarm`). It reads every project value from `.keel/project.yaml` via the `keel` CLI.

# /keel:swarm

## ⚠️ Experimental — the swarm reviews its work only through the opt-in `swarm-review`, which has run once, on a sandbox repository

`keel swarm-plan`, `--plan-only` and `--tree` run and render a plan. A dry `swarm-run`'s worker is
`keel ship` the CLI subcommand — a *dry ship assessment* that reports tier, window, gates and a
decision and never commits, pushes or opens a PR. `swarm-run --live` (#1400) is different: keel
dispatches each cluster's implementer seat (`--delegate`, else `knobs.team.implement`; it must be an
agent CLI such as `claude`, `codex` or `agy` — a `subagent:` seat refuses the run) in the cluster's
own worktree, commits the result, runs the gates, pushes `swarm/<swarm_id>/<cluster_id>` and opens
one pull request per cluster. It needs the **operator's** consent — `--approve-scope
filesystem,git,github --operator <name>` — which you ask the user for and never supply on your own,
and its pull requests carry **no review evidence**: the swarm reviews them only when you run
`keel swarm-review` (Step 3 below; opt-in, and run once so far, on a sandbox repository with
single-vendor review seats — #1423), and
`swarm-land` merges each one through `keel merge` (#1287), so it holds every cluster until that
pull request's review verdicts are posted. The worker leaves the rest `keel merge` asks for
itself (#1420): the seat's attribution labels on the pull request and a gates-pass for its head
in the run ledger — when its result says `labels_applied: false` or `gates_recorded: false`, the
run's warning names the hold and how to clear it.

It is not free, though: that CLI runs `git diff` and executes the project's planned gates, and the
gate run is **not** behind `--live`. A dry `swarm-run` over N issues runs the whole gate suite N
times — one at a time, since a dry run has no worktrees and the runs share your checkout
(#1288). Budget for that before you start one.

Planning reads each issue's scope from the issue (#1274): a `Scope: a/b.py, docs/*` line or a
`## Scope` bullet list in its body, else its `area:<name>` labels mapped through
`policy_pack.scan.areas`. An issue that declares none — or that `gh` cannot read — is planned as
`*` and serialised against every other issue, and stderr names it; paths its text merely mentions
do not make it disjoint. So a backlog without `Scope:` lines plans one wave per issue, and
every wave after the first is `sequential_dependent`, which `swarm-land` refuses: land wave 1,
re-plan the rest, land again. To give an
issue a scope for this run, pass `--issue-scope <n>=<glob>[,<glob>…]` (repeatable, on
`swarm-plan`, `swarm-run` and `swarm-land` alike — pass the same ones to all three), and never
pass `--issue-title`/`--issue-body`/`--issue-label`/`--declared-file` beside several issues:
they describe one issue and are refused. `keel-visual swarm` draws the plan `swarm-run` persisted —
its waves, predicted files and dependencies — and says so when a run has none; keel-visual 0.9.0
still rebuilds a flat one-wave DAG (#1275, #1280).

So: **`--plan-only` is the one that stops**, and it already renders the ASCII tree — `--tree` is
passed on the `swarm-plan` calls either way, so adding it changes nothing. `--visual` is a
different matter twice over: it is read at Step 4, *after* Step 2, so on its own
`/keel:swarm <issues> --visual` walks straight into `swarm-run` and `swarm-land`; and
with `--plan-only` it never runs at all — and could not show anything if it did, because
`keel-visual swarm` reads the state file only `swarm-run` writes and falls back to an empty
board without it. A dry run of that kind runs the N child gate suites above, one at a time in
your checkout — for a run that implements and lands nothing. Read the plan it renders as "what I
passed", not "per-issue scope". A live run implements each cluster and opens one pull request per
cluster; `swarm-land` merges each through `keel merge` once its review verdicts are posted —
by `keel swarm-review`, or by hand. Swarm stays experimental. One live landing has run end to end,
once, on a sandbox repository (#1281): `swarm-run --live` implemented, gated, pushed and opened
the pull requests, their review verdicts were posted from outside the swarm, and `swarm-land`
merged them through `keel merge`. A landing now also closes what it merged as `/keel:ship` does
at s11–s12: the closure comment on the pull request and each issue, then the issues closed
(#1422). Since #1423 the swarm can review its own pull requests with `keel swarm-review`, and
that has run once, on 2026-10-02 on the same sandbox repository: `swarm-plan` → `swarm-run --live`
→ `swarm-review --live` → `swarm-land --live` merged both pull requests and closed their issues
with no hand step — but its review seats were two `claude` seats, a single vendor. Swarm stays
experimental for three reasons: that is one run on a toy repository, its review seats were
single-vendor, and run, review and land are separate opt-in commands. For an issue the user
needs **merged** today, say so and run `/keel:ship` per issue — it is still the proven path.

Do not hand-drive the children to work around this. `swarm-land` lands a cluster only through
`keel merge`, which holds any pull request without an armed gate, its review verdicts pinned to
its head, or a gates-pass for that head — and `knobs.swarm_review_evidence: false` no longer turns
that off (#1287). Driving the children by hand would skip the per-issue ledger and the backbone
that `/keel:ship` gives you. #1423 added `keel swarm-review`, which has run once, on a sandbox
repository with single-vendor review seats; the audit epic #1281 is closed.

## Live progress — stamp this run (required)

So this run shows live on `keel-visual`'s board, record it with `keel activity` **as you
go**. This command's phases are: `config` → `plan` → `isolate` → `execute` → `land` → `report`.
Pick one stable `--run-id` for the whole swarm execution (e.g. `swarm-<date-or-id>`):

- **Right now, before the work below**, stamp the first phase:
  `keel activity .keel/project.yaml --root . --write --command swarm --run-id "$RUN" --phase config`
- Re-run with the next `--phase` (`plan`, `isolate`, `execute`, `land`, `report`) **as you advance** through the flow.
- At the end: `keel activity .keel/project.yaml --root . --run-id "$RUN" --done`

Treat this like any other contractual step — do not skip it. The one allowed exception is a
core too old to ship `keel activity` (keel < 1.6.0): then skip it silently and never block
the command.

## Command step evidence

Every numbered step in this command is contractual. Complete the step, record the
evidence it asks for, or explicitly mark it `N/A — <reason>` before moving on. Any GitHub
comment, review, issue label, branch, PR, merge, report, or queue write must be posted or
written through the selected transport and cited in the final summary.
Never silently skip a step because the runtime, agent, or prompt feels obvious.

Run a high-concurrency multi-agent swarm: partition dependent and independent backlog issues
into topologically ordered execution waves, execute disjoint clusters in parallel isolated
git worktrees, and land them under a single-writer merge lock with sequential
`git merge --no-ff` into the local base branch — a local merge that pushes nothing.

## Who does what — CTO, team lead, worker

Three levels, and each one only does its own job:

- **You are the CTO.** You cluster the backlog, launch one lead per cluster, and land the
  waves. You do not implement, review, or drive a child ship yourself.
- **One team lead per cluster.** A lead is a subagent you spawn for exactly one cluster. It
  runs that cluster's `/keel:ship` runs with the providers the cluster's `assignment` names,
  and it reports through the cluster's worker status record — the same records
  `keel swarm-status` renders, so a lead needs no reporting channel of its own.
- **Workers are the child ship runs** the lead drives, one per issue in its cluster.

The hierarchy is not a suggestion about tone: a lead that reports up through anything other
than the worker record is invisible to the board, and a CTO that implements has no one left
to land the wave.

## Step 0 — Resolve config + swarm contract

```bash
keel validate .keel/project.yaml --root .
keel plan     .keel/project.yaml --root . --command swarm --live --json
keel window   .keel/project.yaml
keel swarm-plan .keel/project.yaml --issues <n,n,n> --tree
```

Parse `contract.operator_consent` before selecting work, creating branches/worktrees,
spawning subagents, opening PRs, merging, writing reports, or touching GitHub labels or
comments. If `requires_operator_consent` is true, STOP and ask the operator to rerun with
the required `--approve-scope` values. Pass
`operator_consent.delegated_agent_scope` into every child `/keel:ship` handoff. Children
may use only `approved_mutation_scopes`; scope expansion blocks or escalates.

## Step 1 — Deterministic static dependency analysis, scoring & staffing

Run static dependency analysis, scope prediction, difficulty scoring and per-cluster
staffing across the target issue set:

```bash
keel swarm-plan .keel/project.yaml --issues <n,n,n> --tree --json
```

Pass the operator's staffing flags straight through — `--delegate`, `--review-delegate`,
`--effort`, `--team <profile>` and `--reviewers` — so the plan shows the team the run will
actually dispatch rather than the default one.

- Inspect the generated waves, disjoint clusters, conflict edges, and direct landing eligibility.
- Read stderr and each issue's `scope_source` in `issue_scopes`: `default` (scope `*`) means
  the issue declared no scope — or could not be read — and so runs alone, in a wave
  `swarm-land` refuses until the waves before it land and the rest is re-planned. Report those issues
  to the operator instead of inventing a scope for them.
- Each cluster carries a `difficulty` (`band`, `score`, `tier` and the `signals` that
  produced them) and an `assignment` (`lead`, `implementer`, `effort`, `reviewers`,
  `review_panel`, `gate`, `fix`). Both are resolved by core from `knobs.team` plus
  `knobs.team.by_difficulty`; do not re-derive either, and do not substitute a provider of
  your own choosing for one the assignment names.
- Read `assignment.warnings` before launching anything. A `--team` profile that names no
  configured bench, or a gate that is its own implementer, is reported there and nowhere
  else.
- If `--plan-only` was requested, render the ASCII DAG tree and exit.

## Step 2 — Launch one lead per cluster

```bash
keel swarm-run .keel/project.yaml --root . --issues <n,n,n>
```

This is the dry run: it assesses each cluster and commits nothing. A worker gets its own git
worktree (`.keel/worktrees/<swarm_id>/<cluster_id>/`, branch `swarm/<swarm_id>/<cluster_id>`)
only when the run is not dry — so in the one mode this command
allows, no worktree is created: each cluster's child assessment runs in your own checkout,
one at a time (#1288). Add `--live` only when the user has consented to the scopes it needs
(see the top of this command) and wants one unreviewed pull request per cluster; otherwise the
implementation is the leads' work, below.

- Spawn **one team lead subagent per cluster**, briefed with that cluster's `assignment`
  and `difficulty` verbatim. The lead runs the cluster's issues through the standard
  `keel ship` backbone steps (`s0`–`s12`) in an isolated worktree of its own, cut from
  `base_branch` (config) — `swarm-run`'s dry run did not create one.
- The lead passes its cluster's team to every child ship it starts, using the **same five
  flags a work block hands down** — `keel ship` accepts all of them:

  ```
  keel ship <project.yaml> --issue <N> --delegate <assignment.implementer> [--review-delegate <provider>]... [--role <assignment.role>] [--effort <assignment.effort>] [--team <assignment.team_profile>]
  ```

  One `--review-delegate` per staffed reviewer slot, in slot order. `--effort` and
  `--team` carry the bench the cluster was staffed from, so the child's own resolution
  reproduces the parent's instead of re-deriving a different one from config alone.
  `keel swarm-run` appends exactly this set for the runs it starts itself; a lead driving
  ships by hand appends the same. A seat that is a host `subagent:` rather than a provider
  is not a `--delegate` value — spawn it as a subagent instead.
- A role that is not `[A-Za-z0-9][A-Za-z0-9._-]*` is **not** passed: it would be parsed as
  a flag by the child and break the run. Core drops it and says so in
  `assignment.warnings`; the child resolves its role from the issue's own labels.
- A lead never re-scores its cluster and never re-staffs it. If the work turns out heavier
  than the band said, it reports that through the worker record and the CTO re-plans.
- When a cluster's issue fails, `rebalance_swarm_plan` drops the clusters carrying that issue from the remaining waves; there is no runtime file-divergence detection — clusters are kept apart by plan-time overlap partitioning (and, in a non-dry run, per-worktree isolation).
- After `swarm-review --live`, the board's `Review` column shows each cluster's last review
  (`posted 2/2 APPROVE @ <head>`, `changes-requested …`, `held …`, `not reviewed`) — a report:
  only `keel merge`'s evidence gate decides whether a cluster lands.
- Track live worker states with `keel swarm-status` — the board's `Lead` and `Band` columns
  are how the operator sees which lead owns which cluster and why it drew its provider.

## Step 3 — Batch landing under the merge lock

**Review first — opt-in (#1423).** A live wave's pull requests carry no review evidence. Either
the user posts the verdicts (`keel review --live` per pull request), or `keel swarm-review`
dispatches each cluster's own reviewer seats — the ones `swarm-plan` printed as `review X, Y`,
each read-only through `keel delegate run --role review` in its own checkout of the head — and
posts their verdicts with `keel review`, pinned to that head. Preview, then review; the live run
needs the same operator consent, which you ask the user for:

```bash
keel swarm-review .keel/project.yaml --root . --swarm-id <swarm_id> --wave <n>
keel swarm-review .keel/project.yaml --root . --swarm-id <swarm_id> --wave <n> --live \
  --approve-scope filesystem,git,github --operator <name>
```

Report each cluster as the command does: `posted` (every seat approved),
`posted-changes-requested` (the verdicts were posted, and at least one requests
changes — `keel merge` holds the pull request on `review-verdict-not-approved` with the seat
named, so relay that seat's findings), `held` (nothing posted: the head moved, a seat changed the
git setup, a seat that did not answer readably, or too few approvals), `refused` (a `subagent:` seat, a seat
from the implementer's vendor, too few eligible seats, the distinct-vendor rule — suggest
`--review-delegate <provider>` per slot), `skipped` or `already-merged`. A seat whose answer does
not parse is `failed`: never an approval, and nothing of it is posted — and a failed seat holds
the cluster's approvals too (only a rejection is posted beside it). A seat that rejected is
never discarded, however thin its answer. Never post an approving
verdict yourself on top of a change request to get a cluster through; fix the findings, push,
and run it again on the new head.

When an execution wave completes and is reviewed, land it: `keel swarm-land` merges each cluster's pull request
through `keel merge` against the project's `base_branch` (config — never assume a branch name).
Preview first, then land; both need the operator's consent, which you ask the user for:

```bash
keel swarm-land .keel/project.yaml --root . --swarm-id <swarm_id> --wave <n> \
  --approve-scope filesystem,git,github --operator <name>
keel swarm-land .keel/project.yaml --root . --swarm-id <swarm_id> --wave <n> --live \
  --approve-scope filesystem,git,github --operator <name>
```

- The landing mode is **derived from the plan's wave mode**, not passed on the command line.
- **Each cluster's pull request goes through `keel merge`** (#1287) — the one the live worker
  recorded, else the one open pull request for `swarm/<swarm_id>/<cluster_id>` — one at a time:
  merge window, merge lock, merge state, CI, review evidence, gates-pass, head-pinned squash,
  drift check. Report each cluster as the command does: `landed` (merged on GitHub, its pull
  request closed as merged), `held` with `keel merge`'s reason (no open PR, a closed or `DIRTY`
  one, the window, missing evidence, no gates-pass for the head, the lock), or `failed` (the
  merge call itself). A held or failed cluster does not stop the next; the exit code is 0 only
  when every cluster landed. Nothing is checked out or merged in the local checkout.
- **A landed cluster is closed** (#1422), as `/keel:ship` closes an issue at s11–s12: keel
  appends the landing's `ship_run` record, posts the `keel.closure-comment.v1` comment rendered
  from it to the pull request and to each of the cluster's issues, and closes each issue as
  completed — under the same consent (`github` covers it). Do not post closure comments or close
  the issues by hand on top of it. Report each cluster's `closures` entry: `closure_posted`,
  `closed_issues`, `already_closed`, and any `warnings` — a warning means that part did not
  happen (the merge stands); tell the user what is left. A held or failed cluster, and the
  preview, close nothing.
- **A dependent wave is refused.** A `sequential_dependent` wave — in a fresh plan, every wave after the first — had its branches cut before the earlier wave it depends on landed, so `swarm-land --wave N` refuses it, dry run or live: no pull request looked up, no `keel merge`, exit 1, `"mode": "refused"` with the reason in `refused`. Land the earlier wave, then re-plan the remaining issues (`keel swarm-plan` / `swarm-run` without the landed ones) and land again.

## Step 4 — Visual tracking & terminal dashboard

Render the spatial DAG cluster graphs and pseudo-3D wave topology for the swarm (a rendered snapshot):

```bash
keel swarm-status .keel/project.yaml --root .
keel-visual swarm .keel/project.yaml --root . --out keel-swarm.html
```

When `--visual` was requested, launch the localhost visualizer dashboard:
```bash
keel-visual swarm .keel/project.yaml --root . --serve --port 8766
```

A live worker that failed after its implementer seat ran keeps its worktree
(`.keel/worktrees/<swarm_id>/<cluster_id>/`) and branch for inspection, and names the path at
the end of its output. Report those paths to the user. When they are done with them — or after
a killed run — list and remove what swarm runs left behind (#1278):

```bash
keel swarm-status .keel/project.yaml --root . --orphans
keel swarm-status .keel/project.yaml --root . --clean
```

`--clean` never removes an unfinished run's leftovers unless `--swarm-id` names that run (only
do that once you know no `swarm-run` of it is still running), and never a branch its worker
pushed or opened a pull request for.

## Step 5 — Swarm recap report

Compile the overall multi-agent swarm outcome:
- Total issues planned, clustered, and executed.
- Per cluster: its difficulty band and score, its lead, and the implementer/reviewer seats
  that ran it — plus any `assignment.warnings` that were raised and what was done about them.
- Worker success/failure breakdown.
- Landing outcome per cluster: merged into the local base branch (not pushed; its pull request still open), or `merge failed` / held with its reason.
- Per-cluster review outcome (the configured review, or the ai-jury panel on tier-3) and each cluster's `compound-learning:` ledger marker.
- Record final completion:
  `keel activity .keel/project.yaml --root . --run-id "$RUN" --done`

<!-- keel-generated: surface=skills command=swarm keel_version=1.31.0 source_sha256=2b5dd80ea0fbaf5e506abac61622ab870e0b68335f3bacaa71cc687390f57ec9 generated_sha256=b2919bd95fad28ce57fe0436c73ed40d1eb10728c12468f354b464dbbd13f946 -->

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.