From e5e0e95da70a84f54ad6dd12322cd06ee4551114 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Tue, 2 Jun 2026 13:18:52 +0200 Subject: [PATCH 1/2] docs(github-project): add 'verify a PR actually merged through the queue' recipe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a merge-queue repo, gh pr merge --merge/--auto only enqueues — the PR stays open and the queue merges later (or stalls). Document that enqueue != merged, the gh-readonly-queue//pr-- CI branch naming, and the GraphQL mergeQueue / merge_group / pr-view recipe to confirm the PR actually lands before declaring done. Signed-off-by: Sebastian Mendel --- .../references/auto-merge-guide.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/skills/github-project/references/auto-merge-guide.md b/skills/github-project/references/auto-merge-guide.md index dfb2d6c..e14c628 100644 --- a/skills/github-project/references/auto-merge-guide.md +++ b/skills/github-project/references/auto-merge-guide.md @@ -333,6 +333,36 @@ When landing multiple dependent PRs, expect the dependent PRs to need rebasing a | `REVIEW_REQUIRED` after rebase | Stale review dismissal cleared approval | Re-run auto-approve workflow | | Unresolved threads block merge | Old threads survive rebase | Resolve via GraphQL `resolveReviewThread` | +### Verifying a PR Actually Merged (enqueue ≠ merged) + +On a merge-queue repo, `gh pr merge … --merge` (or `--auto`) only **enqueues** the PR — +the command returns success immediately and the PR is **still open**. Don't report +"merged" off the exit code; the queue runs its own CI first and merges later (minutes, +if the queue runs the full matrix). It can also silently stall. + +The queue runs CI on a synthetic branch named `gh-readonly-queue//pr--`. +To find that CI and confirm the PR lands: + +```bash +# Is it queued, and at what position? +gh api graphql -f query='{ repository(owner:"OWNER",name:"REPO"){ + mergeQueue { entries(first:10){ nodes{ position state pullRequest{ number } } } } } }' \ + --jq '.data.repository.mergeQueue.entries.nodes[]?' + +# Watch the queue's own CI (note the merge_group event, not pull_request) +gh run list --repo OWNER/REPO --event merge_group --limit 5 \ + --json status,conclusion,headBranch,name \ + --jq '.[] | select(.headBranch|test("pr--")) | "\(.status)/\(.conclusion // "-") \(.name)"' + +# Confirm it actually merged (state MERGED + branch gone) +gh pr view --repo OWNER/REPO --json state,mergedAt,mergeCommit \ + --jq '{state, mergedAt, mergeCommit: (.mergeCommit.oid // "none")}' +``` + +Poll `state == "MERGED"` (or a `merge_group` run concluding `failure`) before declaring +done — green checks on the PR head are necessary but not sufficient once a queue is in +play. + ## Signed Commits and Merge Strategy Compatibility GitHub can only auto-sign **merge commits** and **squash merges**. It **cannot** auto-sign rebased commits. If branch protection requires signed commits and the workflow uses `--rebase`, merges fail with: From 26090ef9679f147b4706711ff7fbfd29acbddf33 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Tue, 2 Jun 2026 13:31:34 +0200 Subject: [PATCH 2/2] =?UTF-8?q?docs(github-project):=20address=20review=20?= =?UTF-8?q?=E2=80=94=20GraphQL=20variables=20+=20null-safe=20jq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the repo's agent-rules: pass values to gh api graphql via -f/-F variables instead of hardcoding owner/name in the query string, and use optional chaining (.mergeQueue?, .mergeCommit?.oid) with a fallback so jq is safe when the merge queue is disabled or the PR is unmerged. Signed-off-by: Sebastian Mendel --- skills/github-project/references/auto-merge-guide.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/skills/github-project/references/auto-merge-guide.md b/skills/github-project/references/auto-merge-guide.md index e14c628..b7a23ed 100644 --- a/skills/github-project/references/auto-merge-guide.md +++ b/skills/github-project/references/auto-merge-guide.md @@ -345,9 +345,11 @@ To find that CI and confirm the PR lands: ```bash # Is it queued, and at what position? -gh api graphql -f query='{ repository(owner:"OWNER",name:"REPO"){ - mergeQueue { entries(first:10){ nodes{ position state pullRequest{ number } } } } } }' \ - --jq '.data.repository.mergeQueue.entries.nodes[]?' +gh api graphql -f query='query($owner:String!,$repo:String!){ + repository(owner:$owner,name:$repo){ + mergeQueue { entries(first:10){ nodes{ position state pullRequest{ number } } } } } }' \ + -f owner=OWNER -f repo=REPO \ + --jq '.data.repository.mergeQueue?.entries?.nodes[]? // empty' # Watch the queue's own CI (note the merge_group event, not pull_request) gh run list --repo OWNER/REPO --event merge_group --limit 5 \ @@ -356,7 +358,7 @@ gh run list --repo OWNER/REPO --event merge_group --limit 5 \ # Confirm it actually merged (state MERGED + branch gone) gh pr view --repo OWNER/REPO --json state,mergedAt,mergeCommit \ - --jq '{state, mergedAt, mergeCommit: (.mergeCommit.oid // "none")}' + --jq '{state, mergedAt, mergeCommit: (.mergeCommit?.oid // "none")}' ``` Poll `state == "MERGED"` (or a `merge_group` run concluding `failure`) before declaring