If any precondition fails, stop — do not merge. For precondition 7,
if the quiet period hasn't elapsed yet, schedule a run_once cron
job for the remaining time and stop. The cron will re-trigger this
skill when the period is up.
If the PR exceeds the size gate, stop. Post a comment noting the PR
needs human review and list which criteria it exceeded.
-
Check quiet period. Verify the PR was marked ready at least
10 minutes ago with no reviewer activity since:
READY_AT=$(gh api "repos/<owner>/<repo>/issues/<N>/timeline" --paginate \
--jq '[.[] | select(.event=="ready_for_review")] | last | .created_at')
Calculate elapsed time. If less than 10 minutes have passed,
schedule a run_once cron job for the remaining time with
entity_key set to the PR entity, and stop. The cron prompt
should instruct the agent to reload the auto-merge skill.
Also check for any reviewer comments or changes_requested
reviews that arrived after READY_AT:
gh api "repos/<owner>/<repo>/pulls/<N>/reviews" \
--jq '[.[] | select(.submitted_at > "'$READY_AT'" and .state != "APPROVED" and .state != "COMMENTED")]'
If any exist, stop — the PR needs human attention.
-
Verify PR state and target branch:
PR_JSON=$(gh pr view <N> --json state,isDraft,baseRefName)
STATE=$(echo "$PR_JSON" | jq -r '.state')
IS_DRAFT=$(echo "$PR_JSON" | jq -r '.isDraft')
BASE=$(echo "$PR_JSON" | jq -r '.baseRefName')
DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
- Expect
STATE=OPEN, IS_DRAFT=false. If draft or closed, stop.
- Expect
BASE == DEFAULT_BRANCH. If the PR targets a release or
other protected branch, stop — those need human review.
-
Verify all checks pass:
CHECKS=$(gh pr view <N> --json statusCheckRollup \
--jq '.statusCheckRollup')
For each check, inspect status and conclusion:
- If any check has
status other than "COMPLETED" (e.g.
"QUEUED", "IN_PROGRESS", "PENDING"), stop — checks
haven't finished yet. Post a comment noting which checks are
still running.
- If any completed check has
conclusion other than "SUCCESS",
"SKIPPED", or "NEUTRAL", stop — checks are failing. Post a
comment listing the failing checks.
Quick jq filter for non-passing completed checks:
FAILING=$(echo "$CHECKS" | jq '[.[] | select(
.status == "COMPLETED" and
.conclusion != "SUCCESS" and
.conclusion != "SKIPPED" and
.conclusion != "NEUTRAL"
)]')
Quick jq filter for still-running checks:
PENDING=$(echo "$CHECKS" | jq '[.[] | select(.status != "COMPLETED")]')
-
Evaluate the size gate:
PR_DATA=$(gh pr view <N> --json additions,deletions,files)
ADDITIONS=$(echo "$PR_DATA" | jq '.additions')
DELETIONS=$(echo "$PR_DATA" | jq '.deletions')
TOTAL=$((ADDITIONS + DELETIONS))
FILES_CHANGED=$(echo "$PR_DATA" | jq '.files | length')
Check each criterion listed in the size gate section. Inspect the
file list for CI/CD, lockfile, migration, auth, or public API
changes:
echo "$PR_DATA" | jq -r '.files[].path'
-
Check for review objections and unresolved threads:
CHANGES_REQUESTED=$(gh pr view <N> --json reviews \
--jq '[.reviews[] | select(.state == "CHANGES_REQUESTED")] | length')
If CHANGES_REQUESTED > 0, stop — a reviewer has requested changes.
Check for unresolved review threads via the GraphQL API:
UNRESOLVED=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes { isResolved }
}
}
}
}' -f owner=<OWNER> -f repo=<REPO> -F pr=<N> \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length')
If UNRESOLVED > 0, stop — there are unresolved review threads.
-
Merge:
gh pr merge <N> --squash --auto --delete-branch
Use --squash to keep the main branch history clean.
Use --auto so GitHub waits for branch protection rules.
Use --delete-branch to clean up the feature branch.
-
Post a short comment confirming the merge was enabled. Mention
the total diff size and that all checks passed. Write it
naturally — vary the wording, don't use a canned phrase.
In all these cases, leave a comment explaining why auto-merge was
skipped, and let a human decide.