Babysit PR
Drive an open pull request to a merge-ready state. This may run for hours — work autonomously in a loop until done or blocked.
Invocation
Examples:
/babysit-pr on #1034Babysit PR #1034 until merge-readyBabysit this PR(resolve from current branch)/babysit-pr --merge #124 #125 #126(shepherd and merge, in queue order)
One babysitter for many PRs beats one babysitter per PR — they share a single CodeRabbit rate limit, so a single session drains the queue without multiplying review-limit stalls.
Hard rules
- Never approve the PR. Never merge unless invoked with
--merge— the flag is the explicit authorization to merge; without it, only the human owner merges. - Lint before every commit — read and follow the
lintskill (uv run lintro fmtthenuv run lintro chk, zero issues). Do not use--toolsfiltering. - Never push while CI is pending or running on the PR head.
- Minimal diffs — fix only what the PR scope and valid review/CI feedback require.
- Never weaken CI/workflows just to make checks pass; report instead if that seems necessary.
- Never force-push, amend pushed commits, or rewrite history unless the user explicitly requested it.
Composed skills
lint— pre-commit formatting and checks (required gate).commit— conventional commits, signed commits, semantic prefixes.gh-fix-ci(if available) — inspect failing GitHub Actions checks and logs.
Do not duplicate their full workflows here — read and follow them at commit time.
Phase 0 — Resolve target PR
PR number: Extract from the user message (
#123, URL) or resolve from the current branch:gh pr view --json number,url,title,headRefName,baseRefName,state,mergeable,mergeStateStatusRepository: Use
gh repo view --json nameWithOwnerfrom the worktree, or parse from a PR URL.Worktree: Prefer a path from conversation context. Otherwise scan worktrees:
git worktree listMatch
headRefNameto a worktree path. If none exists, use the current git root only when it matches the resolved PR branch. Otherwise stop and ask for the correct worktree before continuing.Load repo context (after the target worktree is known): From that worktree root only, read
AGENTS.mdand/orCLAUDE.mdif present. Do not read context from the parent checkout or any other repo. Precedence:AGENTS.mdis authoritative when both exist — do not also apply conflictingCLAUDE.mdinstructions; if only one exists, use that file; if both conflict in a blocking way, stop and ask. Treat house standards, operating agreement (including merge policy / merge-queue notes), and standing constraints as binding for this babysit run. See thestand-generalskill's Per-repo agent context section for the expected file shape. Missing the file is fine — fall back to chat instructions and this skill's defaults.Snapshot before looping: PR URL, branch, worktree path, latest commit SHA, check summary, open review threads.
Phase 1 — Launch background sub-agent (if supported)
Babysitting is long-running. After Phase 0, if the agent supports background sub-agents (e.g. Claude Code's general-purpose agent run in the background), launch one and pass it the resolved PR metadata, worktree path, hard rules, and this skill's main loop (Phase 2 onward). In that mode, the parent agent returns immediately with the sub-agent link and handoff snapshot. The sub-agent runs until exit conditions are met or a human blocker is found.
Portability note: on agents without background sub-agents, skip the handoff, tell the user the babysitting loop will occupy the current session, and run the main loop (Phase 2 onward) inline instead.
Phase 2 — Main loop
Repeat until exit conditions (Phase 5) or a blocker. Track loop iterations and elapsed time; stop and report if more than 20 iterations or 6 hours pass without reaching exit conditions (avoids unbounded token/API cost on flaky CI or re-triggering bots).
Step A — Wait for CI
gh pr checks <number> --repo <owner/repo>
If any check is pending or in_progress, wait and re-poll. Re-poll every 60
seconds; after five consecutive pending polls, double the interval up to 5 minutes.
Do not push during this window.
Step B — Merge conflicts
If mergeable is CONFLICTING, resolve intelligently in the worktree preserving branch
intent. If intents conflict, stop and report — do not guess.
If the branch is behind base and failures look unrelated, merge onto latest base per repo convention. Only rebase/cherry-pick if the user explicitly approves rewriting history.
Step C — Fix CI failures
For each failing GitHub Actions check:
- Get check status via
gh pr checks --json; fetch logs separately withgh run view <run_id> --log. - Fix failures within PR scope in the worktree.
- Run project tests if applicable (follow the
testskill or repo convention). - Lint gate → commit (follow
commitskill) → push. - Return to Step A.
For external CI providers (non-GitHub Actions), report the details URL only.
Step D — Triage review comments
Fetch unresolved review threads and recent bot comments:
gh api graphql -f query='...' # or gh pr view --comments, issue comment APIs
Sources: Greptile, CodeRabbit (coderabbitai[bot]), Bugbot, human reviewers.
For each actionable thread:
- Read the comment and the cited code location.
- Verify against current code — skip or reply N/A if outdated or already fixed.
- Fix valid issues with minimal diffs; lint → commit → push (after Step A allows).
- Reply briefly on invalid/outdated findings (one short paragraph, no argument).
- Resolve the thread when the platform supports it and the issue is addressed.
Greptile: Often posts a summary comment plus inline findings — treat inline items like review threads.
Bugbot: Validate carefully; only fix real bugs. Explain when disagreeing.
CodeRabbit: See Step E for rate limits; otherwise triage like other bots.
When reading GitHub API output, fetch only comment bodies and locations needed — do not load entire JSON payloads into context.
Step E — CodeRabbit review cycle (mandatory)
CodeRabbit is not done when its GitHub check says "Review completed" on an older commit. The babysit loop must cover the current PR head.
Zero unresolved threads is not evidence of review — it is also what an unstarted
review looks like. CodeRabbit posts no check-run on the head SHA, so an in-flight
review is invisible to gh pr checks. Positive evidence means a CodeRabbit review
submission whose commit_id equals the current headRefOid; a summary/walkthrough/
skip comment counts only when its body cites the current head SHA and it
postdates the head's arrival on GitHub — comments carry no commit_id, so a bare
timestamp match is also what a late review of the previous head looks like.
Record the latest commit SHA and when it arrived on GitHub (
gh pr view --json headRefOid; then the earliest Actions run on that SHA):gh api --paginate "repos/<owner>/<repo>/actions/runs?head_sha=<sha>" \ --jq '[.workflow_runs[] | select(.head_branch == "<head-branch>")] | map(.created_at) | min'(The list is newest-first;
--paginatewalks every page so the min is global, and thehead_branchfilter keeps the probe scoped to this PR's branch.)(
Commit.pushedDateis null for most PR head commits, and.commit.committer.dateis the local creation time — neither is the push time. The first Actions run starts within seconds of the push, which is accurate enough for the freshness checks and settle windows below.) If no runs exist yet, snapshot the current time once per head SHA and reuse that frozen value for every later comparison — re-sampling "now" on each re-fetch would make those windows unmeetable.Gather all CodeRabbit evidence — issue comments and review submissions (a review carries the
commit_idit reviewed; paginate both lists):gh api --paginate repos/<owner>/<repo>/issues/<number>/comments \ --jq '.[] | select(.user.login == "coderabbitai[bot]") | {id, created_at, body}' gh api --paginate repos/<owner>/<repo>/pulls/<number>/reviews \ --jq '.[] | select(.user.login == "coderabbitai[bot]") | {id, commit_id, submitted_at, state}'Match the exact
coderabbitai[bot]identity — a substring match would let look-alike accounts satisfy the gate. A comment is evidence only if its body cites the current head SHA; otherwise it is corroboration at best.Rate-limited (
Review limit reached/Next review available in) — a known state, distinct from an in-flight review. The rate-limit comment must postdate the current head's push (CodeRabbit attempted this head and hit the limit); a stale rate-limit comment from an older head is not evidence — treat the head as unreviewed (item 4).- Do not sleep or poll for the reset — the rate limit does not block exit.
- If unpushed commits exist: push per Step A discipline (CI quiescent first), then return to the loop.
- Record that CodeRabbit review of the current head is pending due to the rate limit, and continue toward the Phase 5 exit conditions.
Not rate-limited but head unreviewed (no review submission with
commit_id== the currentheadRefOid, and no SHA-citing summary/walkthrough/skip comment after the head's arrival): first wait ~5 minutes from the push (reviews typically land within ~5 minutes) before concluding anything; if nothing arrives, post@coderabbitai please review, then wait and triage.Do not burn CodeRabbit CLI runs during babysit — PR bot comments are the source of truth.
Repeat Step E after every push until CodeRabbit has reviewed the current head and all resulting threads are triaged.
Step F — Re-check and repeat
After push, return to Step A. Continue until Phase 5 exit conditions.
Phase 3 — Push discipline
Before every push:
- CI quiescent (Step A).
- Lint gate passed.
- Commit signed and conventional.
- CodeRabbit rate limit respected (Step E).
git push origin HEAD
Phase 4 — Human blockers (stop and report)
Stop the loop and report if:
- Merge conflicts you cannot resolve safely.
- CI failures outside PR scope or requiring workflow changes.
- Missing credentials / permissions.
- Ambiguous product or design decisions.
Do not approve the PR to unblock it.
Do not stop solely because REVIEW_REQUIRED or branch protection needs human
approval — that is expected. When all other Phase 5 exit conditions are met, exit
successfully and note pending approval in the final report.
Phase 5 — Exit conditions
Done when all are true:
- All required CI checks green (or only allowed skips like
REVIEW_REQUIRED). - No unresolved actionable Greptile/CodeRabbit/Bugbot threads (fixed or replied).
- CodeRabbit has reviewed the current head, verified positively — a review
submission whose
commit_idequals the currentheadRefOid(primary), or a summary/walkthrough/skip comment whose body cites the current head SHA and that postdates the head's arrival on GitHub, with all threads triaged; zero unresolved threads alone is not evidence — or CodeRabbit is rate-limited for the current head (known state, fresh rate-limit signal) and the pending review is noted in the final report. - No unpushed local commits.
- Branch mergeable (no conflicts).
REVIEW_REQUIRED alone is not a loop blocker — note it in the final report.
Merging (only with --merge)
Skip this entire section unless invoked with --merge. Without the flag, exit at
Phase 5 and let the human owner merge.
Capability check (first)
Before the first merge, detect what the repository provides:
- Merge queue / auto-merge enabled? —
gh repo view --json autoMergeAllowed, ruleset inspection (gh api repos/<owner>/<repo>/rulesets), or a note in the repo'sCLAUDE.md. - Conversation resolution required? — branch protection / ruleset settings.
If the repo has a merge queue or auto-merge, use queue-aware mode. Otherwise fall back to manual serial mode. The per-PR gate, release handling, failure signatures, and signing-park behavior below apply to both modes.
Per-PR gate (both modes)
Before merging (or enqueuing) any single PR:
- Phase 5 exit conditions met for that PR.
- Bot reviewed the CURRENT head, verified positively at gate time — re-gather the
evidence (Step E item 2): a review submission whose
commit_idequals the currentheadRefOid(primary), or a summary/walkthrough/skip comment citing that head SHA and postdating its arrival on GitHub; if absent, wait ~5 minutes from the push, then re-request (@coderabbitai please review) and wait before merging — or CodeRabbit is rate-limited for the current head (known state, fresh rate-limit signal) and the pending review is noted in the final report. - Settle window — always wait
arrival + ~5 minfrom the head's arrival before merging, except once positive head evidence exists, where the wait becomes the evidence's own quiet period: a matching review submission waitssubmitted_at + ~2 min; a SHA-citing comment waits itscreated_at + ~2 min; a fresh rate-limit signal has no submission to wait on. A commit_id-matched review is never required to postdate arrival — an identical SHA means identical reviewed content, so the frozen-now arrival path keeps working. - Re-check PR state immediately before merge — re-fetch
headRefOidand its arrival time (reusing the frozen null-fallback snapshot while the SHA is unchanged); if the head changed since Phase 5 (or the evidence does not postdate the current head arrival), rerun Step E before merging or enqueuing. An already-merged PR is a normal outcome, not an error; re-baseline the queue and move on.
Post-merge review backstop (both modes)
If a merge nonetheless raced a review (or exited with a CodeRabbit review pending),
do not sweep immediately — an instant sweep sees the same empty-thread picture as
pre-merge. Wait ~5 minutes (or until a post-merge CodeRabbit review lands, whichever
comes first), polling with a couple of bounded probes, then sweep the merged PR:
triage late-landing CodeRabbit threads — real findings become fix-forward issues,
invalid ones get a disposition reply and thread resolution. If still in session, a
review landing after that sweep is triaged too; otherwise it is left to the
sweep-prs fleet sweep — late findings must never be silently orphaned.
Queue-aware mode (primary, when available)
Per PR: resolve all review threads (fix or refute — Step D unchanged), get checks green, then enqueue and observe:
gh pr merge <n> --auto --squash --delete-branch
Never pass --subject or --body to gh pr merge (either mode). Repo squash
defaults produce the correct commit on their own: PR title + auto-appended (#N) +
blank body. An explicit --subject suppresses the (#N) append (seen: py-lintro
#1916/#1922 landed numberless); a custom --body trips commitlint
body-max-line-length on main's dogfood. "Squash with PR title and blank body"
means rely on the defaults, not pass them as flags.
The platform serializes merges, rebases each PR, merges when its turn comes, and blocks on unresolved threads. Do not re-implement that machinery: no manual main-green waiting between merges, no single-merger lock, no hand-rolled ordering. Thread resolution is the irreducible judgment step and stays with the babysitter; the mechanical serialization belongs to the platform. Keep observing until each enqueued PR actually merges (or is ejected from the queue — then triage why).
Never use --admin in this mode — it uses administrator privileges against the
whole merge requirement set (reviews, required checks, queue enrollment,
blocked/behind state), so it bypasses queue enrollment entirely or masks a genuine
failure. If the merge fails solely because of the self-approval restriction, that
is a human blocker: stop and report it (see Phase 4) so the human owner reviews and
merges — do not reach for --admin to push past branch protection.
Manual serial mode (fallback — no merge queue / auto-merge)
Merge command:
gh pr merge <n> --squash --admin --delete-branch
Guardrails for --admin here: use it only when the owner has explicitly granted
merge authority for the listed PRs (the --merge invocation naming them); the admin
bypass clears the review requirement only — all required checks must be genuinely
green (never skipped or forced). --admin stays forbidden in queue-aware mode, where
it would bypass queue enrollment.
Queue discipline (this mode only):
- Single merger — before starting, verify no other session is draining the same queue. One merger at a time.
- Strictly sequential — after each merge, wait for all post-merge runs on
mainto finish before merging the next PR. - A
mainrun fails post-merge → STOP and report. No fix-forward; the queue halts until a human decides. - Order by conflict — merge docs/config-only PRs first, wide-touch refactors last, to minimize rebases.
- Under strict up-to-date-branch policies, use
gh pr update-branch <n>to bring each PR current before its turn.
Releases (both modes)
- Expect auto version PRs to appear after merges.
- If the repo convention is 1 PR = 1 release, merge the release PR and wait for its runs before moving to the next change PR.
- Never touch publish gates (PyPI environment approval, etc.) — those are human gates. Stop and report.
Failure signatures (both modes)
CANCELLED= a duplicate/superseded run; onlyFAILURE/TIMED_OUTare genuine failures.- Stale merge ref (setup fails on infra added to
mainafter the PR was created) →gh pr update-branch; reruns of the old ref fail deterministically. - Pages "Multiple artifacts named github-pages" → delete the run's
github-pagesartifacts via the API, then rerun; partial reruns re-hit this forever. - Registry "manifest unknown" on partial reruns (a cleanup job deleted the run-scoped tags) → rerun the entire run, not just the failed job.
- Signing locked ("failed to write commit object") → park with ~20-minute probes, resume on unlock, and report the blocker once (not on every probe).
Phase 6 — Final report
Return a concise summary:
| Field | Value |
|---|---|
| PR | URL, title, number |
| Branch | name @ final SHA |
| CI | pass/fail per check |
| Commits pushed | list (short) |
| Threads handled | fixed / replied N/A |
| CodeRabbit | reviewed current head / review pending (rate-limited) |
| Merge-ready? | yes/no + why |
| PRs merged | list (or n/a without --merge) |
| Human blockers | approval, decisions, etc. |
Notes
- Differs from the built-in Cursor
babysitskill: this workflow is PR-parameterized, Greptile/CodeRabbit-aware, and enforces the lint gate and CodeRabbit rate-limit loop. - Stacked branches on py-lintro sometimes need cherry-pick onto
main, not a blind rebase — prefer conversation context or ask before rewriting history. - See also
backlog: the interactive routing entry point that composes this skill withimplement-issues. It does not auto-invoke either. - See also
sweep-prs: the periodic, fleet-wide complement — where this skill watches the PR it's actively merging,sweep-prsretrospectively audits everything else across repos.