Skip to content

Commit daf4217

Browse files
fix(babysit): trim merge-conflict handling to essentials
1 parent f6f4c94 commit daf4217

3 files changed

Lines changed: 72 additions & 195 deletions

File tree

.agents/skills/babysit/SKILL.md

Lines changed: 24 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ description: Drive a PR to a clean review (Greptile 5/5, zero open threads) —
77

88
Owns a PR end-to-end through review: ship it, wait for the automatic review round, and if it
99
isn't already clean, drive fix → reply → resolve → re-review cycles until Greptile reports 5/5
10-
and there are zero open comment threads. Also keeps the branch mergeable against staging — a
11-
long-running loop spans hours during which staging can drift out from under the PR and produce
12-
a real merge conflict, not just the stray-local-commit drift `/ship`'s sync check catches.
13-
Designed to be run under `/loop` (no fixed interval — let it self-pace on review latency) so it
14-
survives across multiple wakeups in the same session.
10+
and there are zero open comment threads. Also keeps the branch mergeable against staging, since
11+
a long babysit session can outlast staging moving underneath it. Designed to be run under
12+
`/loop` (no fixed interval — let it self-pace on review latency) so it survives across multiple
13+
wakeups in the same session.
1514

1615
## When to use
1716

@@ -36,10 +35,9 @@ round. Always check both conditions freshly after every push.
3635

3736
## Loop
3837

