From 7ca89044ee13b32c8479077878dd542529b231a4 Mon Sep 17 00:00:00 2001 From: Jeff Casimir Date: Thu, 10 Sep 2026 20:17:13 -0600 Subject: [PATCH] Auto-merge: say so when the merge will trigger nothing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A PR merged by native auto-merge is merged as whoever enabled it. With no AUTOMERGE_PAT that is github-actions[bot], and GitHub does not trigger workflows from a GITHUB_TOKEN push. The merge lands on the default branch having started nothing at all — no deploy, and no CI on the default branch either. It fails silently in the worst way: CI is green, the PR merges, every dashboard agrees, and production just stops changing. Frontier lost 18 merges and four and a half hours to this on 2026-09-10 before anyone noticed. The merge is still queued and still gated on CI — blocking it would cost more than it saves. But the job now fails afterwards, loudly, naming the fix, so the gap cannot pass for success. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016Rn19FnLSwT7aje6gTam39 --- .github/workflows/automerge.yml | 42 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 13185c2..95c8ee7 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -23,6 +23,11 @@ jobs: automerge: if: contains(github.event.pull_request.labels.*.name, 'automerge') runs-on: ubuntu-latest + # Resolved here rather than in a step `if:` — the secrets context is + # reliably available to job-level env, and this is the shape the workflow + # this replaced already used. + env: + HAS_PAT: ${{ secrets.AUTOMERGE_PAT != '' }} steps: # Enabling auto-merge on a PR with no required checks does not wait — it # merges immediately. So an ungated repo is not a harmless no-op here, it @@ -46,9 +51,12 @@ jobs: fi echo "'ci' is required on $BASE — safe to queue." - # AUTOMERGE_PAT where it exists: a merge attributed to GITHUB_TOKEN does - # not trigger the downstream deploy workflow (GitHub's recursion guard). - # Repos with no deploy job do not set the secret and fall back cleanly. + # AUTOMERGE_PAT matters more than it looks. GitHub deliberately does not + # trigger workflows from a push made with GITHUB_TOKEN (the anti-recursion + # rule), and native auto-merge performs the merge as whoever ENABLED it. + # Enable it as github-actions[bot] and the merge commit lands on the + # default branch having triggered nothing at all — no deploy, and no CI on + # the default branch either. - name: Queue the merge env: GH_TOKEN: ${{ secrets.AUTOMERGE_PAT || github.token }} @@ -58,3 +66,31 @@ jobs: set -euo pipefail gh pr merge "$NUMBER" --repo "$REPO" --auto --squash --delete-branch echo "Auto-merge enabled on #$NUMBER; GitHub will land it when 'ci' is green." + + # Deliberately AFTER the merge is queued, and deliberately fatal. + # + # Without a PAT the merge still happens and is still gated on CI, so + # blocking it would cost more than it saves. What is lost is everything + # the merge push should have triggered — which fails silently: CI is green, + # the PR merges, every dashboard agrees, and production simply stops + # changing. Frontier lost 18 merges and four and a half hours to exactly + # this on 2026-09-10. + # + # A red check here is the only thing standing between that and nobody + # noticing. If this repo genuinely has nothing that runs on a push to its + # default branch, the fix is still to set the secret rather than to make + # this conditional — one invariant, no per-repo exceptions to remember. + - name: A bot merge triggers nothing downstream + if: env.HAS_PAT != 'true' + run: | + echo "::error::AUTOMERGE_PAT is not set on ${{ github.repository }}." + echo "" + echo "This PR will merge, gated on CI as normal. But the merge will be" + echo "attributed to github-actions[bot], and GitHub does not trigger" + echo "workflows from a GITHUB_TOKEN push. So nothing will run on the" + echo "resulting push to the default branch — including any deploy." + echo "" + echo "Fix: set an AUTOMERGE_PAT secret (org-level covers every repo at" + echo "once). Until then, dispatch the deploy by hand after this merges:" + echo " gh workflow run deploy.yml --repo ${{ github.repository }}" + exit 1