AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Driving Prs To Merge

skill-skyfox675-agents-skills-driving-prs-to-merge · by skyfox675

Owns the entire post-open PR lifecycle in a multi-agent GitHub repo — enabling and re-arming auto-merge, reading mergeStateStatus correctly, triaging CI failures from per-job logs, classifying transient vs real failures, resolving bot and human review threads (including the async-bot orphan trap), rescuing DIRTY/conflicted PRs, operating merge queues, handling pre-existing red CI, and dispatching…

No reviews yet
0 installs
32 views
0.0% view→install

Install

$ agentstack add skill-skyfox675-agents-skills-driving-prs-to-merge

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-skyfox675-agents-skills-driving-prs-to-merge)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Driving Prs To Merge? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Driving PRs to Merge

After opening any PR, the opener owns driving it to merge. No waiting for a human nudge. The loop exit condition: all required checks green AND every review thread resolved — auto-merge then fires on its own. Everything below exists to get to that state cheaply and honestly.

Project bindings

This skill is project-agnostic. The adopting project defines these in its own CLAUDE.md; refer to them by placeholder throughout.

| Placeholder | Meaning | |---|---| | / | GitHub repository slug | | ` | Branch PRs target (may differ from the repo default branch — see "After merge") | | | Allowed strategy flag (--squash, --merge, --rebase) — and whether a **merge queue** is enabled (changes the command, see below) | | | The full local verification command CI will mirror (lint + typecheck + tests), ideally scoped to changed packages + dependents | | | Where feature branches live on disk (see the dispatching-subagents skill) | | | AI review bot identity (if any), whether it reviews sync or async, and whether the ruleset enables requiredreviewthread_resolution | | | Names of merge-gating CI checks | | | Location of the failure-pattern catalog (maps check-name → likely cause → fix recipe → evidence PR#) | | | Where known-flaky jobs are tracked (e.g. a scheduled workflow posting to a pinned issue); absent → degrade to "retry twice on same SHA" | | | Per-PR preview-environment workflow, if any, and its **path filter** | | | Acknowledgment labels/annotations that breaking-change gates honor | | | Generated files (lockfiles, operation manifests) + their regeneration commands | | | Shared append-only files (changelogs, learnings logs) that conflict trivially | | | The human-only AI-action mention string (e.g. @claude), if the repo has one | | | Who may force-push feature branches (some environments permission-block sub-agents; orchestrator performs with operator authorization) | | ` | Commit message format; see the dispatching-subagents skill for the canonical definition |

The PR-open ritual

Run all three immediately after opening, as one atomic ritual:

gh pr create --base  --title "" \
  --body "" \
  --assignee "$(gh api user --jq .login)"
gh pr view  --json assignees,labels   # verify assignee landed
gh pr merge  --auto    # merge queue active? OMIT the strategy flag — see below

If the assignee or required labels did not land, repair via the REST API:

gh api -X POST repos///issues//assignees -f "assignees[]="
gh api -X POST repos///issues//labels -f "labels[]="

Never use gh pr edit for repairs — it can exit 0, print a GraphQL deprecation warning, and persist nothing (observed losing a title fix and assignee, leaving a required title check red).

Why each step:

  • Self-assignment at creation is the scoping mechanism when multiple operators/agents run concurrently: gh pr list --assignee @me is how each operator filters their in-flight work. Author is automatic; assignee is what the filter reads. (See the issue-locking skill (gh-issue-locking / jira-issue-locking) for the full multi-operator protocol.)
  • Auto-merge at open means the PR merges the moment gates clear, with no babysitting.
  • Merge-queue caveat (battle-tested): when a merge queue manages `, gh pr merge --auto --squash is *rejected by the queue and the auto-merge enrollment silently drops* — no error appears on the PR; it just never merges. Pass --auto` with no strategy flag. This was the root cause of auto-merge "mysteriously dropping" across many PRs in practice.
  • Re-issue rule: if auto-merge enrollment is rejected at open (e.g. a review requirement not yet satisfiable), it does NOT queue itself for later. Note it in the PR body and explicitly re-run gh pr merge --auto once the blocker clears, or the green PR sits unmerged indefinitely. The command is idempotent — "already queued to merge" is confirmation, not an error.
  • Never --admin past checks or required reviews — the sanctioned alternatives are `` or an honest stop-and-report (see "Meta-gates").

Stuck-PR triage query

When scanning for stuck PRs, use --assignee @me (the ownership signal) as the primary filter. If the agent may have dropped the assignee flag, add a secondary --author sweep to catch assignee-dropped PRs:

gh pr list --repo / --assignee @me --state open \
  --json number,createdAt,mergeStateStatus,statusCheckRollup \
  --jq '[.[] | select(.mergeStateStatus == "DIRTY" or
        ([.statusCheckRollup[]? | select(.conclusion=="FAILURE")] | length > 0))]
        | sort_by(.createdAt) | .[] | {n: .number, age: .createdAt}'

Pre-flight: local gates before any push

Run ` before pushing — via git hooks or manually mid-development. If a hook blocks the push, fix the issue and push again; never bypass with --no-verify`. Bypassing converts a 30-second local catch into lint/type errors landing in CI 10 minutes after the push — the prohibition exists exactly to prevent that loop. Scope the gate to changed packages and their dependents so it stays fast.

One reliability caveat learned the hard way: with heavy pre-push hooks, git push exit 0 is not proof the push landed (hook rejection can surface as exit 0 in background tasks). Verify git ls-remote origin matches local HEAD before claiming "pushed".

Reading PR state: the mergeStateStatus table

gh pr view --json mergeStateStatus,autoMergeRequest,statusCheckRollup

| State | Means | Your action | |---|---|---| | BLOCKED | Waiting on a required check (most commonly CI in progress). Normal. | Wait. If green-but-BLOCKED for 30+ min with zero queue activity, check review threads first — it is almost always unresolved threads, not a broken queue. | | CLEAN | All required checks green; eligible to merge or actively in the queue. Often shows autoMerge=false because the queue consumed the flag. | "CLEAN auto=false" after queue pickup means QUEUED, not broken. gh pr merge --auto replying "already queued" is confirmation. Do not panic-toggle. | | UNKNOWN | Being processed by the merge queue; a merge_group run is in flight. Normal. | Wait. | | DIRTY | Branch conflicts with `. | Run the cheap-first rescue ladder (below). | | BEHIND | Behind base but not conflicting. | Auto-rebase (if configured) or gh pr update-branch handles it. | | UNSTABLE` | A non-required check failed. | A non-required check failed; doesn't block merge by itself. Check whether it's a known advisory (no action) or a real regression (fix it — prioritize if it gates other work). |

autoMergeRequest: null is ambiguous (never armed OR consumed by the queue), and mergeStateStatus can read CLEAN/UNSTABLE while enqueued. Only GraphQL mergeQueueEntry is definitive:

gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){
  repository(owner:$o,name:$r){ pullRequest(number:$n){
    mergeQueueEntry{ state position } autoMergeRequest{ enabledAt } } } }' \
  -f o= -f r= -F n=

CI failure triage — in this order

1. Read the failing job's actual log

gh run view --log-failed (and check-summary UIs) usually show only the aggregate/gate job that failed because an upstream job failed — you diagnose the cascade, not the cause. In one production repo this made a checkout transient look like a coverage failure and burned a full rescue dispatch. Fetch the specific failed job's raw log:

gh run list --branch  --limit 3 --json databaseId,workflowName,conclusion
gh run view  --json jobs --jq '.jobs[] | "\(.name) → \(.conclusion // "running")"'
JOB_ID=$(gh run view  --json jobs --jq '.jobs[] | select(.name == "") | .databaseId')
gh api "repos///actions/jobs/$JOB_ID/logs" | grep -E "FAIL|Error|::error" | head -20

Walk the pipeline chain in dependency order (e.g. deploy → DB bootstrap → smoke → E2E) so you find the FIRST real failure, not a downstream symptom.

2. Classify transient vs real before touching code

Rebasing or "fixing" a flake wastes rebase debt and can introduce conflicts that wouldn't otherwise exist. In order:

  1. Does the same test pass on sibling PRs / `` right now? If yes, lean transient.
  2. Is the job on ``? Mechanical classification rule: a job is flaky iff the same head SHA produced both a failure and a success conclusion. On the list → retry, don't rebase.
  3. Rerun the failed job once. Only investigate code after a second identical failure on the same SHA. One rerun has rescued queue-gating PRs outright.
  4. Known infra transients (e.g. checkout exit-128 "Bad credentials", registry WAF pull blocks misreported as "pull access denied" — not a rate limit, no 429) always clear on rerun; never dispatch a rescue for them. Note: gh run rerun --failed is rejected on superseded or in-progress runs with a misleading "workflow file may be broken" error — use a full rerun.
  5. Stale-failure shadow: a displayed failure can be from a cancelled/superseded run. If a recent SUCCESS run exists for the same SHA, the failure is stale — nothing to fix.

3. Your own behavior change can break specs legitimately

If your PR changes user-facing behavior (e.g. adds a confirm dialog before delete), an existing E2E spec failing on that flow is a real contract change, not a flake: update the spec to walk the new UX in the same PR. Related trap: whole-page scans (e.g. axe accessibility) inside unrelated specs can catch UI your PR added elsewhere on the page — scope the scan to the component that spec actually tests (matching the spec's stated intent); don't suppress the rule.

4. Match against the failure-pattern catalog

Maintain a catalog at `` mapping check-name → likely cause → fix recipe → evidence PR#. Recurring failures recur; pre-classification means a rescue starts at the fix, not the discovery. Seed entries (drawn from a JS/TS project — keep the failure SHAPES, rebuild names and recipes per stack):

  • New-lint-rule cascade — a stricter rule merged to base retroactively breaks every open PR after rebase. Fix: substitute the canonical helpers the rule demands; never disable the rule.
  • Mock-drift cascade — a merged feature changed a signature; tests mocking the old one fail. Fix: update mocks to the new signature, one commit per pattern. Never blanket-skip (the skipped test is often a security gate).
  • Async-timing flake — asserting synchronously after an event that schedules async work (focus/RAF/animation). Fix: await waitFor(() => expect(...)) (JS instance of the shape: assertion races scheduled async work — use your framework's retry-until-true primitive). Retrying forever doesn't fix the race.
  • Stale ephemeral-env data — preview env provisioned before a recent migration/default change. Fix: rebase+push so the fresh deploy re-runs migrations, or add an idempotent UPDATE to the env-bootstrap script aligning reference data with existing assertions.
  • Cold-deploy timeout — first per-PR deploy provisions cold and exceeds timeouts. Fix: rebase (picks up timeout bumps already on base) + retrigger; subsequent deploys reuse state. Not a rescue-agent job.
  • DIRTY-no-failures — see the rescue ladder below.

`` caveat: deploy workflows with path filters will NOT re-trigger on an empty commit if the PR's diff doesn't match the deploy paths (e.g. test-only PRs). Pushing empty commits to "bring the deploy back" is a dead end by design — know the filter.

Review threads: bot review = human review

Treat ` comments identically to human review comments. For each: (1) read it — if a suggestion looks technically wrong, *verify before agreeing or refusing* (no performative agreement, no reflexive dismissal); (2) apply the change OR reply with a reasoned counter; (3) mark the thread resolved. When the ruleset enables requiredreviewthread_resolution`, auto-merge will not fire while any thread is unresolved — the recurring symptom is a fully green PR stuck BLOCKED with zero queue runs, which looks exactly like a broken merge queue and has caused repeated misdiagnoses in practice. Check threads FIRST.

gh api repos///pulls//comments     # all line comments (bot + human)
gh pr view  --json reviews,comments             # review summaries + top-level comments

# gh pr view --json reviewThreads can return EMPTY even when threads exist — use raw GraphQL:
gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){
  repository(owner:$o,name:$r){ pullRequest(number:$n){
    reviewThreads(first:50){ nodes{ id isResolved path
      comments(first:1){ nodes{ author{login} body } } } } } } }' \
  -f o= -f r= -F n=

# After applying or countering, resolve:
gh api graphql -f query='mutation($t:ID!){
  resolveReviewThread(input:{threadId:$t}){ thread{ isResolved } } }' -f t=

Three traps with real incident history:

  • The async-orphan trap. Async bots post threads minutes after PR-open; implementer agents exit before the threads exist, so the orchestrator must sweep open PRs and apply-or-counter-then-resolve on their behalf (see the orchestrating-slots skill for cadence). Worse: a fast PR can auto-merge before the threads post — threads land on the CLOSED PR, and a fix pushed to the merged branch is **orphaned, never reaching `** (observed: a real bug stayed live while its threads showed "resolved"). Before dispatching any thread-fix work, check gh pr view --json state; re-land orphaned fixes via a fresh branch off ` + cherry-pick.
  • Threads are a subset of findings. Some bots put their full priority list and security review only in the PR body summary, which doesn't gate merge — real P1s (including an authz bypass, in one incident) can auto-merge unaddressed if you triage only threads. Read the body summary too.
  • Resolved threads, unpushed fix. An agent can resolve its threads (PR goes green and auto-enqueues) while the actual fix sits committed-but-unpushed — the queue merges pre-fix code under falsely-resolved threads. Verify git -C log origin/..HEAD is empty before trusting resolutions.

Never post `` from any agent — it is a human-only trigger; one AI session invoking another creates unsupervised loops. Enforce mechanically with a permission deny rule matching the mention string in any CLI invocation.

Meta-gates: titles, breaking changes, honest failure

  • Title-format checks can gate merge, and with a merge queue one bad title boots the entire merge group containing it (innocent green PRs get ejected alongside). Example (observed in practice): subjects beginning with 2+ consecutive capitals were rejected, booting three PRs at once. Fix via API title edit (gh api -X PATCH repos///pulls/ -f title=...); the check re-runs on edit.
  • Breaking-change gates (API schema diffs, unsafe migrations) get the sanctioned hatch — `` or an annotated in-file comment with reason — never an admin merge. The hatch leaves an audit trail; the bypass leaves nothing, and without a sanctioned hatch agents under pressure invent unsanctioned ones.
  • Honest-failure escalation: when you genuinely cannot fix a gate (missing secret, no infra access, ambiguous intent), say so explicitly and stop. Every paper-over (--no-verify, skip-checks, deleting tests) converts a visible, attributable failure into invisible debt discovered later without context.

Conflicts and DIRTY rescue — cheap-first ladder

Burn the minimum resource at each rung: an API call before an orchestrator action before an agent slot.

  1. gh pr update-branch --repo / — for DIRTY/BEHIND with zero failing checks. Succeeds (done, no agent) or fails loudly with "Cannot update PR branch due to conflicts" (a real conflict — escalate). This CLI call is fine; the GitHub web "Update branch" button on a conflicted PR is not — it produces a merge nobody verified.
  2. Self-handled trivial conflicts — ` conflicts resolve mechanically by keeping both entries; ` resolve by **taking the integration bran

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.