Skip to content

Merge queue: bound subprocess calls and re-check PR state at merge time - #130

Merged
pallaoro merged 3 commits into
mainfrom
what-do-you-think-about-this
Aug 20, 2026
Merged

Merge queue: bound subprocess calls and re-check PR state at merge time#130
pallaoro merged 3 commits into
mainfrom
what-do-you-think-about-this

Conversation

@pallaoro

Copy link
Copy Markdown
Member

Fixes the merge-queue wedge observed in the field (three green PRs stalled in one day, each unblocked only by a direct API merge) and the draft-PR retry loop that set it up.

The two defects

  1. A drafted PR passed every gate. GitHub reports drafts as mergeable: MERGEABLE, and nothing in the queue or the auto-merge loop read isDraft — so a drafted PR was enqueued, rejected by GitHub's API, had its badge cleared, and was re-enqueued on the next loop tick, forever, with a base-update push on every pass.
  2. No subprocess had a timeout. One git/gh call stalled on a dead connection or credential prompt never settles, and the SerialQueue (which chains on settle) wedges for that repo until app restart.

The fix

  • git-client: simple-git inactivity timeout (5-min block — resets on output, so slow-but-alive fetches survive) + GIT_TERMINAL_PROMPT=0.
  • merge: 2-min timeout on gh calls; prStatus now reports isDraft.
  • merge-queue: new preMergeGate() re-reads the PR the moment a job leaves the queue. Draft/closed always skip (new not-mergeable result). Loop-originated jobs (verifyChecksGreen) also re-verify checks are still green, closing the race between enqueue-time verdict and merge-time reality (the same invariant Bors/merge-train designs enforce). Manual button/shim merges keep their human-override semantics — red checks don't block an explicit human "merge".
  • templates: auto-merge-when-green skips drafts at its gate and flags its jobs for merge-time verification.

Verification

  • 132/132 tests pass, including 9 new preMergeGate cases (draft, closed, merged, red/pending checks, transient UNKNOWN mergeability).
  • Full-repo typecheck clean (also validates the timeout option shapes against real types).
  • Reproduced the wedge empirically: a git fetch to a blackhole IP through the exact gitFor config was killed at 2.1s (block timeout reached), settling its promise — which is what advances the SerialQueue.

Note: a currently-running app instance keeps its wedged in-memory queue; this protects after restart on the new build.

🤖 Generated with Claude Code

pallaoro and others added 3 commits August 19, 2026 09:52
…e, so one hung process can't wedge a repo's merges

Two defects compounded in the field: a drafted PR passed every gate
(drafts report mergeable: MERGEABLE), so the auto-merge loop re-enqueued
it endlessly, and with no timeout on git/gh subprocesses one stalled
call wedged the SerialQueue for its repo until app restart.

- git-client: simple-git inactivity timeout (5 min block) +
  GIT_TERMINAL_PROMPT=0, so stalled or prompting git calls settle
  instead of hanging forever
- merge: 2-min timeout on gh calls; prStatus now reports isDraft
- merge-queue: preMergeGate() re-reads the PR as the job leaves the
  queue — draft/closed always skip (new "not-mergeable" result);
  loop-originated jobs also re-verify checks are still green, closing
  the enqueue-time/merge-time race. Manual merges keep their
  human-override semantics.
- templates: auto-merge-when-green skips drafts and flags its jobs
  for merge-time verification

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pallaoro
pallaoro merged commit 6513ac8 into main Aug 20, 2026
1 check passed
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.

1 participant