From 35bd5cc277f59928bb7ded0ea5bbe59029c5a11a Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:24:24 +0200 Subject: [PATCH] ci: file an issue the moment main goes red A red main silently stops the whole repo. On 2026-08-05 a regression reddened CI on main; the fix was pushed to a PR branch and that branch went green, but nothing re-runs a base-branch run, so main's own run stayed red. Auto-merge's green-base guard then refused every merge for ~16h, holding back 17 PRs while logging its refusal into a workflow nobody opens. The cost was not the breakage, it was the misattribution: the symptom ("the agent can't merge") points away from the cause ("the base is broken"). Ported from OrangeCat, adapted to this repo's CI workflow name. Files or updates ONE issue on a failed CI run on main, closes it when main is green again, and says in the body that clearing it means re-running MAIN's run, not the branch's. Co-Authored-By: Claude Opus 5 --- .github/workflows/main-red-alert.yml | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/main-red-alert.yml diff --git a/.github/workflows/main-red-alert.yml b/.github/workflows/main-red-alert.yml new file mode 100644 index 000000000..600de3339 --- /dev/null +++ b/.github/workflows/main-red-alert.yml @@ -0,0 +1,74 @@ +name: Main Red Alert + +# A red main is invisible, and it silently stops the whole repo. +# +# 2026-08-06: a regression reddened CI on main at 17:40 the previous day. The +# fix was pushed to a PR branch, that branch went green, and the fix was called +# done — but nothing re-runs a base-branch run, so main's own run stayed red. +# Auto-merge's green-base guard then refused every merge for ~16 hours, holding +# back 17 PRs while logging "refusing to merge onto a broken base" into a +# workflow nobody opens. It presented as "the agent can't merge" rather than +# "the base is broken", which is the expensive part: the symptom points away +# from the cause. +# +# This files (or updates) ONE issue the moment CI fails on main, and closes it +# when main is green again — so "is main broken?" is answerable without +# watching Actions, and the merge train never stalls unnoticed again. +# +# Fixing a red main means re-running MAIN's run (`gh run rerun --failed`), +# not just your branch's. A green branch on a red base merges nothing. + +on: + workflow_run: + workflows: ['CI Pipeline'] + branches: [main] + types: [completed] + +permissions: + issues: write + actions: read + +jobs: + alert: + runs-on: ubuntu-latest + steps: + - name: File or resolve the main-red issue + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONCLUSION: ${{ github.event.workflow_run.conclusion }} + RUN_URL: ${{ github.event.workflow_run.html_url }} + RUN_SHA: ${{ github.event.workflow_run.head_sha }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + TITLE="🔴 main is red — CI failing on main" + + # A cancelled run means a newer push superseded this one (the CI + # concurrency group cancels in-progress runs). That is not a failure, + # and treating it as one is how "cancelled" gets misread as "broken". + if [ "$CONCLUSION" != "failure" ]; then + existing=$(gh issue list -R "$REPO" --state open --search "$TITLE in:title" --json number --jq '.[0].number // empty') + if [ "$CONCLUSION" = "success" ] && [ -n "$existing" ]; then + gh issue close "$existing" -R "$REPO" \ + --comment "main is green again as of ${RUN_SHA:0:8} — $RUN_URL" + fi + exit 0 + fi + + BODY=$(printf '%s\n' \ + "CI failed on \`main\` at commit \`${RUN_SHA:0:8}\`." \ + "" \ + "Run: $RUN_URL" \ + "" \ + "**Every PR branched from this commit inherits the failure**, and auto-merge refuses to merge onto a broken base — so nothing ships until this is green." \ + "" \ + "Fix or revert, then re-run **main's** run (\`gh run rerun --failed\`). Re-running your own branch does not clear this." \ + "" \ + "This issue closes itself when a CI run on main succeeds.") + + existing=$(gh issue list -R "$REPO" --state open --search "$TITLE in:title" --json number --jq '.[0].number // empty') + if [ -n "$existing" ]; then + gh issue comment "$existing" -R "$REPO" --body "$BODY" + else + gh issue create -R "$REPO" --title "$TITLE" --body "$BODY" + fi