Skip to content

fix(merge-queue): merge the winner directly when auto-merge doesn't fire - #10786

Merged
davidfirst merged 2 commits into
masterfrom
fix/merge-queue-fallback-merge
Oct 12, 2026
Merged

davidfirst merged 2 commits into
masterfrom
fix/merge-queue-fallback-merge

Conversation

@davidfirst

Copy link
Copy Markdown
Member

GitHub's auto-merge doesn't reliably wake up from the merge-queue/turn success status (posted with GITHUB_TOKEN), so a PR whose turn comes long after its last CircleCI status can sit "merging now" until the 15m timeout revokes it.

Now, once a turn is older than 2m and the PR is still open, the reconcile squash-merges it itself — pinned to the granted head sha, using the PR's auto-merge commit message. Failures are logged; the 15m revoke still applies.

Requires a new repo secret MERGE_QUEUE_MERGE_TOKEN: a bit-core member's fine-grained token (contents + pull-requests write), since master's push restriction excludes GITHUB_TOKEN. Without it, the fallback logs the refusal and behavior is unchanged.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Merge queue winners directly when GitHub auto-merge stalls

🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Squash-merge winners still open two minutes after receiving their turn, avoiding stalled queue
 handoffs.
• Pin fallback merges to the granted head SHA and preserve the requested auto-merge message.
• Supply a dedicated merge token; failed attempts remain logged and subject to the 15-minute
 timeout.
Diagram

graph TD
  R["Queue reconcile"] --> W{"Sticky winner?"} -->|yes| G{"Gate over 2m?"} -->|yes| M["Merge API"] -->|merged| L["PR landed"]
  W -->|new turn| A["Grant success gate"] --> N["GitHub auto-merge"] -->|merged| L
  G -->|no| N
  M -->|repeated refusals| T["15m turn revoke"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Own every winner merge
  • ➕ Removes dependence on GitHub auto-merge waking up.
  • ➖ Uses the privileged merge token for every winner.
  • ➖ Removes the grace period in which GitHub can merge normally.

Recommendation: Keep the conditional fallback: it addresses stalled winners without replacing the normal auto-merge path, and the SHA check protects against merging a newly pushed head. Reviewers should pay particular attention to the dedicated token's access and the absence of fallback tests.

Files changed (2) +56 / -5

Bug fix (1) +54 / -5
merge-queue.jsAdd a delayed, SHA-pinned direct merge fallback +54/-5

Add a delayed, SHA-pinned direct merge fallback

• Fetches the auto-merge method and commit message, then attempts a direct merge when an existing winner's success gate is older than two minutes. The request uses the granted head SHA and a dedicated token when available; failures are logged, while dry-run behavior and the existing 15-minute revocation remain intact.

.github/scripts/merge-queue.js

Other (1) +2 / -0
merge-queue.ymlPass a dedicated merge token to reconciliation +2/-0

Pass a dedicated merge token to reconciliation

• Exposes MERGE_QUEUE_MERGE_TOKEN from repository secrets so the fallback can merge under master's push restrictions. The existing GITHUB_TOKEN remains available for other queue operations.

.github/workflows/merge-queue.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Stalled winners can miss the merge fallback 🐞 Bug ≡ Correctness
Description
main calls mergeStuckWinner only for a sticky winner, but it demotes that winner once its gate
is 15 minutes old before reaching the new fallback check. If no reconcile runs between minutes 2 and
15, the next run revokes the turn without attempting the merge; the workflow documents 20–40-minute
schedule gaps and the gate status does not trigger another run.
Code

.github/scripts/merge-queue.js[R756-758]

+  if (winner && winner === stickyWinner) {
+    const gateAgeMs = Date.now() - new Date(getGateStatus(winner.pullRequest).createdAt).getTime();
+    if (gateAgeMs > AUTO_MERGE_GRACE_MS) await mergeStuckWinner(winner.pullRequest);
Evidence
The 15-minute branch prevents assignment to stickyWinner, while the added fallback requires
winner === stickyWinner. The workflow documents cron delays longer than that window, and posting
the gate does not schedule a follow-up reconcile.

.github/scripts/merge-queue.js[726-758]
.github/scripts/merge-queue.js[399-416]
.github/workflows/merge-queue.yml[41-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A stalled winner is demoted before the fallback runs if the next reconcile occurs more than 15 minutes after its gate succeeded. Documented schedule delays can skip the entire 2–15-minute fallback window.
## Fix Focus Areas
- .github/scripts/merge-queue.js[726-758]
## Recommended Fix
For an open winner with a successful gate and a settled master, attempt the SHA-pinned fallback before applying the 15-minute demotion, even when the first subsequent reconcile is late. Preserve the existing demotion behavior if the merge is refused.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +756 to +758
if (winner && winner === stickyWinner) {
const gateAgeMs = Date.now() - new Date(getGateStatus(winner.pullRequest).createdAt).getTime();
if (gateAgeMs > AUTO_MERGE_GRACE_MS) await mergeStuckWinner(winner.pullRequest);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Stalled winners can miss the merge fallback 🐞 Bug ≡ Correctness

main calls mergeStuckWinner only for a sticky winner, but it demotes that winner once its gate
is 15 minutes old before reaching the new fallback check. If no reconcile runs between minutes 2 and
15, the next run revokes the turn without attempting the merge; the workflow documents 20–40-minute
schedule gaps and the gate status does not trigger another run.
Agent Prompt
## Issue description
A stalled winner is demoted before the fallback runs if the next reconcile occurs more than 15 minutes after its gate succeeded. Documented schedule delays can skip the entire 2–15-minute fallback window.
## Fix Focus Areas
- .github/scripts/merge-queue.js[726-758]
## Recommended Fix
For an open winner with a successful gate and a settled master, attempt the SHA-pinned fallback before applying the 15-minute demotion, even when the first subsequent reconcile is late. Preserve the existing demotion behavior if the merge is refused.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@davidfirst
davidfirst enabled auto-merge (squash) October 12, 2026 15:25
@davidfirst
davidfirst merged commit 03962fc into master Oct 12, 2026
14 checks passed
@davidfirst
davidfirst deleted the fix/merge-queue-fallback-merge branch October 12, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants