wave skill: one home for the brief checklist and the agent rules (improvise-1f8e)

.agents/skills/wave is the one copy; .claude/skills/wave is a symlink to it,
as beads-merge already is. SKILL.md holds the brief checklist, agent-rules.md
the agent-side rules, and AGENTS.md points at both instead of carrying a third
list. The bd-generated Session Completion block is overridden in agent-rules.md
rather than hand-edited (improvise-x1uk).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-22 21:04:01 -07:00
parent 7eed469970
commit b6d2c47ae3
5 changed files with 77 additions and 171 deletions
+37 -27
View File
@@ -7,6 +7,8 @@ description: Dispatch parallel agents to work through open beads and shepherd ea
An orchestrator chunks the open backlog, fans agents out, reviews what comes 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. back, and merges. `AGENTS.md` has the history; this is the operating procedure.
This file is the brief checklist's one home, and `agent-rules.md` beside it
holds the rules each agent follows.
## Every agent gets its own worktree. No exceptions. ## Every agent gets its own worktree. No exceptions.
@@ -17,9 +19,8 @@ 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 --` and copy across **only files it can prove it touched**, then `git checkout --`
those files in the primary tree. those files in the primary tree.
In the worktree: `direnv allow`, or `bd` reports a version *mismatch* rather than In the worktree `.envrc` must be allowed, or `bd` fails confusingly:
failing cleanly. `direnv status` prints `Found RC allowed 0`, and `0` means `agent-rules.md`, "Discipline per ticket".
allowed — it reads like the opposite.
## Partitioning ## Partitioning
@@ -33,32 +34,39 @@ it must cross. Two agents adding a workspace member will still collide in
## What every brief must carry ## What every brief must carry
**Point the agent at [`agent-rules.md`](agent-rules.md) and tell it to read it in
full first.** That file is the agent's standing rules: base and push mechanics,
the gate list, the claim, the vacuity shapes, evidence, the files it must not
write, GitHub conventions, the hand-back and disk cleanup. Do not restate them
in the brief. A restated rule drifts from its source, and the brief is the copy
nobody corrects. The brief adds only what varies per wave:
1. **The expected base commit, by name.** Worktrees do not reliably branch from 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 HEAD. One was created three merges behind and was only noticed because it
told to check. was told to check.
2. **The baseline**, so "N passing" is checkable rather than asserted: Rust test 2. **A reference baseline**, so "N passing" is checkable and a drop is
count, harness counts, node counts. visible: Rust test count, harness counts, node counts, **and the machine**.
3. **Its ground, and the ground that is not.** The same commit once measured `improvise-io`'s binary at 1.4s on one machine
4. **Never write** `.beads/**`, `prolog-sessions/kb.pl`, `context/repo-map.md`. and 3.7–5.2s on another, so a figure with no machine is a claim about the
Agents read them freely and report proposed text; the orchestrator applies it. suite, which it is not. The agent still measures its own.
5. **The commit trailers and PR footer**, spelled out. 3. **Its ground, and the ground that is not.** Name any orchestrator-only file
6. **Do not push to `main`, do not merge.** The orchestrator merges. it is exceptionally allowed to edit.
7. **What to report**: branch and head SHA, PR number, what changed per ticket, 4. **Its `BEADS_ACTOR` and its tickets.**
*what could not be verified and why*, proposed repo-map/kb text, and **what it 5. **The commit trailers and PR footer**, spelled out. They carry this
learned that the ticket did not predict** — consistently the most valuable session's URL, which is why `agent-rules.md` cannot hold them.
part, and omitted unless asked for explicitly. 6. **An explicit ask for what the ticket did not predict.** `agent-rules.md`
lists it in the hand-back. Say it in the brief anyway: it is consistently
the most valuable part of a hand-back, and agents omit it unless asked.
## Method to require, because this repo's defect is a test that passes When a hand-back teaches a new rule, add it to `agent-rules.md` in the same
wave, not to the next brief.
- Failing test first, then the fix. ## Method to require
- 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 `agent-rules.md`, "Discipline per ticket" and "Gates": failing test first, a
require a positive signal the build ran. rival injected and watched failing by name, `--no-fail-fast`, and test *names*
- `cargo test --workspace` is **fail-fast**; `--no-fail-fast` for any claim about diffed after any merge. Hold the hand-back to it. This repo's characteristic
blast radius. defect is a test that passes.
- 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 ## Shepherding
@@ -72,7 +80,9 @@ it must cross. Two agents adding a workspace member will still collide in
commit reads as a pass. commit reads as a pass.
- Merge on GitHub, then `git fetch gh && git merge --ff-only gh/main`. - 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 - Anything posted to GitHub goes out under the owner's account: open every
comment with a line saying a Codex agent wrote it. comment with a line naming the model and saying an agent wrote it. That
wording is agent-neutral because this one file serves Claude and Codex
(`.claude/skills/wave` is a symlink to `.agents/skills/wave`).
## Between waves ## Between waves
@@ -5,9 +5,10 @@ points here and adds only what is specific to its wave: the expected base
commit, the agent's ground, its `BEADS_ACTOR`, and its tickets. Every rule below commit, the agent's ground, its `BEADS_ACTOR`, and its tickets. Every rule below
was bought by a failure. was bought by a failure.
This file holds each rule **once**. Where `AGENTS.md` ("The Wave Workflow") This file holds each agent-side rule **once**. The brief checklist is
already carries the history and the argument, the rule here is one line and `SKILL.md` beside it. Where `AGENTS.md` ("The Wave Workflow") carries a longer
points there. Change a rule here, not in a brief, and not in a second copy. history, the rule here points there. Change a rule here, not in a brief, and
not in a second copy.
## Base, branch, push ## Base, branch, push
@@ -24,6 +25,12 @@ points there. Change a rule here, not in a brief, and not in a second copy.
4. **Before pushing a fix to an existing PR, check `gh pr view <n> --json 4. **Before pushing a fix to an existing PR, check `gh pr view <n> --json
state`.** GitHub deletes a merged PR's branch. A push to it then prints state`.** GitHub deletes a merged PR's branch. A push to it then prints
`* [new branch]` and lands nowhere. Why: `AGENTS.md`, "Shipping a wave". `* [new branch]` and lands nowhere. Why: `AGENTS.md`, "Shipping a wave".
5. **The "Session Completion" block near the end of `AGENTS.md` does not apply
to you.** It says `git merge origin/main` and then a bare `git push`. On a
wave branch the first is the #145 trap and the second is refused (rule 3).
Rules 1–4 replace it. The block sits between bd's generated
`BEGIN/END BEADS INTEGRATION` markers, so it is not edited by hand
(improvise-x1uk).
## Commit early: a worktree is not durable storage ## Commit early: a worktree is not durable storage
@@ -44,8 +51,18 @@ direnv exec . ./scripts/checkers.sh
direnv exec . ./scripts/node-test.sh 'web/tests/*.test.js' direnv exec . ./scripts/node-test.sh 'web/tests/*.test.js'
``` ```
- **Run everything under `direnv exec .`, and never pipe a gate.** Why, and - **Never `cargo fmt --all`.** It reaches only **one** of this repo's two
why not `cargo fmt --all`: `AGENTS.md`, brief item 3. workspace roots, because `harness/opfs-contract` declares its own
`[workspace]`. The obvious repair is worse: `cargo fmt -p opfs-contract` from
the repo root exits **1**, *"not a member of the workspace"*, which looks
exactly like a real formatting failure (improvise-b4ks). Both spellings fail
in the direction that looks like success.
- **Run every gate under `direnv exec .`.** `./scripts/checkers.sh` scrubs the
environment to an allowlist, so a bare run drops `tpl` off `PATH` and reports
spurious failures.
- **Never pipe a gate.** `$?` must be the gate's own. And
`check-comment-runs.py --check-evidence` prints two lines with the comment
cap **first**, so `| tail -1` silently drops half of it.
- **`--no-fail-fast`**, because plain `cargo test --workspace` stops at the - **`--no-fail-fast`**, because plain `cargo test --workspace` stops at the
first failing binary. Use it for any claim about blast radius. first failing binary. Use it for any claim about blast radius.
- If you touch a browser-bundle crate (core, formula, io, protocol, command, - If you touch a browser-bundle crate (core, formula, io, protocol, command,
@@ -69,8 +86,12 @@ direnv exec . ./scripts/node-test.sh 'web/tests/*.test.js'
- **Claim first:** `export BEADS_ACTOR=<your actor>` in every shell, then - **Claim first:** `export BEADS_ACTOR=<your actor>` in every shell, then
`bd update <id> --claim`. Exit 13 means another actor holds it. Pick another; `bd update <id> --claim`. Exit 13 means another actor holds it. Pick another;
do not retry. `bd` in a worktree needs `.envrc` allowed: `AGENTS.md`, the do not retry.
paragraph after the brief item list. - **`bd` needs `.envrc` allowed in your worktree.** Without it you get a
**version mismatch**, not a clean failure. Run `direnv allow` in the worktree,
or rely on the `[whitelist] prefix` in `~/.config/direnv/direnv.toml`.
`direnv status` reports `Found RC allowed 0`, and `0` means **allowed** (the
enum is 0=Allowed, 1=NotAllowed). It reads like the opposite.
- **Reproduce at HEAD before fixing.** A dozen tickets were stale-open in one - **Reproduce at HEAD before fixing.** A dozen tickets were stale-open in one
campaign, one fixed two days before it was filed. If it does not reproduce, campaign, one fixed two days before it was filed. If it does not reproduce,
find the fixing commit, confirm with `git merge-base --is-ancestor <sha> HEAD`, find the fixing commit, confirm with `git merge-base --is-ancestor <sha> HEAD`,
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/wave
-85
View File
@@ -1,85 +0,0 @@
---
name: wave
description: 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
**Point the agent at [`agent-rules.md`](agent-rules.md) and tell it to read it in
full first.** That file is the agent's standing rules: base and push mechanics,
the gate list, the claim, the vacuity shapes, evidence, the files it must not
write, GitHub conventions, the hand-back and disk cleanup. Do not restate them
in the brief. A restated rule drifts from its source, and the brief is the copy
nobody corrects. The brief adds only what varies per wave:
1. **The expected base commit, by name.** Worktrees do not reliably branch from
HEAD. One was created three merges behind and was only noticed because it
was told to check.
2. **A reference baseline**, so "N passing" is checkable: Rust test count,
harness counts, node counts, and the machine. The agent still measures its
own.
3. **Its ground, and the ground that is not.** Name any orchestrator-only file
it is exceptionally allowed to edit.
4. **Its `BEADS_ACTOR` and its tickets.**
5. **The commit trailers and PR footer**, spelled out. They carry this
session's URL, which is why `agent-rules.md` cannot hold them.
When a hand-back teaches a new rule, add it to `agent-rules.md` in the same
wave, not to the next brief.
## Method to require
`agent-rules.md`, "Discipline per ticket" and "Gates": failing test first, a
rival injected and watched failing by name, `--no-fail-fast`, and test *names*
diffed after any merge. Hold the hand-back to it. This repo's characteristic
defect is a test that passes.
## 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.
+11 -52
View File
@@ -100,60 +100,19 @@ Two agents each adding a workspace member will still collide in
### Every wave agent's brief must include ### Every wave agent's brief must include
The rules an agent follows itself are The wave skill holds both halves, each in one place (improvise-1f8e):
**`.claude/skills/wave/agent-rules.md`**, and a brief points there rather than
restating them. That covers base and push mechanics, the gate list, the claim,
the vacuity shapes, evidence, forbidden files, GitHub conventions, the
hand-back and disk cleanup. The list below is what this section argues for. A
new rule an agent must follow goes into that file. It does not go here, and it
does not go into the next brief.
1. **Verify your base commit first.** Worktrees do **not** reliably branch from - **The brief checklist** — what an orchestrator must put in a brief — is
the orchestrator's HEAD — one was created three merges behind and only `.agents/skills/wave/SKILL.md`, "What every brief must carry".
noticed because it was told to look. `git log --oneline -1` against a named - **The agent's standing rules** — base and push, gates, claim, vacuity shapes,
commit, reset onto `main` if behind. Name the expected commit in the brief. evidence, forbidden files, GitHub, hand-back, disk, commit early — are
2. **The baseline test count**, so "1090 passing" is checkable rather than `.agents/skills/wave/agent-rules.md`. A brief points there rather than
asserted, and a drop is visible. Tell the agent to **measure its own** and restating them.
cite the machine: the same commit measured `improvise-io`'s binary at 1.4s on
one machine and 3.7–5.2s on another, so a figure with no machine is a claim
about the suite, which it is not.
3. **The gate list, and it must not say `cargo fmt --all`.** That reaches only
**one** of this repo's two workspace roots — `harness/opfs-contract` declares
its own `[workspace]` — and the obvious repair is worse: `cargo fmt
-p opfs-contract` from the repo root exits **1**, *"not a member of the
workspace"*, which is indistinguishable from a real formatting failure
(improvise-b4ks). Both spellings fail in the direction that looks like
success. The gate is:
``` `.claude/skills/wave` is a symlink to `.agents/skills/wave`, so Claude and Codex
direnv exec . ./scripts/fmt-workspaces.sh # exit 2 = the gate did not run read the same files. A new rule goes into one of those two files, not here and
``` not into the next brief: a restated rule is the copy nobody corrects, and this
list was once a third copy that drifted from the skill's.
Every gate goes through `direnv exec .` — `./scripts/checkers.sh` scrubs the
environment to an allowlist, so a bare invocation drops `tpl` off `PATH` and
reports spurious failures. And **never pipe a gate**: `$?` must be the gate's
own, and `check-comment-runs.py --check-evidence` prints two lines with the
comment cap **first**, so `| tail -1` silently drops half of it.
4. **Commit frequently, so the work is safe in git.** A WIP commit after the
first green test, and again after every meaningful step. A worktree is not
durable storage: an agent finished a whole ticket with every gate green and
lost all of it, uncommitted, when its worktree was deleted during a disk
cleanup. Tidying the history before the PR is fine; losing it is not.
5. **Do not merge to `main`.** The orchestrator merges. Pushing the agent's own
branch (`git push gh HEAD:refs/heads/<branch>`) is encouraged once it is
worth keeping.
6. The commit-message trailers, spelled out.
7. **What to report**: branch and commits, what changed per ticket, *what could
not be verified and why*, and proposed repo-map/`kb.pl` text. Ask explicitly
for the thing the agent learned that the ticket did not predict — that is
consistently the most valuable part of a hand-back, and agents omit it unless
asked.
`bd` works in any worktree, but `.envrc` must be allowed there or you get a
**version mismatch** rather than a clean failure — `direnv allow` in the
worktree, or rely on the `[whitelist] prefix` in `~/.config/direnv/direnv.toml`.
Note `direnv status` reports `Found RC allowed 0`, and `0` means **allowed**
(the enum is 0=Allowed, 1=NotAllowed). It reads like the opposite.
### Between waves ### Between waves