Merge pull request #210 from dan-claude-bot/build/208-checks-state-self-filter

fix: checks_state never grades the label machine's own runs (#208)
This commit is contained in:
Daniel Marin 2026-08-01 18:44:44 +01:00 committed by GitHub
commit b6d7e26d82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 85 additions and 1 deletions

View file

@ -49,6 +49,15 @@ LABELS=""
# retirement heals the board instead of stranding a label nothing recomputes.
RETIRED=(state:needs-rebase)
STALE_AFTER=$((48 * 3600))
# The workflow whose runs checks_state must never grade — its own (#208).
# GITHUB_WORKFLOW is ambient in every Actions step and names the CALLER (the
# consumer's PR-facing workflow, since consumers name the caller), so this
# self-serves with no workflow-file change. The explicit override exists for
# two readers: the fixtures, and #209's detached sweep caller, which will
# need to point this at the PR-facing caller's name once reconcile no longer
# runs inside it. Empty means "filter nothing" — a caller outside Actions
# (a local rehearsal, an older pin) must not silently start dropping entries.
SELF_WORKFLOW="${SELF_WORKFLOW:-${GITHUB_WORKFLOW:-}}"
# The needs-ruling invariants (#52) — one implementation for both surfaces.
# shellcheck source=lib/ruling.sh
@ -221,7 +230,27 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE |
# a context whose entries are ALL cancelled never reported at all (a killed
# or timed-out required job), so it keeps CANCELLED and still blocks —
# discard needs a surviving verdict, never an empty context.
jq -r '
#
# And one exclusion that comes before every rule above: the label machine
# never grades its own runs (#208). Every reconcile sweep serializes
# through one shared concurrency group, and GitHub records a displaced
# queued run as CANCELLED — there is no "superseded" conclusion for queue
# displacement. When the displaced run was born from a pull_request_target
# event, that cancelled entry attaches to the victim PR while its
# SUCCESSOR — triggered by a different PR or an issues event — attaches
# elsewhere, so the #139 carve-out's premise (a surviving sibling on the
# same PR) fails structurally: on the victim the newest self entry stays
# CANCELLED, the deny-list scores it FAILURE, and the sweep sets
# blocker:ci-red off its own corpse — then re-affirms it every cadence.
# Proven on crew#227: every real check green, the only red rollup entry
# the sweep's own displaced run. So drop every entry belonging to
# $SELF_WORKFLOW before the newest-per-context collapse. Accepted
# consequences: a rollup of ONLY self entries scores NONE (honestly: no
# checks — never SUCCESS), and a genuine reconcile failure surfaces on the
# Actions tab instead of as blocker:ci-red, which is right because no PR
# edit can fix the label machinery. An empty $self filters nothing — the
# exclusion must never widen into dropping entries on a guess.
jq -r --arg self "$SELF_WORKFLOW" '
if (has("statusCheckRollup") | not) then "UNREADABLE" else
# NEUTRAL and SKIPPED satisfy branch protection — a skipped required check
@ -262,7 +291,11 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE |
# and treating it as newest keeps an undateable in-flight run from being
# discarded in favour of a stale success. Every ambiguity resolves toward
# "not settled".
# The #208 exclusion (header above): self entries leave the rollup here,
# BEFORE the group_by — a self-only context must vanish entirely, never
# survive as an all-cancelled context that still classifies FAILURE.
| [ (.statusCheckRollup // [])[]
| select($self == "" or (.workflowName // "") != $self)
| { ctx: [.workflowName // "", .name // .context // ""],
at: ([.startedAt, .createdAt, .completedAt]
| map(select(type == "string" and . != ""

11
changelog.d/208.md Normal file
View file

@ -0,0 +1,11 @@
### Fixed
- `checks_state` drops rollup entries belonging to the workflow it runs
inside — `SELF_WORKFLOW`, defaulting to the ambient `GITHUB_WORKFLOW`
before the newest-per-context collapse: the label machine never grades
its own runs, and an empty name filters nothing (#208).
- A sweep displaced from the shared concurrency queue attaches CANCELLED to
its PR while its successor attaches elsewhere, so the sweep set
`blocker:ci-red` off its own displaced run and re-affirmed it every
cadence (crew#227). A rollup of only self entries now honestly scores
NONE (#208).

View file

@ -351,6 +351,13 @@ run_() { jq -n --arg n "$1" --arg o "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
ctx_() { jq -n --arg n "$1" --arg s "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
'{__typename:"StatusContext", context:$n, state:$s, createdAt:$t}'; }
# Pinned empty for every fixture below except the #208 block, which sets its
# own. The script defaults SELF_WORKFLOW from the ambient GITHUB_WORKFLOW —
# present in any CI run of this suite — and an inherited name that happened
# to match a fixture's workflowName ("ci", "labels") would silently drop
# entries these fixtures rely on. The verdicts must not flip with the runner.
SELF_WORKFLOW=""
expect "no checks at all is NONE" NONE "$(rollup '[]' | checks_state)"
# A failed fetch leaves no rollup KEY; a PR with no checks leaves an empty
# ARRAY. Collapsing the two let an API hiccup read as "nothing is failing" —
@ -429,6 +436,39 @@ expect "a cancelled newest over an earlier FAILURE is still that failure" FAILUR
"$(rollup "[$(rec_ FAILURE 2026-07-24T12:16:17Z 2026-07-24T12:17:06Z),\
$(rec_ CANCELLED 2026-07-24T12:16:41Z 2026-07-24T12:16:41Z)]" | checks_state)"
# -- the #208 exclusion: the label machine never grades its own runs. The
# shared concurrency group displaces queued sweeps as CANCELLED, and the
# displaced run's successor was triggered by a DIFFERENT PR or an issues
# event — so on the victim PR the #139 carve-out's premise (a surviving
# sibling on the same head) fails structurally: the newest self entry
# stays CANCELLED, and the sweep set blocker:ci-red off its own corpse,
# re-affirming it every cadence. Proven on crew#227: every real check
# green, the only red rollup entry the sweep's own displaced run. rec_
# already builds entries under workflowName "labels"; naming that as
# self must drop them whole, before the newest-per-context collapse.
SELF_WORKFLOW="labels"
expect "a displaced self CANCELLED beside green others is no verdict (crew#227)" SUCCESS \
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
expect "a FAILED self run surfaces on the Actions tab, not as the PR's red" SUCCESS \
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
$(rec_ FAILURE 2026-08-01T15:00:00Z 2026-08-01T15:01:00Z)]" | checks_state)"
expect "a rollup of ONLY self entries is honestly NONE, never SUCCESS" NONE \
"$(rollup "[$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
# must-fail: the filter keys on the self workflow ALONE. Widening it — any
# cancelled entry, any labels-shaped name — certifies a genuine foreign
# failure green, which is #136's unknown-as-green shape all over again.
expect "a genuine foreign FAILURE still blocks beside a cancelled self entry" FAILURE \
"$(rollup "[$(run_ a FAILURE),\
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
# ...and an empty self filters NOTHING: outside Actions no workflow name is
# ambient, and the exclusion must never drop entries on a guess — the same
# displaced-self rollup keeps blocking there, all-cancelled context intact.
SELF_WORKFLOW=""
expect "an empty SELF_WORKFLOW filters nothing — the same rollup still blocks" FAILURE \
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
# -- a run still IN FLIGHT. `run_()` cannot express this: it always carries a
# real completedAt, which is exactly why the supersede rule shipped dating
# runs by completion and nothing caught it. Both spellings of "no