d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
132 lines
7.7 KiB
Markdown
132 lines
7.7 KiB
Markdown
# Babysit Command
|
||
|
||
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.
|
||
|
||
## 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 `.cursor/commands/ship.md`) to
|
||
create one.
|
||
|
||
## Definition of "clean"
|
||
|
||
Both must hold:
|
||
1. The latest Greptile summary comment reports **Confidence Score: 5/5**
|
||
2. `reviewThreads` (GraphQL, see below) has **zero threads with `isResolved: false`**
|
||
|
||
Do not stop early on "no new comments this round" alone — a thread can be open from an earlier
|
||
round. Always check both conditions freshly after every push.
|
||
|
||
## Loop
|
||
|
||
1. **Check current state** before doing anything:
|
||
```bash
|
||
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 } } } } } } }'
|
||
```
|
||
`[.comments[]] | last | .body`, not `... | .body | tail -1` — the latter pipes every matching
|
||
comment's full multi-line body through the pipeline and keeps only the final *line* of that
|
||
combined output (usually the "Reviews (n): Last reviewed commit..." footer), not the last
|
||
*comment*, so it silently misses the actual "Confidence Score: X/5" line.
|
||
`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.
|
||
If Greptile is 5/5 and every thread across all pages has `isResolved: true`, stop — report the
|
||
outcome (see "Reporting" below) and skip the rest of this list.
|
||
|
||
2. **If no review has run yet** (fresh PR, no Greptile/Cursor comments): they usually run
|
||
automatically on PR open — confirm via `gh pr checks <n>` (look for `Cursor Bugbot` /
|
||
`Greptile Review`) and wait for that first round before doing anything else.
|
||
|
||
3. **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.
|
||
|
||
4. **Reply to every thread individually** before resolving it — never resolve silently:
|
||
```bash
|
||
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):
|
||
```bash
|
||
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<threadId>"}) { thread { isResolved } } }'
|
||
```
|
||
|
||
5. **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 — not just lint/typecheck/boundary-
|
||
validation, but also the conditional `/cleanup` (UI changes) and `/db-migrate`
|
||
(schema/migration changes) gates from `.agents/skills/ship/SKILL.md` steps 4 and 5 (Cursor's
|
||
own 7-step `ship.md` doesn't carry those steps, but a review-fix round is still a code change
|
||
and can trip either gate just as easily as the original commit did).
|
||
|
||
6. **Commit and push** the round's fixes as one commit — `--force-with-lease` whenever step 5'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 7's post-push verify — not just before
|
||
the first push, every push in the loop (the Cursor `/ship` has 7 steps; the Claude Code skill
|
||
version's equivalent is step 9 — see `.agents/skills/babysit/SKILL.md` if working from that copy):
|
||
```bash
|
||
git fetch origin staging && git log --oneline --reverse origin/staging..HEAD
|
||
gh pr view <n> --json commits -q '.commits[].messageHeadline'
|
||
```
|
||
`--reverse` matches `git log`'s newest-first default to the PR commit list's oldest-first
|
||
order — without it a positional comparison can spuriously fail on any multi-commit branch.
|
||
These two lists must describe the same commits. A review loop runs many pushes across many
|
||
rounds; checking sync only before the push (step 5) and never after is how a bad push or a
|
||
PR whose commit history quietly went stale between rounds goes unnoticed.
|
||
|
||
7. **Re-trigger review** by posting `@greptile` and `@cursor review` as **two separate PR
|
||
comments** — never combine them into one comment, each bot only responds to its own mention:
|
||
```bash
|
||
gh pr comment <n> --body "@greptile"
|
||
gh pr comment <n> --body "@cursor review"
|
||
```
|
||
|
||
8. **Wait for the new round**, then go back to step 1. Never busy-poll in a sleep loop — pace the
|
||
wait to roughly how long Greptile/Cursor take (1–3 minutes).
|
||
|
||
9. **Stop conditions**: clean state reached (see above), or the same unresolved finding 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 Greptile score / thread count.
|
||
|
||
## Hard rules
|
||
|
||
- Never post the two re-review mentions as a single combined comment.
|
||
- 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.
|
||
- Always re-run the `/ship`-style sync check before every push in the loop, not just the first.
|