merge-to-master: an unknown CI read is waited through inside the deadline, not refused at once (7 October 2026, 18:0x UK: 500s on pushes, a 404 on a live run id, then the account suspension; the rule refuses only a verdict, and a missing verdict only at the deadline)

ci_gate: unknown prints the reason and asks again until --ci-wait runs out (CI_WAIT_SECS for the self-test). master_gate: three reads
over a minute before an unknown counts; a lasting unknown still refuses (no verdict, no merge). Self-test: a blip (unknown, then
green) merges; a lasting unknown is refused at the deadline; the same two shapes for master's read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 17:04:22 +00:00
parent 7594ae0a45
commit 74bb841dfe

View file

@ -27,13 +27,14 @@ clock() { TZ=Europe/London date '+%H:%M %Z'; }
# waits on a queued or running run; refuses (1) a red or unknown one. master_gate: refuses (1) when master's last compiled run is red.
ci_gate() {
local sha="$1" branch="$2"; shift 2; local line state deadline pushed=0
deadline=$(( $(date +%s) + CI_WAIT_MIN * 60 ))
deadline=$(( $(date +%s) + ${CI_WAIT_SECS:-$(( CI_WAIT_MIN * 60 ))} ))
while :; do
line=$($CI_STATE "$sha" 2>&1); state="${line%% *}"
case "$state" in
success) echo "merge-to-master: ci on ${sha:0:8} is green: $line"; return 0 ;;
none) if [ "$pushed" = 1 ]; then echo "merge-to-master: waiting for a ci run to appear on ${sha:0:8} ($(clock))"; else echo "merge-to-master: no ci run on ${sha:0:8} yet; pushing $branch so CI runs it ($(clock))"; "$@" || { echo "merge-to-master: the branch push failed; CI cannot run ${sha:0:8}" >&2; return 1; }; pushed=1; fi ;;
pending) echo "merge-to-master: ci on ${sha:0:8} is ${line#* * * }; waiting ($(clock))" ;;
unknown) echo "merge-to-master: ci on ${sha:0:8} could not be read (${line#* * * }); GitHub or gh wobbling, asking again ($(clock))" ;; # 7 Oct 2026 18:0x UK: 500s on pushes, a 404 on a live run id
*) echo "merge-to-master: REFUSED. ci on ${sha:0:8} is $line" >&2; echo " A branch whose own ci run is not green never merges (CI red is stop-the-line). Fix the branch, push a new commit, and run this again." >&2; return 1 ;;
esac
[ "$(date +%s)" -lt "$deadline" ] || { echo "merge-to-master: REFUSED. ci on ${sha:0:8} gave no verdict in $CI_WAIT_MIN minutes ($line); run it again when the queue drains (gh run list --branch $branch --limit 3)" >&2; return 1; }
@ -41,7 +42,12 @@ ci_gate() {
done
}
master_gate() {
local line state; line=$($CI_STATE --master-code 2>&1); state="${line%% *}"
local line state try
for try in 1 2 3; do # an unknown read (gh or GitHub wobbling) is asked again, three times over a minute, before it counts
line=$($CI_STATE --master-code 2>&1); state="${line%% *}"
[ "$state" = unknown ] && [ "$try" -lt 3 ] && { echo "merge-to-master: master's verdict could not be read (${line#* * * }); asking again ($(clock))"; sleep "${CI_POLL_SECS:-30}"; continue; }
break
done
case "$state" in
success|none|pending) echo "merge-to-master: master's last compiled run: $line"; return 0 ;;
*) if [ "$FIXES_MASTER" = 1 ]; then echo "merge-to-master: master's last compiled run is $state and this merge is declared the fix (--fixes-master): $line"; return 0; fi
@ -67,9 +73,10 @@ FAKE
printf 'failure 2 u push run: igneum-pow tests at "igneum-pow tests (release)"
' > "$d/answer-red"
ci_gate red b false >/dev/null 2>&1 && { echo "self-test failed: a red branch run merged"; fails=1; }
printf 'unknown - - gh: exit 1
' > "$d/answer-unk"
ci_gate unk b false >/dev/null 2>&1 && { echo "self-test failed: an unknown verdict merged"; fails=1; }
printf 'unknown - - gh: exit 1\n' > "$d/answer-unk"
CI_WAIT_SECS=1 ci_gate unk b false >/dev/null 2>&1 && { echo "self-test failed: a lasting unknown verdict merged"; fails=1; }
printf 'unknown - - gh: HTTP 404\nsuccess 9 u push run\n' > "$d/answer-blip"
ci_gate blip b false >/dev/null 2>&1 || { echo "self-test failed: a GitHub blip (unknown, then green) was refused instead of waited through"; fails=1; }
printf 'pending 3 u queued since 16:00 UK
pending 3 u in_progress since 16:00 UK
success 3 u push run at 16:05 UK
@ -91,8 +98,12 @@ success 4 u push run
printf 'success 6 u master @e5171a32, compile job success
' > "$d/answer-master"; rm -f "$d/i-master"
FIXES_MASTER=0 master_gate >/dev/null || { echo "self-test failed: a green master refused a merge"; fails=1; }
printf 'unknown - - gh: HTTP 404\nsuccess 7 u master, compile job success\n' > "$d/answer-master"; rm -f "$d/i-master"
FIXES_MASTER=0 master_gate >/dev/null || { echo "self-test failed: an unknown master read that turned green refused a merge"; fails=1; }
printf 'unknown - - gh: exit 1\n' > "$d/answer-master"; rm -f "$d/i-master"
FIXES_MASTER=0 master_gate >/dev/null 2>&1 && { echo "self-test failed: a lasting unknown master read let a merge through"; fails=1; }
rm -rf "$d"
[ "$fails" = 0 ] && echo "self-test passed: a green branch run merges; a red or unknown one is refused; a queued run is waited for with the clock; an unrun branch is pushed once and then waited for; a red master refuses every merge but the declared fix"
[ "$fails" = 0 ] && echo "self-test passed: a green branch run merges; a red one is refused; an unknown read is waited through and refused only at the deadline; a queued run is waited for with the clock; an unrun branch is pushed once and then waited for; a red master refuses every merge but the declared fix"
exit $fails
fi
[ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "merge-to-master: the tree has uncommitted tracked changes; commit first" >&2; exit 1; }