docs: spell out crew's four-edit migration; fold in crew#250 field facts (#209)

crew#250 verified two facts the design prose now carries: a queue-
displaced run is not independently rerunnable (gh run rerun / --failed /
--job all refuse), so a victim PR had no manual escape hatch; and the
displacing burst is deterministic — one review_requested event per
panelist per request — so displacement is the steady state of a working
fleet, scaling with panel size, not a traffic spike.

CONSUMERS.md now walks the adoption as one atomic four-edit PR with crew
as the worked example: the pin bump in every ceremony uses: reference,
the new labels-sweep.yml caller, the cron RELOCATED (bold warning: a
copied-not-moved schedule double-fires sweeps into the one shared group
and reads as the bug getting worse after the fix), and actions: write
replacing the labels caller's actions: read.

Refs #209

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
dan-claude-bot 2026-08-01 17:25:26 +00:00
parent 45aa806207
commit be660358f2
3 changed files with 46 additions and 9 deletions

View file

@ -12,8 +12,13 @@ name: labels-sweep
# one-running-plus-one-pending queue records every extra run as CANCELLED. # one-running-plus-one-pending queue records every extra run as CANCELLED.
# That displacement is semantically lossless — the surviving sweep does the # That displacement is semantically lossless — the surviving sweep does the
# displaced run's work — but while the sweep rode pull_request_target runs # displaced run's work — but while the sweep rode pull_request_target runs
# the ❌ landed on that PR's checks and read as red CI. Here a displaced # the ❌ landed on that PR's checks and read as red CI, with no manual
# run attaches to no PR: the cancellations live on the Actions tab only. # escape hatch: GitHub refuses to rerun a queue-displaced run (crew#250).
# And displacement is the steady state of a working fleet, not a spike —
# one panel request emits one review_requested event per reviewer, so
# every review round over-fills the one-running-plus-one-pending queue.
# Here a displaced run attaches to no PR: the cancellations live on the
# Actions tab only.
# #
# Bootstrap semantics: a manual dispatch of the caller bootstraps the # Bootstrap semantics: a manual dispatch of the caller bootstraps the
# taxonomy (its `bootstrap` input defaults to "yes"), exactly what # taxonomy (its `bootstrap` input defaults to "yes"), exactly what

View file

@ -12,7 +12,12 @@ name: labels
# The reconcile sweep lived here until #209. Riding the PR-triggered run # The reconcile sweep lived here until #209. Riding the PR-triggered run
# meant every displacement in the sweep's shared concurrency queue recorded # meant every displacement in the sweep's shared concurrency queue recorded
# a CANCELLED `reconcile` check on some PR — read as red CI by every human # a CANCELLED `reconcile` check on some PR — read as red CI by every human
# and agent, though the surviving sweep does the displaced run's work. The # and agent, though the surviving sweep does the displaced run's work. Two
# field facts made that untenable (crew#250): a displaced run cannot be
# rerun — `gh run rerun`, `--failed`, and `--job` all refuse — so a victim
# PR has no manual escape hatch; and the displacing burst is deterministic,
# one `review_requested` event per panelist per request, so every review
# round displaces runs and the rate scales with panel size. The
# sweep now lives in labels-sweep.yml behind its own caller, and the # sweep now lives in labels-sweep.yml behind its own caller, and the
# trigger job below is its wake: it fires on every event this caller # trigger job below is its wake: it fires on every event this caller
# subscribes — the exact surface that used to run reconcile directly — so # subscribes — the exact surface that used to run reconcile directly — so

View file

@ -289,7 +289,9 @@ together at the same pin:
one shared concurrency group, and GitHub records every queue-displaced one shared concurrency group, and GitHub records every queue-displaced
run as CANCELLED — harmless (the surviving sweep does its work) until it run as CANCELLED — harmless (the surviving sweep does its work) until it
rode a `pull_request_target` run and the ❌ landed on that PR's checks rode a `pull_request_target` run and the ❌ landed on that PR's checks
as fake red CI. Behind its own caller, a displaced sweep cancels on the as fake red CI that GitHub refuses to rerun (crew#250: `gh run rerun`
and its `--failed`/`--job` forms all decline a queue-displaced run).
Behind its own caller, a displaced sweep cancels on the
Actions tab, attached to no PR; PR checks show `scope` and the green Actions tab, attached to no PR; PR checks show `scope` and the green
`trigger` only. `trigger` only.
@ -437,11 +439,36 @@ the stub and ceremony's own caller stay byte-for-byte identical, the parity
The two-caller split (ceremony#209) is **unreleased**. A consumer pinned to The two-caller split (ceremony#209) is **unreleased**. A consumer pinned to
`0.4.0` or earlier keeps the previous single-caller shape — the labels `0.4.0` or earlier keeps the previous single-caller shape — the labels
caller carrying the cron, `workflow_dispatch`, and `actions: read` — and caller carrying the cron, `workflow_dispatch`, and `actions: read` — and
adopts the split at the pin bump to the first tag carrying ceremony#209, adopts the split at the pin bump to the first tag carrying ceremony#209.
as one atomic PR: the pin, the reshaped labels caller (cron and dispatch Never mix refs to adopt it early.
removed, `actions: write` added), and the new sweep caller file. Never mix
refs to adopt it early, and never bump without the sweep caller — that is The migration is **one atomic PR** with exactly four edits — crew, the
the red-trigger failure mode above. consumer whose displaced-check evidence drove #209 (crew#227, crew#250),
is the worked example; written here against `0.4.1` as the illustrative
first tag carrying the split:
1. **Pin bump, every reference together** ([Version pinning](#version-pinning)):
`0.4.0``0.4.1` in the labels caller's `uses:` line **and in every
other ceremony `uses:` in the repo** — crew also pins in
`release.yml` and its `ci.yml` guard steps. A repo on the doctrine
mirror re-runs `docs-sync --fix` in the same PR.
2. **New file `.github/workflows/labels-sweep.yml`** — the sweep caller
stub above, verbatim, `bootstrap` input included (the trigger's
`-f bootstrap=no` dispatch is refused if the input is undeclared).
3. **The hourly cron RELOCATES — it is moved, never copied.** Delete the
`schedule:` block (and the bare `workflow_dispatch:`) from the labels
caller in the same edit that adds the sweep caller.
**Warning**: a consumer that copies the sweep caller and leaves the
old schedule on the labels caller gets DOUBLE sweeps — every cron tick
fires both callers into the one shared `labels-reconcile` group — so
displacement goes **up**, and the fix reads as the bug getting worse.
4. **`actions: write` on the labels caller** — consumers carry
`actions: read` today (crew does); the trigger job's `gh workflow run`
is a write. The sweep caller keeps `actions: read`.
Bump without the sweep caller and the trigger job goes red on every PR
and issue event — the loud failure mode above — so never split these
four edits across PRs.
`pull_request_target` is intentional: fork PRs need the base repository's `pull_request_target` is intentional: fork PRs need the base repository's
token to write labels. The reusable workflows execute no PR code. They check token to write labels. The reusable workflows execute no PR code. They check