39-
1. **Check current state** before doing anything — this includes mergeable state, not just
40-
review state:
38+
1. **Check current state** before doing anything, including whether the PR is still mergeable:
4139
```bash
42-
gh pr view <n> --json mergeable,mergeStateStatus
40+
gh pr view <n> --json mergeable
4341
gh pr view <n> --json comments -q '[.comments[] | select(.author.login=="greptile-apps")] | last | .body'
4442
gh api graphql -f query='
4543
query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <n>) {
@@ -54,44 +52,16 @@ round. Always check both conditions freshly after every push.
5452
stop yet: re-run the same query with `after: "<endCursor>"` and keep paging until
5553
`hasNextPage` is `false` before evaluating "clean." A PR with more than 50 threads is rare but
5654
stopping on a partial page would silently miss unresolved ones past the cutoff.
57-
`mergeable` is `UNKNOWN` for a few seconds right after any push or base-branch move while
58-
GitHub computes it — re-poll `gh pr view <n> --json mergeable` every few seconds until it
59-
settles to `MERGEABLE` or `CONFLICTING` before acting on it; don't treat `UNKNOWN` as either.
60-
If `mergeable` is `CONFLICTING`, skip straight to step 2 (Resolve merge conflicts) — do not
61-
evaluate review cleanliness yet, since Greptile/Cursor threads anchored to a conflicting diff
62-
can be stale and CI can't even run to confirm anything.
63-
If `mergeable` is `MERGEABLE`, Greptile is 5/5, and every thread across all pages has
64-
`isResolved: true`, stop — report the outcome (see "Reporting" below) and skip the rest of
65-
this list.
66-
67-
2. **If `mergeable` is `CONFLICTING`**, resolve it before anything else this round. This can
68-
happen even on a PR that was clean at creation, since staging moves several times a day —
69-
recheck every iteration, don't assume it's a one-time state handled by `/ship`.
70-
```bash
71-
git status # stash -u first (ship-sync-fix pattern) if anything uncommitted
72-
git fetch origin staging
73-
git rebase origin/staging
74-
```
75-
Resolve each conflicted file on its actual merits — open it, read both sides inside the
76-
`<<<<<<<` / `=======` / `>>>>>>>` markers, and keep the intent of *both* changes where they're
77-
not truly contradictory. Never resolve with a blanket `git checkout --ours`/`--theirs` across
78-
a whole file — that silently discards one side's real change instead of merging it. `git add`
79-
each resolved file, then `git rebase --continue`; repeat until the rebase finishes clean (a
80-
rebase with **zero remaining conflicts** does not by itself mean the resolution is correct —
81-
see the next point).
82-
- A conflict resolution is a code change like any other: before pushing, run this repo's
83-
typecheck/lint pass on the touched files (the same checks `/ship` step 6 runs) — merge
84-
markers can resolve syntactically clean and still be semantically wrong or fail to compile.
85-
- Push with `git push --force-with-lease` — a rebase here always rewrites already-published
86-
history, so a plain `git push` will be rejected.
87-
- Re-poll `gh pr view <n> --json mergeable,mergeStateStatus` until it reports `MERGEABLE`
88-
(not `UNKNOWN`) before moving on. If it still reports `CONFLICTING`, the rebase resolved
89-
against a stale local `origin/staging``git fetch origin staging` again and redo the
90-
rebase; don't push a second time believing it's fixed without this recheck.
91-
- This push needs a fresh review like any other code change — go to step 8 (re-trigger
92-
review) rather than trying to also address old review threads in the same pass, especially
93-
if the conflict touched files a thread was anchored to (that anchor may no longer be valid
94-
against the resolved code). Re-evaluate thread state fresh once the new round lands.
55+
If `mergeable` comes back `CONFLICTING`, go fix that first (step 2) before evaluating review
56+
state — a conflicting PR can't run CI, and this can happen mid-loop even on a PR that was
57+
clean at creation, since staging moves several times a day. Otherwise, if Greptile is 5/5 and
58+
every thread across all pages has `isResolved: true`, stop — report the outcome (see
59+
"Reporting" below) and skip the rest of this list.
60+
61+
2. **If the PR has a merge conflict**, fix it: `git fetch origin staging`, `git rebase
62+
origin/staging`, resolve the conflicts for real (don't just take one side blindly), then push
63+
with `--force-with-lease`. Continue on to step 8 to trigger a fresh review of the resolved
64+
code.
9565

9666
3. **If no review has run yet** (fresh PR, no Greptile/Cursor comments): they usually run
9767
automatically on PR open — confirm via `gh pr checks <n>` (look for `Cursor Bugbot` /
@@ -130,11 +100,7 @@ round. Always check both conditions freshly after every push.
130100
committing — not just lint/typecheck/boundary-validation, but also the conditional `/cleanup`
131101
(if this round's fix touched UI code) and `/db-migrate` (if it touched schema/migrations)
132102
gates from `/ship` steps 4 and 5. A review-fix round is still a code change and can trip
133-
either gate just as easily as the original commit did. This is the same sync check as step 2
134-
above but for local-drift, not for the base-branch textual conflicts step 2 handles — step 2
135-
already left the branch rebased onto current `origin/staging` when it ran, so this step is
136-
normally a no-op in that case, but still run it: it also catches stray local commits that
137-
have nothing to do with merge conflicts.
103+
either gate just as easily as the original commit did.
138104

139105
7. **Commit and push** the round's fixes as one commit — `--force-with-lease` whenever step 6's
140106
sync check rewrote history, which includes a plain `git rebase origin/staging` that completed
@@ -164,17 +130,15 @@ round. Always check both conditions freshly after every push.
164130
in a sleep loop. Pass the same `/loop babysit PR <n>` prompt on each wakeup so the loop
165131
resumes correctly.
166132

167-
10. **Stop conditions**: clean state reached (see above), or the same unresolved finding
168-
survives two consecutive rounds with no new information, or the same merge conflict recurs
169-
every round with no new information (e.g. a semantic conflict you can't confidently resolve
170-
without changing intent), or the user interrupts — surface any of these to the user instead
171-
of looping forever.
133+
10. **Stop conditions**: clean state reached (see above), or the same unresolved finding or
134+
merge conflict survives two consecutive rounds with no new information (surface it to the
135+
user instead of looping forever), or the user interrupts.
172136

173137
## Reporting
174138

175-
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each),
176-
what was pushed back on as a false positive and why, how many merge conflicts came up and against
177-
which staging commits (if any), and the final Greptile score / thread count / mergeable state.
139+
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each,
140+
including any merge conflict resolved), what was pushed back on as a false positive and why, and
141+
the final Greptile score / thread count.
178142

179143
## Hard rules
180144

@@ -184,9 +148,4 @@ which staging commits (if any), and the final Greptile score / thread count / me
184148
pattern elsewhere in the codebase solving the same class of problem and match it.
185149
- Never silently drop a finding — every thread gets either a code fix or a reasoned reply.
186150
- Always re-run the `/ship`-style sync check before every push in the loop, not just the first.
187-
- Never resolve a merge conflict with a blanket `--ours`/`--theirs` across a whole file — read
188-
both sides and preserve the real intent of each; a wrong resolution ships silently since
189-
nothing else catches it.
190-
- Never treat `mergeable: UNKNOWN` as either `MERGEABLE` or `CONFLICTING` — poll until it settles.
191-
- Never evaluate review-thread cleanliness while `mergeable` is `CONFLICTING` — resolve the
192-
conflict first, since threads anchored to a conflicting diff can be stale.
151+
- Never resolve a merge conflict by blindly taking one side — check the actual diff.

.claude/commands/babysit.md

Lines changed: 24 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ description: Drive a PR to a clean review (Greptile 5/5, zero open threads) —
66

77
Owns a PR end-to-end through review: ship it, wait for the automatic review round, and if it
88
isn't already clean, drive fix → reply → resolve → re-review cycles until Greptile reports 5/5
9-
and there are zero open comment threads. Also keeps the branch mergeable against staging — a
10-
long-running loop spans hours during which staging can drift out from under the PR and produce
11-
a real merge conflict, not just the stray-local-commit drift `/ship`'s sync check catches.
12-
Designed to be run under `/loop` (no fixed interval — let it self-pace on review latency) so it
13-
survives across multiple wakeups in the same session.
9+
and there are zero open comment threads. Also keeps the branch mergeable against staging, since
10+
a long babysit session can outlast staging moving underneath it. Designed to be run under
11+
`/loop` (no fixed interval — let it self-pace on review latency) so it survives across multiple
12+
wakeups in the same session.
1413

1514
## When to use
1615

@@ -35,10 +34,9 @@ round. Always check both conditions freshly after every push.
3534

3635
## Loop
3736

38-
1. **Check current state** before doing anything — this includes mergeable state, not just
39-
review state:
37+
1. **Check current state** before doing anything, including whether the PR is still mergeable:
4038
```bash
41-
gh pr view <n> --json mergeable,mergeStateStatus
39+
gh pr view <n> --json mergeable
4240
gh pr view <n> --json comments -q '[.comments[] | select(.author.login=="greptile-apps")] | last | .body'
4341
gh api graphql -f query='
4442
query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <n>) {
@@ -53,44 +51,16 @@ round. Always check both conditions freshly after every push.
5351
stop yet: re-run the same query with `after: "<endCursor>"` and keep paging until
5452
`hasNextPage` is `false` before evaluating "clean." A PR with more than 50 threads is rare but
5553
stopping on a partial page would silently miss unresolved ones past the cutoff.
56-
`mergeable` is `UNKNOWN` for a few seconds right after any push or base-branch move while
57-
GitHub computes it — re-poll `gh pr view <n> --json mergeable` every few seconds until it
58-
settles to `MERGEABLE` or `CONFLICTING` before acting on it; don't treat `UNKNOWN` as either.
59-
If `mergeable` is `CONFLICTING`, skip straight to step 2 (Resolve merge conflicts) — do not
60-
evaluate review cleanliness yet, since Greptile/Cursor threads anchored to a conflicting diff
61-
can be stale and CI can't even run to confirm anything.
62-
If `mergeable` is `MERGEABLE`, Greptile is 5/5, and every thread across all pages has
63-
`isResolved: true`, stop — report the outcome (see "Reporting" below) and skip the rest of
64-
this list.
65-
66-
2. **If `mergeable` is `CONFLICTING`**, resolve it before anything else this round. This can
67-
happen even on a PR that was clean at creation, since staging moves several times a day —
68-
recheck every iteration, don't assume it's a one-time state handled by `/ship`.
69-
```bash
70-
git status # stash -u first (ship-sync-fix pattern) if anything uncommitted
71-
git fetch origin staging
72-
git rebase origin/staging
73-
```
74-
Resolve each conflicted file on its actual merits — open it, read both sides inside the
75-
`<<<<<<<` / `=======` / `>>>>>>>` markers, and keep the intent of *both* changes where they're
76-
not truly contradictory. Never resolve with a blanket `git checkout --ours`/`--theirs` across
77-
a whole file — that silently discards one side's real change instead of merging it. `git add`
78-
each resolved file, then `git rebase --continue`; repeat until the rebase finishes clean (a
79-
rebase with **zero remaining conflicts** does not by itself mean the resolution is correct —
80-
see the next point).
81-
- A conflict resolution is a code change like any other: before pushing, run this repo's
82-
typecheck/lint pass on the touched files (the same checks `/ship` step 6 runs) — merge
83-
markers can resolve syntactically clean and still be semantically wrong or fail to compile.
84-
- Push with `git push --force-with-lease` — a rebase here always rewrites already-published
85-
history, so a plain `git push` will be rejected.
86-
- Re-poll `gh pr view <n> --json mergeable,mergeStateStatus` until it reports `MERGEABLE`
87-
(not `UNKNOWN`) before moving on. If it still reports `CONFLICTING`, the rebase resolved
88-
against a stale local `origin/staging``git fetch origin staging` again and redo the
89-
rebase; don't push a second time believing it's fixed without this recheck.
90-
- This push needs a fresh review like any other code change — go to step 8 (re-trigger
91-
review) rather than trying to also address old review threads in the same pass, especially
92-
if the conflict touched files a thread was anchored to (that anchor may no longer be valid
93-
against the resolved code). Re-evaluate thread state fresh once the new round lands.
54+
If `mergeable` comes back `CONFLICTING`, go fix that first (step 2) before evaluating review
55+
state — a conflicting PR can't run CI, and this can happen mid-loop even on a PR that was
56+
clean at creation, since staging moves several times a day. Otherwise, if Greptile is 5/5 and
57+
every thread across all pages has `isResolved: true`, stop — report the outcome (see
58+
"Reporting" below) and skip the rest of this list.
59+
60+
2. **If the PR has a merge conflict**, fix it: `git fetch origin staging`, `git rebase
61+
origin/staging`, resolve the conflicts for real (don't just take one side blindly), then push
62+
with `--force-with-lease`. Continue on to step 8 to trigger a fresh review of the resolved
63+
code.
9464

9565
3. **If no review has run yet** (fresh PR, no Greptile/Cursor comments): they usually run
9666
automatically on PR open — confirm via `gh pr checks <n>` (look for `Cursor Bugbot` /
@@ -129,11 +99,7 @@ round. Always check both conditions freshly after every push.
12999
committing — not just lint/typecheck/boundary-validation, but also the conditional `/cleanup`
130100
(if this round's fix touched UI code) and `/db-migrate` (if it touched schema/migrations)
131101
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. This is the same sync check as step 2
133-
above but for local-drift, not for the base-branch textual conflicts step 2 handles — step 2
134-
already left the branch rebased onto current `origin/staging` when it ran, so this step is
135-
normally a no-op in that case, but still run it: it also catches stray local commits that
136-
have nothing to do with merge conflicts.
102+
either gate just as easily as the original commit did.
137103

138104
7. **Commit and push** the round's fixes as one commit — `--force-with-lease` whenever step 6's
139105
sync check rewrote history, which includes a plain `git rebase origin/staging` that completed
@@ -163,17 +129,15 @@ round. Always check both conditions freshly after every push.
163129
in a sleep loop. Pass the same `/loop babysit PR <n>` prompt on each wakeup so the loop
164130
resumes correctly.
165131

166-
10. **Stop conditions**: clean state reached (see above), or the same unresolved finding
167-
survives two consecutive rounds with no new information, or the same merge conflict recurs
168-
every round with no new information (e.g. a semantic conflict you can't confidently resolve
169-
without changing intent), or the user interrupts — surface any of these to the user instead
170-
of looping forever.
132+
10. **Stop conditions**: clean state reached (see above), or the same unresolved finding or
133+
merge conflict survives two consecutive rounds with no new information (surface it to the
134+
user instead of looping forever), or the user interrupts.
171135

172136
## Reporting
173137

174-
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each),
175-
what was pushed back on as a false positive and why, how many merge conflicts came up and against
176-
which staging commits (if any), and the final Greptile score / thread count / mergeable state.
138+
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each,
139+
including any merge conflict resolved), what was pushed back on as a false positive and why, and
140+
the final Greptile score / thread count.
177141

178142
## Hard rules
179143

@@ -183,9 +147,4 @@ which staging commits (if any), and the final Greptile score / thread count / me
183147
pattern elsewhere in the codebase solving the same class of problem and match it.
184148
- Never silently drop a finding — every thread gets either a code fix or a reasoned reply.
185149
- Always re-run the `/ship`-style sync check before every push in the loop, not just the first.
186-
- Never resolve a merge conflict with a blanket `--ours`/`--theirs` across a whole file — read
187-
both sides and preserve the real intent of each; a wrong resolution ships silently since
188-
nothing else catches it.
189-
- Never treat `mergeable: UNKNOWN` as either `MERGEABLE` or `CONFLICTING` — poll until it settles.
190-
- Never evaluate review-thread cleanliness while `mergeable` is `CONFLICTING` — resolve the
191-
conflict first, since threads anchored to a conflicting diff can be stale.
150+
- Never resolve a merge conflict by blindly taking one side — check the actual diff.

0 commit comments

Comments
 (0)