fix: the CONSUMERS.md stub's issues: types match the caller's, parity-tested
The stub published [opened, labeled, unlabeled, assigned, unassigned,
closed] while ceremony's own caller listens on eight types — PR #32's
70db91f widened the caller by edited and reopened and the stub never
followed. Both are load-bearing: an edited body rewrites the Blocked-by
declaration the reconcile sweep parses, and a reopened issue re-enters
the queue wearing labels derived at close (#144).
The stub's list is now byte-identical to the caller's, a parity row in
test/labels.test.sh keeps it that way (red on a dropped type, a drift,
or a reorder in one file only), and one adoption note names the tag the
widened list rides in on. The caller does not narrow.
Closes #144
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6a16a30bab
commit
0b77d4b860
3 changed files with 59 additions and 21 deletions
10
changelog.d/144.md
Normal file
10
changelog.d/144.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The `docs/CONSUMERS.md` labels-caller stub lists the same `issues:` types
|
||||||
|
as ceremony's own caller — `edited` and `reopened` included — so a consumer
|
||||||
|
adopting the stub wakes when an issue body's `Blocked by #N` declaration is
|
||||||
|
edited, and when a closed issue re-enters the queue wearing labels derived
|
||||||
|
at close. The two lists drifted apart inside PR #32; a parity test now pins
|
||||||
|
them together, red if either file drops a type or the lists diverge.
|
||||||
|
Adopting the widened list is a stub edit riding the pin bump to the first
|
||||||
|
tag carrying this change (#144).
|
||||||
|
|
@ -306,7 +306,10 @@ on:
|
||||||
# Available at 0.2.0 and later (the first tag carrying ceremony#32); a
|
# Available at 0.2.0 and later (the first tag carrying ceremony#32); a
|
||||||
# consumer pinned to 0.1.0 omits this block.
|
# consumer pinned to 0.1.0 omits this block.
|
||||||
issues:
|
issues:
|
||||||
types: [opened, labeled, unlabeled, assigned, unassigned, closed]
|
# edited/reopened are unreleased — not in 0.2.0; add them with the pin
|
||||||
|
# bump to the first tag carrying ceremony#144, the same bump as the
|
||||||
|
# pull_request_target additions above.
|
||||||
|
types: [opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
checks: read # mergeability/check-rollup read for PR state
|
checks: read # mergeability/check-rollup read for PR state
|
||||||
|
|
@ -326,18 +329,23 @@ needs both explicit reads above; without them the failure appears as an empty
|
||||||
The `issues:` trigger is available at `0.2.0` and later — `0.2.0` is the
|
The `issues:` trigger is available at `0.2.0` and later — `0.2.0` is the
|
||||||
first tag carrying ceremony#32. A consumer pinned to `0.1.0` omits it. Adopt
|
first tag carrying ceremony#32. A consumer pinned to `0.1.0` omits it. Adopt
|
||||||
it only by bumping every ceremony reference to `0.2.0` or later; never mix
|
it only by bumping every ceremony reference to `0.2.0` or later; never mix
|
||||||
refs to adopt it early.
|
refs to adopt it early. At `0.2.0` the type list stops at `closed`: `edited`
|
||||||
|
and `reopened` are newer, part of the pending stub edit below.
|
||||||
|
|
||||||
`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 workflow executes no PR code. It checks
|
token to write labels. The reusable workflow executes no PR code. It checks
|
||||||
out only the consumer's base branch and the pinned ceremony implementation.
|
out only the consumer's base branch and the pinned ceremony implementation.
|
||||||
The #52 ruling invariants ride exactly these triggers — but the caller above
|
The #52 ruling invariants ride exactly these triggers — but the caller above
|
||||||
is no longer the #18 shape, so adopting current triggers is a stub edit, not
|
is no longer the #18 shape, so adopting current triggers is a stub edit, not
|
||||||
a bare pin bump. The pending edit is `review_requested` and
|
a bare pin bump. The pending edits are `review_requested` and
|
||||||
`review_request_removed` (#137): the wake that clears `blocker:unrequested`
|
`review_request_removed` on `pull_request_target:` (#137) — the wake that
|
||||||
the moment the panel is asked, without which a quiet repo wears that flag
|
clears `blocker:unrequested` the moment the panel is asked, without which a
|
||||||
until the advisory cron. Make that edit with the pin bump to the first tag
|
quiet repo wears that flag until the advisory cron — and `edited` and
|
||||||
carrying ceremony#137 — never before it and never through mixed refs.
|
`reopened` on `issues:` (#144): the wakes for a body edit rewriting the
|
||||||
|
`Blocked by #N` declaration the reconcile sweep parses, and for a closed
|
||||||
|
issue re-entering the queue wearing labels derived when it closed. Make both
|
||||||
|
edits together, with the pin bump to the first tag carrying ceremony#137 and
|
||||||
|
ceremony#144 — never before it and never through mixed refs.
|
||||||
|
|
||||||
`.github/labels.conf` has one mandatory panel setting, one mandatory
|
`.github/labels.conf` has one mandatory panel setting, one mandatory
|
||||||
`triage-actors` setting, and then zero or more scope rows:
|
`triage-actors` setting, and then zero or more scope rows:
|
||||||
|
|
|
||||||
|
|
@ -65,41 +65,61 @@ check "PR author is recused from the required panel" 0 "one three" printf '%s\n'
|
||||||
check "LABELS.md enumerates no repo's scope labels" 1 "0" \
|
check "LABELS.md enumerates no repo's scope labels" 1 "0" \
|
||||||
grep -c 'scope:[a-z0-9]' "$ROOT/LABELS.md"
|
grep -c 'scope:[a-z0-9]' "$ROOT/LABELS.md"
|
||||||
|
|
||||||
# The caller's pull_request_target list and the CONSUMERS.md stub's must be
|
# The caller's trigger type lists and the CONSUMERS.md stub's must be the
|
||||||
# the same list. review_requested/review_request_removed are the wake that
|
# same lists. review_requested/review_request_removed are the wake that
|
||||||
# clears blocker:unrequested — the label sat false for as long as a quiet
|
# clears blocker:unrequested — the label sat false for as long as a quiet
|
||||||
# repo stayed quiet because the one event that falsifies it was never
|
# repo stayed quiet because the one event that falsifies it was never
|
||||||
# listed (#137). The stub is prose, so nothing but this row keeps the two
|
# listed (#137). edited/reopened are the wakes for the two events that
|
||||||
# lists from drifting: a type in one file only is a wake that fires at home
|
# falsify issue labels silently — an edited body rewrites the `Blocked by
|
||||||
# and nowhere in the fleet, or the reverse.
|
# #N` declaration the reconcile sweep parses, and a reopened issue
|
||||||
pr_target_types() { # $1 = file → its pull_request_target types line, unindented
|
# re-enters the queue wearing labels derived at close; PR #32 widened the
|
||||||
awk '/pull_request_target:/{f=1; next} f && /types: /{sub(/^ */,""); print; exit}' "$1"
|
# caller by both and the stub never followed (#144). The stub is prose, so
|
||||||
|
# nothing but these rows keeps the lists from drifting: a type in one file
|
||||||
|
# only is a wake that fires at home and nowhere in the fleet, or the
|
||||||
|
# reverse. The NF guard keeps `issues: write` under permissions: from
|
||||||
|
# matching the issues: trigger key.
|
||||||
|
event_types() { # $1 = file, $2 = trigger key → that trigger's types line, unindented
|
||||||
|
awk -v key="$2:" '$1 == key && NF == 1 {f=1; next} f && /types: /{sub(/^ */,""); print; exit}' "$1"
|
||||||
}
|
}
|
||||||
types_in_sync() { # $1 = caller, $2 = stub → 0 when both lists exist and match
|
types_in_sync() { # $1 = trigger key, $2 = caller, $3 = stub → 0 when both lists exist and match
|
||||||
local a b
|
local a b
|
||||||
a="$(pr_target_types "$1")" b="$(pr_target_types "$2")"
|
a="$(event_types "$2" "$1")" b="$(event_types "$3" "$1")"
|
||||||
[ -n "$a" ] && [ "$a" = "$b" ]
|
[ -n "$a" ] && [ "$a" = "$b" ]
|
||||||
}
|
}
|
||||||
CALLER="$ROOT/.github/workflows/self-labels.yml"
|
CALLER="$ROOT/.github/workflows/self-labels.yml"
|
||||||
STUB="$ROOT/docs/CONSUMERS.md"
|
STUB="$ROOT/docs/CONSUMERS.md"
|
||||||
check "caller and stub pull_request_target lists are identical" 0 "" \
|
check "caller and stub pull_request_target lists are identical" 0 "" \
|
||||||
types_in_sync "$CALLER" "$STUB"
|
types_in_sync pull_request_target "$CALLER" "$STUB"
|
||||||
# shellcheck disable=SC2016 # expansion belongs to the nested bash
|
# shellcheck disable=SC2016 # expansion belongs to the nested bash
|
||||||
check "the caller lists both review-request wakes" 0 "" bash -c \
|
check "the caller lists both review-request wakes" 0 "" bash -c \
|
||||||
'awk "/pull_request_target:/{f=1; next} f && /types: /{print; exit}" "$1" |
|
'awk "/pull_request_target:/{f=1; next} f && /types: /{print; exit}" "$1" |
|
||||||
grep -F review_requested | grep -qF review_request_removed' _ "$CALLER"
|
grep -F review_requested | grep -qF review_request_removed' _ "$CALLER"
|
||||||
|
check "caller and stub issues lists are identical" 0 "" \
|
||||||
|
types_in_sync issues "$CALLER" "$STUB"
|
||||||
|
check "the caller still lists all eight issue types" 0 \
|
||||||
|
"types: [opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened]" \
|
||||||
|
event_types "$CALLER" issues
|
||||||
# the failing cases: drop a type from either file, or reorder one list only,
|
# the failing cases: drop a type from either file, or reorder one list only,
|
||||||
# and the identity row above goes red — exercised here on mutated copies
|
# and the identity rows above go red — exercised here on mutated copies
|
||||||
mut_caller="$TMP/mut-caller.yml" mut_stub="$TMP/mut-stub.md"
|
mut_caller="$TMP/mut-caller.yml" mut_stub="$TMP/mut-stub.md"
|
||||||
sed 's/, review_request_removed//' "$CALLER" >"$mut_caller"
|
sed 's/, review_request_removed//' "$CALLER" >"$mut_caller"
|
||||||
check "a type dropped from the caller goes red" 1 "" \
|
check "a type dropped from the caller goes red" 1 "" \
|
||||||
types_in_sync "$mut_caller" "$STUB"
|
types_in_sync pull_request_target "$mut_caller" "$STUB"
|
||||||
sed 's/, review_request_removed//' "$STUB" >"$mut_stub"
|
sed 's/, review_request_removed//' "$STUB" >"$mut_stub"
|
||||||
check "a type dropped from the stub goes red" 1 "" \
|
check "a type dropped from the stub goes red" 1 "" \
|
||||||
types_in_sync "$CALLER" "$mut_stub"
|
types_in_sync pull_request_target "$CALLER" "$mut_stub"
|
||||||
sed 's/review_requested, review_request_removed/review_request_removed, review_requested/' \
|
sed 's/review_requested, review_request_removed/review_request_removed, review_requested/' \
|
||||||
"$STUB" >"$mut_stub"
|
"$STUB" >"$mut_stub"
|
||||||
check "a reorder in one list only goes red" 1 "" \
|
check "a reorder in one list only goes red" 1 "" \
|
||||||
types_in_sync "$CALLER" "$mut_stub"
|
types_in_sync pull_request_target "$CALLER" "$mut_stub"
|
||||||
|
sed 's/, edited//' "$CALLER" >"$mut_caller"
|
||||||
|
check "an issue type dropped from the caller goes red" 1 "" \
|
||||||
|
types_in_sync issues "$mut_caller" "$STUB"
|
||||||
|
sed 's/, edited//' "$STUB" >"$mut_stub"
|
||||||
|
check "an issue type dropped from the stub goes red" 1 "" \
|
||||||
|
types_in_sync issues "$CALLER" "$mut_stub"
|
||||||
|
sed 's/closed, reopened/reopened, closed/' "$STUB" >"$mut_stub"
|
||||||
|
check "an issue-list reorder in one file only goes red" 1 "" \
|
||||||
|
types_in_sync issues "$CALLER" "$mut_stub"
|
||||||
|
|
||||||
summary
|
summary
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue