Install
$ agentstack add skill-skyfox675-agents-skills-driving-prs-to-merge ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
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 →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 @meis 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 --squashis *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 --autoonce the blocker clears, or the green PR sits unmerged indefinitely. The command is idempotent — "already queued to merge" is confirmation, not an error. - Never
--adminpast 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:
- Does the same test pass on sibling PRs / `` right now? If yes, lean transient.
- 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.
- 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.
- 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 --failedis rejected on superseded or in-progress runs with a misleading "workflow file may be broken" error — use a full rerun. - 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, checkgh 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/..HEADis 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.
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.- 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.
- Author: skyfox675
- Source: skyfox675/agents-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.