Files
improvise/.claude/skills/wave/SKILL.md
T
Edward Langley 5c3c796308 docs: a wave skill, so the brief stops being retyped
Four agents were dispatched without worktree isolation and all edited the same
working tree: one git status held one agent's effect.rs, another's
harness/build.sh and a third's scripts/node-test.sh. Any commit would have swept
up three units' half-finished work.

The brief had carried that rule before and lost it, which is what a retyped brief
does. It is now a skill: worktree isolation first, partition by crate rather than
ticket count, the base commit by name, the baseline, the never-write list, the
injection discipline, pr-watch --expect, and attribution.

Two doctrines: every_wave_agent_gets_its_own_worktree records the tell and the
per-agent recovery (copy only what an agent can PROVE it touched -- two units can
legitimately both be in improvise-protocol); the_orchestrator_delegates_substantial_work
records the user's rule and the non-obvious half of it, that orchestrator work
lands in the shared primary tree and collides with agents who own that ground.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:40:32 -07:00

4.2 KiB

name, description
name description
wave Dispatch parallel agents to work through open beads and shepherd each to a merged PR. Use when an orchestrator is running a wave, partitioning the backlog, or writing a brief for a work-unit agent.

Running a wave

An orchestrator chunks the open backlog, fans agents out, reviews what comes back, and merges. AGENTS.md has the history; this is the operating procedure.

Every agent gets its own worktree. No exceptions.

Dispatch with isolation: "worktree". Without it every agent edits the same working tree: one git status shows four agents' half-finished files, any commit sweeps up the others' work, and any git checkout destroys it. This has happened here. If you discover it mid-wave, stop every agent, have each create a worktree and copy across only files it can prove it touched, then git checkout -- those files in the primary tree.

In the worktree: direnv allow, or bd reports a version mismatch rather than failing cleanly. direnv status prints Found RC allowed 0, and 0 means allowed — it reads like the opposite.

Partitioning

By crate or directory, never by ticket count. Tickets that share a file go to one agent, to be done sequentially — four widget migrations all touching src/draw.rs are a guaranteed conflict split four ways. Group first, then count.

Tell each agent what ground is not theirs, and to report rather than guess when it must cross. Two agents adding a workspace member will still collide in Cargo.toml/Cargo.lock; keep both sides.

What every brief must carry

  1. The expected base commit, by name. Worktrees do not reliably branch from HEAD — one was created three merges behind and only noticed because it was told to check.
  2. The baseline, so "N passing" is checkable rather than asserted: Rust test count, harness counts, node counts.
  3. Its ground, and the ground that is not.
  4. Never write .beads/**, prolog-sessions/kb.pl, context/repo-map.md. Agents read them freely and report proposed text; the orchestrator applies it.
  5. The commit trailers and PR footer, spelled out.
  6. Do not push to main, do not merge. The orchestrator merges.
  7. What to report: branch and head SHA, PR number, what changed per ticket, what could not be verified and why, proposed repo-map/kb text, and what it learned that the ticket did not predict — consistently the most valuable part, and omitted unless asked for explicitly.

Method to require, because this repo's defect is a test that passes

  • Failing test first, then the fix.
  • Every guard proved by injecting the fault it names and watching it fail by name. An injection that does not compile reads as a clean run, so require a positive signal the build ran.
  • cargo test --workspace is fail-fast; --no-fail-fast for any claim about blast radius.
  • A textually clean merge can delete tests with no conflict marker, and cargo check will not notice. After any merge that moves a test module, diff test names — a count is masked by the other side's additions.

Shepherding

  • Wait on checks with scripts/pr-watch <pr> --expect <sha>. Never hand-roll a poll; see the pr-watch skill for the three ways they report green while wrong.
  • Review before merging. Copilot's quota may be exhausted and its request is a silent no-op; an absent review and an unrequested one both read as "pending". Dispatch an adversarial reviewer and give it the head SHA, the base, and a test-merge, so it reviews the tree that will exist.
  • A re-review is of the delta, at a named SHA. A review of a superseded commit reads as a pass.
  • Merge on GitHub, then git fetch gh && git merge --ff-only gh/main.
  • Anything posted to GitHub goes out under the owner's account: open every comment with a line saying a Claude agent wrote it.

Between waves

Run the code-review skill on the merged range. The characteristic find here is not a wrong line, it is a test that passes while the thing it names is broken — a vacuous fold test, a one-category fixture, a helper that fabricates what the code under test needs. Ask the reviewer to inject the fault and watch the check fail.