test: terminate the ci.yml step block at the job boundary too

The monotonic step is the LAST step of the `check` job, so stopping only at
the next `- name:` ran the block into `rehearsal` and swallowed its job-level
`if:`. Unanchored `grep -q 'if:'` then fired on it — reintroducing the bug the
scoping was meant to fix, moved from "any step in the file" to "this step plus
the head of the next job".

Terminates on a new step OR a new job now, and the key is anchored so an `if:`
inside a `run:` line is not mistaken for a step condition.

Mutation-tested four ways: baseline green; an unrelated job gated stays green
(was the failure); the monotonic step gated fails; the step renamed trips the
companion found-the-block guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dan-claude-bot 2026-07-20 20:54:31 +00:00
parent 4b658b6fb0
commit 1e3fb473c3

View file

@ -501,12 +501,19 @@ check "ci.yml: ...and STRICT, so a skip is a red run and not a green one" 0 "" \
# Scoped to the step's OWN block, deliberately. A file-wide negative would # Scoped to the step's OWN block, deliberately. A file-wide negative would
# forbid any FUTURE step in ci.yml from being pull_request-gated and would fail # forbid any FUTURE step in ci.yml from being pull_request-gated and would fail
# citing #143 when one legitimately is — #143 constrains this step, not the file. # citing #143 when one legitimately is — #143 constrains this step, not the file.
# Terminates on a new STEP or a new JOB. The job boundary is not optional: the
# monotonic step is the LAST step of `check`, so stopping only at the next
# `- name:` runs the block into the `rehearsal` job below and swallows its
# job-level `if:`. That reintroduces the very bug the scoping fixed — an
# unrelated edit failing while citing #143 — just moved from "any step in the
# file" to "this step plus the head of the next job".
mono_step_block() { mono_step_block() {
awk '/^ - name: no shipped changelog heading/ {f=1; print; next} awk '/^ - name: no shipped changelog heading/ {f=1; print; next}
f && /^ - name: / {exit} f && (/^ - / || /^ [^ ]/) {exit}
f {print}' "$ROOT/.github/workflows/ci.yml" f {print}' "$ROOT/.github/workflows/ci.yml"
} }
mono_step_gated() { mono_step_block | grep -q 'if:'; } # Anchored: an `if:` appearing inside a `run:` line is not a step condition.
mono_step_gated() { mono_step_block | grep -q '^ if:'; }
check "ci.yml: the monotonic step itself is not pull_request-gated (#143)" 1 "" mono_step_gated check "ci.yml: the monotonic step itself is not pull_request-gated (#143)" 1 "" mono_step_gated
check "ci.yml: ...and the block was actually found (guards the awk above)" 0 "changelog-monotonic" mono_step_block check "ci.yml: ...and the block was actually found (guards the awk above)" 0 "changelog-monotonic" mono_step_block
check "ci.yml: ...and falls back to ref_name, so a push has a base to resolve" 0 "" \ check "ci.yml: ...and falls back to ref_name, so a push has a base to resolve" 0 "" \