Babysit PRs
Owns a PR end-to-end through review: ship it, wait for the automatic review round, and if it
isn't already clean, drive fix → reply → resolve → re-review cycles until Greptile reports 5/5
and there are zero open comment threads, keeping the branch mergeable against staging along the
way. Two bots review this repo — Greptile and cubic — and they behave differently; see
"Two reviewers" below. Designed to be run under /loop (no fixed interval — let it self-pace on review latency)
so it survives across multiple wakeups in the same session.
When to use
- The user says "babysit this PR", "keep working the reviews until it's clean", or similar
- As the natural follow-up to
/ship when the user wants the review loop automated rather than
manually re-triggering reviews and answering comments themselves
Inputs
Needs a PR number. If none is given and there's no open PR for the current branch, run /ship
first (which includes the origin/staging sync check — see .agents/skills/ship/SKILL.md) to
create one.
Two reviewers
Both post inline threads that count toward "clean", and they need re-triggering separately:
|
Greptile (greptile-apps) |
cubic (cubic-dev-ai) |
| Verdict |
Confidence Score: X/5 in a summary comment |
no score — only inline threads |
| Summary comment |
edited in place across rounds |
fresh review per run |
| Re-trigger |
@greptile |
@cubic-dev-ai review this PR |
| Latency |
1–3 min |
1–3 min |
Post both after every push, as two separate comments. Triggering only Greptile is the easy
mistake: the PR then shows 5/5 with cubic's threads still open from an earlier commit, and its
findings never get re-checked against the fix.
@cubic-dev-ai review this PR is the documented wording — @cubic alone does not trigger it.
cubic reviews the commit that was HEAD when its run started, so a thread can describe code the
next commit already changed. Before treating a cubic finding as real, check whether the current
HEAD still has the problem — a stale round is a reply-and-resolve, not a fix.
Definition of "clean"
All three must hold:
- The latest Greptile summary comment reports Confidence Score: 5/5
reviewThreads (GraphQL, see below) has zero threads with isResolved: false, from
either bot
- Every check has finished and passed —
gh pr checks <n> shows no fail and no
pending. A red run is not clean no matter what the reviewers say, and the lint/audit jobs
routinely catch what a local run misses. A pending one is not clean either: it has not
reported yet, and treating "not failing" as "passing" reports the PR clean before CI has
had its say. Wait for it — the step-10 stop condition covers a check that never settles.
Do not stop early on "no new comments this round" alone — a thread can be open from an earlier
round, and cubic often lands its first threads a round after Greptile's. Always check all three
conditions freshly after every push.
Loop
Check current state before doing anything, including whether the PR is still mergeable:
gh pr view <n> --json mergeable
gh pr checks <n> | grep -v skipping
gh pr view <n> --json comments -q '[.comments[] | select(.author.login=="greptile-apps")] | last | .body'
gh api graphql -f query='
query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <n>) {
reviewThreads(first: 50) { pageInfo { hasNextPage endCursor } nodes { id isResolved path line
comments(first: 5) { nodes { id databaseId author { login } body } } } } } } }'
The score is a line inside the body of Greptile's latest comment (| last | .body), which
it edits in place across rounds.
reviewThreads(first: 50) is a single page — check pageInfo.hasNextPage. If true, don't
stop yet: re-run the same query with after: "<endCursor>" and keep paging until
hasNextPage is false before evaluating "clean." A PR with more than 50 threads is rare but
stopping on a partial page would silently miss unresolved ones past the cutoff.
The query returns both bots' threads. A ReviewThread has no author of its own — identity
lives on its comments, so read the opener's at comments.nodes[0].author.login and do not
add an author field at the thread level, which makes the query fail to compile.
If mergeable is CONFLICTING, fix that first (step 2). If a check is failing, fix that too
— treat it exactly like a review finding. If a check is still pending, do not evaluate
"clean" at all: go to step 9 and wait for it. Otherwise, if Greptile is 5/5, every thread
across all pages has isResolved: true, and every check has finished and passed, stop —
report the outcome (see "Reporting" below) and skip the rest of this list.
If the PR has a merge conflict, merge origin/staging, resolve the conflicts, run the
usual pre-push checks, push, and go to step 8 to re-trigger review.
If no review has run yet (fresh PR, no bot comments): both run automatically on PR open —
confirm via gh pr checks <n> (look for Greptile Review and cubic · AI code reviewer) and
wait for both before doing anything else. They finish at different times, so a PR that looks
clean because only one has reported is not clean yet.
If a review round has landed and it isn't clean: for every thread where
isResolved: false, triage the finding on its own merits — this is the part that requires
judgment, not a mechanical loop:
- Real bug: fix it in the cleanest way available. Match the codebase's existing
conventions for that kind of problem before inventing a new one (e.g. an SSRF-prone
user-supplied-host fetch should use whatever
validateUrlWithDNS/secureFetchWithPinnedIP
pattern the rest of the codebase already uses for that exact situation — grep for a sibling
integration solving the same problem first). Never patch around a finding with a
workaround, a broad try/catch, or a suppression comment — fix the actual cause.
- False positive: don't change code. Reply with the specific reason it doesn't apply
(cite the type definition, the established pattern it matches, or the doc it follows) so
the reviewer bot and a human skimming later both understand why it was left as-is.
- Already fixed by an earlier finding in the same round: note that and resolve without a
duplicate code change.
Reply to every thread individually before resolving it — never resolve silently:
gh api repos/<owner>/<repo>/pulls/<n>/comments/<databaseId>/replies -f body="<what was done and why>"
Then resolve via GraphQL (needs the thread id from step 1, not the comment id):
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<threadId>"}) { thread { isResolved } } }'
Before pushing, re-run the full sync check from /ship step 2 — not just the log command,
the whole check-and-recover flow (stash WIP if needed, rebase, verify the rebase didn't just
cleanly replay stray commits, cherry-pick rebuild if it did or if it conflicted). A babysit
loop spanning a long session is exactly the scenario where a branch can drift, and pushing
review fixes on top of undetected drift is how an oversized PR happens even after the branch
was fixed once. Then run the repo's pre-ship checks the same way /ship does before
committing — not just lint/typecheck/boundary-validation, but also the conditional /cleanup
(if this round's fix touched UI code) and /db-migrate (if it touched schema/migrations)
gates from /ship steps 4 and 5. A review-fix round is still a code change and can trip
either gate just as easily as the original commit did.
Commit and push the round's fixes as one commit — --force-with-lease whenever step 6's
sync check rewrote history, which includes a plain git rebase origin/staging that completed
with no conflicts, not only the cherry-pick rebuild path; both rewrite commits already
published to the remote, so a plain git push can be rejected either way — then run /ship
step 9's post-push verify — not just before the first push, every push in the loop:
git fetch origin staging && git log --oneline --reverse origin/staging..HEAD
gh pr view <n> --json commits -q '.commits[].messageHeadline'
These two lists must describe the same commits. A review loop runs many pushes across many
rounds; checking sync only before the push (step 6) and never after is how a bad push or a
PR whose commit history quietly went stale between rounds goes unnoticed.
Re-trigger both reviewers, each as its own PR comment — a combined comment does not
reliably trigger both:
gh pr comment <n> --body "@greptile"
gh pr comment <n> --body "@cubic-dev-ai review this PR"
Then confirm both actually picked it up before waiting — gh pr checks <n> should show
Greptile Review and cubic · AI code reviewer as pending. If one stayed pass from the
previous round, its trigger did not land; re-post that one.
Wait for the new round, then go back to step 1. Pace the wait with ScheduleWakeup using
a fallback delay of ~300s — both bots take 1–3 minutes, and CI is usually the slowest of the
three — never busy-poll in a sleep loop. Pass the same /loop babysit PR <n> prompt on each wakeup so the loop
resumes correctly.
Stop conditions: clean state reached (see above), or the same unresolved finding or
merge conflict survives two consecutive rounds with no new information (surface it to the
user instead of looping forever), or the user interrupts.
Reporting
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each),
what was pushed back on as a false positive and why, and the final state — Greptile score, open
thread count across both bots, and whether every check finished and passed.
Public-repo hygiene
Every reply, comment and commit you post here is public and permanent, and review bots quote
your replies back, so a leak propagates. /ship's "What to Omit" (the category list and the
pre-publish grep) applies to every post in this loop. Triaging a finding often means pasting
evidence gathered from prod — that is exactly the moment it gets violated. Run the grep on the
reply before posting, not after: editing a comment does not unsend its notification email.
Hard rules
- Never paste prod evidence into a reply without scrubbing it first (see above).
- Never resolve a thread without replying to it first.
- Never fix a finding with a hacky workaround — if the clean fix isn't obvious, find the sibling
pattern elsewhere in the codebase solving the same class of problem and match it.
- Never silently drop a finding — every thread gets either a code fix or a reasoned reply.
- Never re-trigger only one reviewer. Both get a comment after every push, and both get confirmed
pending before you start waiting.
- Always re-run the
/ship-style sync check before every push in the loop, not just the first.
1---2name: babysit3description: Drive a PR to a clean review (Greptile 5/5, zero open threads) — ships if needed, keeps it mergeable against staging, re-triggers both Greptile and cubic, fixes real findings, replies to and resolves every thread, and loops until clean4---5
6# Babysit PRs
7
8Owns a PR end-to-end through review: ship it, wait for the automatic review round, and if it
9isn't already clean, drive fix → reply → resolve → re-review cycles until Greptile reports 5/5
10and there are zero open comment threads, keeping the branch mergeable against staging along the
11way. Two bots review this repo — Greptile and cubic — and they behave differently; see
12"Two reviewers" below. Designed to be run under `/loop` (no fixed interval — let it self-pace on review latency)
13so it survives across multiple wakeups in the same session.
14
15## When to use
16
17- The user says "babysit this PR", "keep working the reviews until it's clean", or similar
18- As the natural follow-up to `/ship` when the user wants the review loop automated rather than
19 manually re-triggering reviews and answering comments themselves
20
21## Inputs
22
23Needs a PR number. If none is given and there's no open PR for the current branch, run `/ship`
24first (which includes the `origin/staging` sync check — see `.agents/skills/ship/SKILL.md`) to
25create one.
26
27## Two reviewers
28
29Both post inline threads that count toward "clean", and they need re-triggering separately:
30
31| | Greptile (`greptile-apps`) | cubic (`cubic-dev-ai`) |
32|---|---|---|
33| Verdict | `Confidence Score: X/5` in a summary comment | no score — only inline threads |
34| Summary comment | edited in place across rounds | fresh review per run |
35| Re-trigger | `@greptile` | `@cubic-dev-ai review this PR` |
36| Latency | 1–3 min | 1–3 min |
37
38Post **both** after every push, as two separate comments. Triggering only Greptile is the easy
39mistake: the PR then shows 5/5 with cubic's threads still open from an earlier commit, and its
40findings never get re-checked against the fix.
41
42`@cubic-dev-ai review this PR` is the documented wording — `@cubic` alone does not trigger it.
43
44cubic reviews the commit that was HEAD when its run started, so a thread can describe code the
45next commit already changed. Before treating a cubic finding as real, check whether the current
46HEAD still has the problem — a stale round is a reply-and-resolve, not a fix.
47
48## Definition of "clean"
49
50All three must hold:
511. The latest Greptile summary comment reports **Confidence Score: 5/5**
522. `reviewThreads` (GraphQL, see below) has **zero threads with `isResolved: false`**, from
53 either bot
543. Every check has **finished and passed** — `gh pr checks <n>` shows no `fail` *and* no
55 `pending`. A red run is not clean no matter what the reviewers say, and the lint/audit jobs
56 routinely catch what a local run misses. A `pending` one is not clean either: it has not
57 reported yet, and treating "not failing" as "passing" reports the PR clean before CI has
58 had its say. Wait for it — the step-10 stop condition covers a check that never settles.
59
60Do not stop early on "no new comments this round" alone — a thread can be open from an earlier
61round, and cubic often lands its first threads a round after Greptile's. Always check all three
62conditions freshly after every push.
63
64## Loop
65
661. **Check current state** before doing anything, including whether the PR is still mergeable:
67 ```bash
68 gh pr view <n> --json mergeable
69 gh pr checks <n> | grep -v skipping
70 gh pr view <n> --json comments -q '[.comments[] | select(.author.login=="greptile-apps")] | last | .body'
71 gh api graphql -f query='
72 query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <n>) {
73 reviewThreads(first: 50) { pageInfo { hasNextPage endCursor } nodes { id isResolved path line
74 comments(first: 5) { nodes { id databaseId author { login } body } } } } } } }'
75 ```
76 The score is a line inside the body of Greptile's *latest* comment (`| last | .body`), which
77 it edits in place across rounds.
78 `reviewThreads(first: 50)` is a single page — check `pageInfo.hasNextPage`. If `true`, don't
79 stop yet: re-run the same query with `after: "<endCursor>"` and keep paging until
80 `hasNextPage` is `false` before evaluating "clean." A PR with more than 50 threads is rare but
81 stopping on a partial page would silently miss unresolved ones past the cutoff.
82 The query returns both bots' threads. A `ReviewThread` has no author of its own — identity
83 lives on its comments, so read the opener's at `comments.nodes[0].author.login` and do not
84 add an `author` field at the thread level, which makes the query fail to compile.
85 If `mergeable` is `CONFLICTING`, fix that first (step 2). If a check is failing, fix that too
86 — treat it exactly like a review finding. If a check is still `pending`, do not evaluate
87 "clean" at all: go to step 9 and wait for it. Otherwise, if Greptile is 5/5, every thread
88 across all pages has `isResolved: true`, and every check has finished and passed, stop —
89 report the outcome (see "Reporting" below) and skip the rest of this list.
90
912. **If the PR has a merge conflict**, merge `origin/staging`, resolve the conflicts, run the
92 usual pre-push checks, push, and go to step 8 to re-trigger review.
93
943. **If no review has run yet** (fresh PR, no bot comments): both run automatically on PR open —
95 confirm via `gh pr checks <n>` (look for `Greptile Review` and `cubic · AI code reviewer`) and
96 wait for both before doing anything else. They finish at different times, so a PR that looks
97 clean because only one has reported is not clean yet.
98
994. **If a review round has landed and it isn't clean**: for every thread where
100 `isResolved: false`, triage the finding on its own merits — this is the part that requires
101 judgment, not a mechanical loop:
102 - **Real bug**: fix it in the cleanest way available. Match the codebase's existing
103 conventions for that kind of problem before inventing a new one (e.g. an SSRF-prone
104 user-supplied-host fetch should use whatever `validateUrlWithDNS`/`secureFetchWithPinnedIP`
105 pattern the rest of the codebase already uses for that exact situation — grep for a sibling
106 integration solving the same problem first). Never patch around a finding with a
107 workaround, a broad try/catch, or a suppression comment — fix the actual cause.
108 - **False positive**: don't change code. Reply with the specific reason it doesn't apply
109 (cite the type definition, the established pattern it matches, or the doc it follows) so
110 the reviewer bot and a human skimming later both understand why it was left as-is.
111 - **Already fixed by an earlier finding in the same round**: note that and resolve without a
112 duplicate code change.
113
1145. **Reply to every thread individually** before resolving it — never resolve silently:
115 ```bash
116 gh api repos/<owner>/<repo>/pulls/<n>/comments/<databaseId>/replies -f body="<what was done and why>"
117 ```
118 Then resolve via GraphQL (needs the thread `id` from step 1, not the comment id):
119 ```bash
120 gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<threadId>"}) { thread { isResolved } } }'
121 ```
122
1236. **Before pushing, re-run the full sync check from `/ship` step 2** — not just the log command,
124 the whole check-and-recover flow (stash WIP if needed, rebase, verify the rebase didn't just
125 cleanly replay stray commits, cherry-pick rebuild if it did or if it conflicted). A babysit
126 loop spanning a long session is exactly the scenario where a branch can drift, and pushing
127 review fixes on top of undetected drift is how an oversized PR happens even after the branch
128 was fixed once. Then run the repo's pre-ship checks the same way `/ship` does before
129 committing — not just lint/typecheck/boundary-validation, but also the conditional `/cleanup`
130 (if this round's fix touched UI code) and `/db-migrate` (if it touched schema/migrations)
131 gates from `/ship` steps 4 and 5. A review-fix round is still a code change and can trip
132 either gate just as easily as the original commit did.
133
1347. **Commit and push** the round's fixes as one commit — `--force-with-lease` whenever step 6's
135 sync check rewrote history, which includes a plain `git rebase origin/staging` that completed
136 with no conflicts, not only the cherry-pick rebuild path; both rewrite commits already
137 published to the remote, so a plain `git push` can be rejected either way — then run `/ship`
138 step 9's post-push verify — not just before the first push, every push in the loop:
139 ```bash
140 git fetch origin staging && git log --oneline --reverse origin/staging..HEAD
141 gh pr view <n> --json commits -q '.commits[].messageHeadline'
142 ```
143 These two lists must describe the same commits. A review loop runs many pushes across many
144 rounds; checking sync only before the push (step 6) and never after is how a bad push or a
145 PR whose commit history quietly went stale between rounds goes unnoticed.
146
1478. **Re-trigger both reviewers**, each as its own PR comment — a combined comment does not
148 reliably trigger both:
149 ```bash
150 gh pr comment <n> --body "@greptile"
151 gh pr comment <n> --body "@cubic-dev-ai review this PR"
152 ```
153 Then confirm both actually picked it up before waiting — `gh pr checks <n>` should show
154 `Greptile Review` and `cubic · AI code reviewer` as `pending`. If one stayed `pass` from the
155 previous round, its trigger did not land; re-post that one.
156
1579. **Wait for the new round**, then go back to step 1. Pace the wait with `ScheduleWakeup` using
158 a fallback delay of ~300s — both bots take 1–3 minutes, and CI is usually the slowest of the
159 three — never busy-poll in a sleep loop. Pass the same `/loop babysit PR <n>` prompt on each wakeup so the loop
160 resumes correctly.
161
16210. **Stop conditions**: clean state reached (see above), or the same unresolved finding or
163 merge conflict survives two consecutive rounds with no new information (surface it to the
164 user instead of looping forever), or the user interrupts.
165
166## Reporting
167
168When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each),
169what was pushed back on as a false positive and why, and the final state — Greptile score, open
170thread count across both bots, and whether every check finished and passed.
171
172## Public-repo hygiene
173
174Every reply, comment and commit you post here is public and permanent, and review bots quote
175your replies back, so a leak propagates. `/ship`'s "What to Omit" (the category list and the
176pre-publish grep) applies to every post in this loop. Triaging a finding often means pasting
177evidence gathered from prod — that is exactly the moment it gets violated. Run the grep on the
178reply before posting, not after: editing a comment does not unsend its notification email.
179
180## Hard rules
181
182- Never paste prod evidence into a reply without scrubbing it first (see above).
183- Never resolve a thread without replying to it first.
184- Never fix a finding with a hacky workaround — if the clean fix isn't obvious, find the sibling
185 pattern elsewhere in the codebase solving the same class of problem and match it.
186- Never silently drop a finding — every thread gets either a code fix or a reasoned reply.
187- Never re-trigger only one reviewer. Both get a comment after every push, and both get confirmed
188 `pending` before you start waiting.
189- Always re-run the `/ship`-style sync check before every push in the loop, not just the first.