diff --git a/skills/git-workflow/references/merge-gate-watcher.md b/skills/git-workflow/references/merge-gate-watcher.md index 786e5eb..3cf90fa 100644 --- a/skills/git-workflow/references/merge-gate-watcher.md +++ b/skills/git-workflow/references/merge-gate-watcher.md @@ -150,3 +150,7 @@ the remote (`git fetch origin` then re-add the worktree tracking `origin/`) — but only while the remote ref still exists (a merged PR's branch is often auto-deleted). The discipline is cheaper than the recovery: **confirm `state == MERGED` before any destructive cleanup.** + +## A queued PR can silently leave the merge queue + +A PR queued via `gh pr merge --auto` on a merge-queue repo can drop back out with no visible event: `isInMergeQueue` flips to `false`, `mergeStateStatus` reads `CLEAN`, and nothing merges. Verify the real queue state via GraphQL (`state` / `merged` / `isInMergeQueue` / `mergeStateStatus`) — a status read that only looks at `mergeStateStatus` reports a dropped PR as merge-ready. Re-arm once (`gh pr merge --disable-auto`, then `--auto`, which forces the queue to re-evaluate); if it drops again, diagnose the queue's required contexts instead of re-arming repeatedly. diff --git a/skills/git-workflow/scripts/merge-gate.sh b/skills/git-workflow/scripts/merge-gate.sh index df57f25..bcd428d 100755 --- a/skills/git-workflow/scripts/merge-gate.sh +++ b/skills/git-workflow/scripts/merge-gate.sh @@ -36,6 +36,18 @@ fi # resolution via GraphQL (owner/repo/number parsed from the url). INFO=$(gh pr view "$PR" "${REPO_FLAG[@]}" --json mergeStateStatus,url 2>/dev/null) || exit 0 MSS=$(echo "$INFO" | jq -r '.mergeStateStatus // "null"') +# UNKNOWN usually means GitHub is still computing the state (fresh push, or a +# merge landed elsewhere moments ago). Re-poll briefly before evaluating, so a +# legitimate merge isn't denied on a transient; a persistent UNKNOWN still +# denies below (fail-closed). This is a bounded pre-check inside a PreToolUse +# hook, not a merge-driver loop — pr-status.sh --watch remains the watcher. +tries=0 +while [[ "$MSS" == "UNKNOWN" && $tries -lt 3 ]]; do + sleep 3 + INFO=$(gh pr view "$PR" "${REPO_FLAG[@]}" --json mergeStateStatus,url 2>/dev/null) || exit 0 + MSS=$(echo "$INFO" | jq -r '.mergeStateStatus // "null"') + tries=$((tries + 1)) +done URL=$(echo "$INFO" | jq -r '.url // ""') [[ "$URL" =~ github\.com/([^/]+)/([^/]+)/pull/([0-9]+) ]] || exit 0 O="${BASH_REMATCH[1]}"; RN="${BASH_REMATCH[2]}"; NUM="${BASH_REMATCH[3]}"