drills/README.md — the standing runner-probe venue, and why the drill disposal rule does not apply to it (#202) #207

Merged
andres merged 14 commits from build/202-runner-probe-venue into main 2026-08-05 17:18:59 +00:00
2 changed files with 44 additions and 22 deletions
Showing only changes of commit 20f4b287f7 - Show all commits

View file

@ -29,7 +29,7 @@
checkout points at the fork (#202).
- The arming gate asserts what each carrier IS, not only that the old literal
is gone: every `repository:` equals the fork, both `CEREMONY_SELF_REF` values
is gone: every `repository:` equals the fork, every `CEREMONY_SELF_REF` value
equal the candidate code SHA, and callers match the layer they belong to
(#202).
@ -38,8 +38,15 @@
that must stay `${{ github.repository }}` (#202).
- Both published snippets parse, lint clean and were driven against a
constructed armed/probe pair: all six failure classes refuse and the armed
control passes (#202).
constructed armed/probe pair: deletion, both role swaps, wrong owner, wrong
SHA, wrong path, a deleted caller class and an extra carrier all refuse, and
the armed control passes (#202).
- The manifest records complete caller coordinates, so a path swapped under the
right owner and SHA is caught (#202).
- Generator and checker share one domain — ceremony callers — so a third-party
`actions/checkout` is neither manifested nor reported as unrecognised (#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

@ -153,20 +153,33 @@ So:
```sh
#!/usr/bin/env bash
# write-manifest <armed-checkout> <probe-checkout> <fork> <code-sha> <armed-sha>
#
# `|| true` on every extraction, for the same reason the checker needs it:
# git grep exits 1 on no-match and `set -e` would abort BEFORE the manifest
# is written — silently, which is how the first version of this generator
# produced no file and no diagnostic when a probe exercised only one layer
# (@codex-reviewer-andresmgsl). A probe need not use both.
set -euo pipefail
armed="$1"; probe="$2"; fork="$3"; code_sha="$4"; armed_sha="$5"
{
git -C "$armed" grep -n 'CEREMONY_SELF_REF:' -- .github/workflows \
| cut -d: -f1,2 | sed "s|\$|\tself_ref\t$code_sha|"
| cut -d: -f1,2 | sed "s|$|\tself_ref\t$code_sha|" || true
git -C "$armed" grep -n 'repository: heavy-duty/ceremony' -- .github/workflows \
| cut -d: -f1,2 | sed "s|\$|\tinternal_repo\t$fork|"
| cut -d: -f1,2 | sed "s|$|\tinternal_repo\t$fork|" || true
git -C "$armed" grep -n 'repository: ${{ github.repository }}' -- .github/workflows \
| cut -d: -f1,2 | sed 's|$|\tconsumer_repo\t${{ github.repository }}|'
git -C "$probe" grep -n 'uses: .*/.github/workflows/' -- .github \
| cut -d: -f1,2 | sed "s|\$|\tworkflow_caller\t$armed_sha|"
git -C "$probe" grep -n 'uses: .*/actions/' -- .github \
| cut -d: -f1,2 | sed "s|\$|\taction_caller\t$code_sha|"
| cut -d: -f1,2 | sed 's|$|\tconsumer_repo\t${{ github.repository }}|' || true
# Callers record the COMPLETE expected coordinate, not just the sha: the
# path is as rewritable as the owner, and a manifest that stores only the
# suffix cannot notice `…/actions/wrong-one@<right-sha>`.
git -C "$probe" grep -nE 'uses:[[:space:]]*[^[:space:]]*/ceremony/\.github/workflows/' -- .github \
| sed -E "s|^([^:]+):([0-9]+):.*/ceremony/(\.github/workflows/[^@[:space:]]+)@.*|\\1:\\2\\tworkflow_caller\\t$fork/\\3@$armed_sha|" || true
git -C "$probe" grep -nE 'uses:[[:space:]]*[^[:space:]]*/ceremony/actions/' -- .github \
| sed -E "s|^([^:]+):([0-9]+):.*/ceremony/(actions/[^@[:space:]]+)@.*|\\1:\\2\\taction_caller\\t$fork/\\3@$code_sha|" || true
} | sort >manifest.tsv
# Zero ceremony callers is a refusal by name; one layer only is fine.
callers="$(grep -cE '(workflow|action)_caller' manifest.tsv || true)"
[ "$callers" -gt 0 ] || { echo "manifest: no ceremony callers found in $probe" >&2; exit 1; }
```
Run it against the **pre-arming** tree — that is what enumerates the
@ -184,13 +197,21 @@ So:
# carrier class that vanished ENTIRELY produced silence instead of a
# refusal. Silence is the worst of the three outcomes; the comparison below
# is what must report it.
[ "$(grep -cE '(workflow|action)_caller' "$manifest" || true)" -gt 0 ] \
|| fail "manifest names no ceremony callers — it cannot prove an arming"
actual="$(mktemp)"
{
git -C "$armed" grep -nP '(?<=CEREMONY_SELF_REF: ")[^"]+' -- .github/workflows \
| sed -E 's/^([^:]+):([0-9]+):.*CEREMONY_SELF_REF: "([^"]*)".*/\1:\2\tself_ref\t\3/' || true
git -C "$armed" grep -nE 'repository: .+' -- .github/workflows \
| sed -E 's|^([^:]+):([0-9]+):[[:space:]]*repository:[[:space:]]*(.*)$|\1:\2\t__repo__\t\3|' || true
git -C "$probe" grep -nE 'uses: .+' -- .github \
# Only CEREMONY callers, matching the generator's domain exactly — a
# third-party `actions/checkout` is not this gate's business, and
# extracting it here while the generator ignores it made every probe fail
# as an "unrecognised carrier" (@codex-reviewer-andresmgsl). A wrong OWNER
# is still caught: `wrong-owner/ceremony/...` matches this pattern.
git -C "$probe" grep -nE 'uses:[[:space:]]*[^[:space:]]*/ceremony/' -- .github \
| sed -E 's|^([^:]+):([0-9]+):[[:space:]]*-?[[:space:]]*uses:[[:space:]]*(.*)$|\1:\2\t__uses__\t\3|' || true
} | sort >"$actual"
@ -205,17 +226,11 @@ So:
have="$(awk -F'\t' -v l="$loc" '$1==l && $2=="__uses__"{print $3}' "$actual")" ;;
esac
[ -n "$have" ] || fail "carrier vanished: $loc ($kind)"
case "$kind" in
self_ref|internal_repo|consumer_repo)
[ "$have" = "$want" ] || fail "$loc ($kind): expected '$want', found '$have'" ;;
workflow_caller)
[ "$have" = "$fork/.github/workflows/${have##*/.github/workflows/}" ] \
|| fail "$loc: caller owner is not $fork — '$have'"
[ "${have##*@}" = "$armed_sha" ] || fail "$loc: workflow caller not armed sha" ;;
action_caller)
case "$have" in "$fork/actions/"*) ;; *) fail "$loc: caller owner is not $fork — '$have'" ;; esac
[ "${have##*@}" = "$code_sha" ] || fail "$loc: action caller not code sha" ;;
esac
# ONE comparison for every kind: the manifest already carries the complete
# expected value, so owner, path AND sha are checked at once. Checking the
# owner and the sha separately let `…/actions/wrong-one@<right-sha>`
# through (@codex-reviewer-andresmgsl).
[ "$have" = "$want" ] || fail "$loc ($kind): expected '$want', found '$have'"
done <"$manifest"
# and nothing UNRECOGNISED: every uses:/repository: in the trees must appear