From 755f465463501861dc88ab477e50505db0516db0 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:51:20 +0200 Subject: [PATCH] fix(ci): fall back to native auto-merge when a direct merge is refused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three consecutive sweeps read #278 and #282 as MERGEABLE/CLEAN, announced the merge, and were refused: X Pull request maonakamoto/evig#278 is not mergeable: the base branch policy prohibits the merge. There is no such policy. main has branch protection with every option disabled, no rulesets, no CODEOWNERS, no required checks and no required reviews. Throughout the same window the bot merged #225, #226, #279 and #280 without trouble — including #225/#226, which modify .github/workflows/, so this is not the token's workflows scope either. The refusal is gh's client-side precheck reading the mergeStateStatus that GITHUB_TOKEN sees, which is not the one a PAT sees — nothing this script can inspect its way around. So log both what we saw and delegate: native auto-merge is GitHub performing the merge itself, and does not go through that precheck. It is the escape hatch gh names in its own error message. Still one car per sweep — the fallback is followed by the same `break`, so at most one PR per sweep is handed over, and the green-base guard is unchanged. Co-Authored-By: Claude Opus 5 --- scripts/ci/auto-merge-sweep.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/scripts/ci/auto-merge-sweep.sh b/scripts/ci/auto-merge-sweep.sh index b585c4c77..9bd9f8295 100644 --- a/scripts/ci/auto-merge-sweep.sh +++ b/scripts/ci/auto-merge-sweep.sh @@ -304,15 +304,33 @@ for number in $(printf '%s' "$prs_json" | jq -r 'sort_by(.number) | .[].number') continue fi - echo "[auto-merge] #${number} green and ready — merging: ${title}" + echo "[auto-merge] #${number} green and ready (${mergeable}/${state}) — merging: ${title}" if gh pr merge "$number" --repo "$REPO" --squash --delete-branch; then merged_any=1 echo "[auto-merge] #${number} merged" # One car per sweep: let CI verify this on the base before the next couples. break else - # Losing a race (someone merged first, or the base moved underneath) is - # normal; the next sweep re-evaluates from fresh state. + # A direct merge can be refused with "the base branch policy prohibits the + # merge" for a PR that this same sweep just read as MERGEABLE/CLEAN. The + # refusal is gh's client-side precheck acting on the mergeStateStatus that + # GITHUB_TOKEN sees, which is not always the one a PAT sees — so it is not + # something this script can inspect its way out of. Observed 2026-08-07 on + # #278 and #282: three consecutive sweeps, both PRs fully green, both + # refused, while PRs not touching .github/ merged normally throughout. + # + # Native auto-merge is GitHub performing the merge itself once its own + # requirements are met, so it does not go through that precheck. It is the + # documented escape hatch (gh prints it in the failure message). + # + # Deliberately still followed by `break`: at most one PR per sweep is ever + # handed to auto-merge, which keeps the one-car-per-sweep discipline even + # though GitHub, not this script, completes the merge. + echo "[auto-merge] #${number} direct merge refused (${mergeable}/${state}) — delegating to native auto-merge" >&2 + if gh pr merge "$number" --repo "$REPO" --squash --delete-branch --auto; then + echo "[auto-merge] #${number} handed to native auto-merge; GitHub will complete it" + break + fi echo "[auto-merge] #${number} merge failed — leaving for the next sweep" >&2 fi done