docs(runner-probes): enumerate the real carriers, spare the consumer checkouts, and make the snippet run (#202)

@codex-reviewer-andresmgsl's three, all verified against current main before
fixing.

1. THERE ARE THREE SELF-REF CARRIERS, NOT TWO — labels-sweep.yml:52,
   labels.yml:51, release.yml:132. My `[ "$n" -eq 2 ]` came from the
   pre-upstream tree, so it would have REJECTED a correctly armed candidate and
   told the operator to rewrite two of three, leaving one workflow pinned to
   the tag. The gate enumerates from the tree now, with the derivation commands
   beside the table so the list is re-checked rather than trusted.

2. NOT EVERY `repository:` BELONGS TO THE FORK. Three are
   `${{ github.repository }}` — labels-sweep.yml:69, labels.yml:92,
   release-exercise.yml:72 — and they fetch the CALLER's repository. My loop
   required every one to equal the fork, which would have rewritten the
   consumer checkouts and quietly changed what the probe exercises. Internal
   self-checkouts (four) are asserted to be the fork; consumer checkouts are
   asserted to stay dynamic.

3. EACH CHECK IS BOUND TO THE TREE IT IS ABOUT — `git -C "$armed"` for the
   carriers, `git -C "$probe"` for the callers, instead of depending on the
   operator's current directory. And `mapfile` rather than `git grep | while …
   fail`: the loop ran in a pipeline subshell, so `fail` exited the subshell
   and the gate carried on. Collect first, validate after, under a declared
   `set -euo pipefail`.

And the snippet is now executable rather than illustrative: placeholders became
positional parameters, so it parses, is shellcheck-clean, and runs. Driven
against the unarmed tree it refuses with `CEREMONY_SELF_REF=0.6.0` — a tag
rather than the candidate SHA, which is exactly the case it exists to catch.
Publishing a gate that could not run would have been the same defect one level
up.

Branch updated from merged main (e236318). test/run.sh 28/28; shellcheck 0.10.0
and changelog-armed clean.

Refs #202
This commit is contained in:
cluade-reviewer-andresmgsl 2026-08-05 14:13:40 +00:00
parent 8c3c37d412
commit dc87051c69
2 changed files with 72 additions and 35 deletions

View file

@ -33,8 +33,12 @@
equal the candidate code SHA, and callers match the layer they belong to
(#202).
- It counts the `CEREMONY_SELF_REF` carriers as well as comparing them, which
is what catches one that vanished rather than being rewritten (#202).
- It enumerates the carriers from the tree rather than encoding a count, and
distinguishes ceremony's internal self-checkouts from the consumer checkouts
that must stay `${{ github.repository }}` (#202).
- The published snippet parses, lints clean and runs: driven against an unarmed
tree it refuses, naming the carrier (#202).
- Probe results are written to an issue in the probe repo and carried to the
ceremony issue by a human, so the probe holds no path that can write to the

View file

@ -124,43 +124,76 @@ So:
- reusable-workflow callers →
`<identity>/ceremony/.github/workflows/<file>@<armed-workflow-sha>`, since
that is the only revision whose inner checkout is rewritten.
4. **Gate the arming POSITIVELY, against the armed tree.** Absence of the old
literal is not proof of correct arming: a negative grep stays green if
`CEREMONY_SELF_REF` names a tag, the *armed* SHA or some other commit; if a
carrier was rewritten to the wrong fork; if an executable carrier lives
outside `.github`; or if a carrier simply disappeared
(@codex-reviewer-andresmgsl, #202 review). So assert what each carrier
**is**, not merely what it is not:
4. **Gate the arming POSITIVELY, and against the tree each check is about.**
Absence of the old literal proves nothing: a negative grep stays green if
`CEREMONY_SELF_REF` names a tag or the *armed* SHA, if a carrier went to the
wrong fork, or if a carrier vanished instead of being rewritten. So assert
what each carrier **is** — and enumerate them, because the set is a property
of the current tree rather than a number to remember
(@codex-reviewer-andresmgsl, #202 review).
**The carriers on `main` today.** Re-derive this list each time rather than
trusting it; workflows come and go:
```sh
fork=<identity>/ceremony ; code_sha=<candidate-code-sha> ; armed_sha=<armed-workflow-sha>
fail() { echo "arming incomplete: $*" >&2; exit 1; }
# every self-checkout points at the fork
for v in $(git grep -hoP '(?<=repository:\s)\S+' -- .github); do
[ "$v" = "$fork" ] || fail "repository: $v"
done
# both CEREMONY_SELF_REF carriers name the CANDIDATE CODE sha
n=0
for v in $(git grep -hoP '(?<=CEREMONY_SELF_REF:\s)"?\K[^"]+' -- .github); do
[ "$v" = "$code_sha" ] || fail "CEREMONY_SELF_REF: $v"; n=$((n+1))
done
[ "$n" -eq 2 ] || fail "expected 2 CEREMONY_SELF_REF carriers, found $n"
# and in the PROBE repo: workflows pin the armed sha, actions the code sha
git grep -hoP '(?<=uses:\s)\S+' -- .github | while read -r u; do
case "$u" in
*/.github/workflows/*) [ "${u##*@}" = "$armed_sha" ] || fail "workflow caller $u" ;;
*/actions/*) [ "${u##*@}" = "$code_sha" ] || fail "action caller $u" ;;
esac
done
# the negative check stays, as a cheap extra rather than as the proof
! git grep -qE '(uses:|repository:)[[:space:]]*heavy-duty/ceremony' -- .github \
|| fail "canonical coordinate still present"
git grep -n 'CEREMONY_SELF_REF:' -- .github/workflows
git grep -n 'repository:' -- .github/workflows
```
**A count, not just a comparison**`n -eq 2` is what catches a carrier
that vanished rather than being rewritten, which a per-value loop alone
cannot see.
| carrier | on current `main` | armed value |
|---|---|---|
| `CEREMONY_SELF_REF` | `labels-sweep.yml`, `labels.yml`, `release.yml` | the **candidate code** SHA |
| internal self-checkout | `labels-sweep.yml`, `labels.yml`, `release.yml` ×2 | `<identity>/ceremony` |
| **consumer** checkout | `labels-sweep.yml`, `labels.yml`, `release-exercise.yml` | **unchanged**`${{ github.repository }}` |
That third row is the one an over-broad rewrite destroys. Those checkouts
fetch the **caller's** repository; pointing them at the fork changes what
the probe exercises into something else entirely.
```sh
#!/usr/bin/env bash
# check-arming <armed-checkout> <probe-checkout> <fork> <code-sha> <armed-sha>
set -euo pipefail
armed="$1"; probe="$2"; fork="$3"; code_sha="$4"; armed_sha="$5"
fail() { echo "arming incomplete: $*" >&2; exit 1; }
# (a) every self-ref carrier names the CANDIDATE CODE sha — enumerated, so a
# carrier that vanished is a failure rather than one fewer loop pass.
mapfile -t refs < <(git -C "$armed" grep -hoP '(?<=CEREMONY_SELF_REF: ")[^"]+' \
-- .github/workflows)
[ "${#refs[@]}" -gt 0 ] || fail "no CEREMONY_SELF_REF carriers found"
for v in "${refs[@]}"; do [ "$v" = "$code_sha" ] || fail "CEREMONY_SELF_REF=$v"; done
# (b) internal checkouts point at the fork; consumer checkouts stay dynamic.
mapfile -t repos < <(git -C "$armed" grep -hoP '(?<=repository: ).*' \
-- .github/workflows)
for v in "${repos[@]}"; do
case "$v" in
'${{ github.repository }}') ;; # the caller's repo — must NOT change
"$fork") ;; # an internal self-checkout, armed
*) fail "repository: $v" ;;
esac
done
# (c) in the PROBE repo: workflow callers pin the armed sha, action callers
# the code sha.
mapfile -t uses < <(git -C "$probe" grep -hoP '(?<=uses: ).*' -- .github)
for u in "${uses[@]}"; do
case "$u" in
*/.github/workflows/*@*) [ "${u##*@}" = "$armed_sha" ] || fail "workflow caller $u" ;;
*/actions/*@*) [ "${u##*@}" = "$code_sha" ] || fail "action caller $u" ;;
esac
done
# (d) the negative check, as a cheap extra rather than as the proof.
! git -C "$armed" grep -qE '(uses:|repository:)[[:space:]]*heavy-duty/ceremony' \
-- .github || fail "canonical coordinate still present"
```
**`mapfile`, not a pipeline.** `git grep | while … fail` runs the loop in a
subshell, so `fail` exits *that* subshell and the gate continues. Collect
first, validate after — and run under `set -euo pipefail`, which the snippet
declares rather than assumes.
5. **Invoke the probe by the event it is about**, and record which: a
`workflow_dispatch`, or the real board event under test. A probe that fires