Commit 74c346b
feat: split fleet PR maintenance out of implement-issue into sweep-prs (#594)
* feat: sweep open PRs for CI and conflicts in implement-issue Step 4
Step 4 only pruned worktrees whose PR had merged. It never looked at an
open PR, so it could not see either way a parked PR rots: red CI, or a
branch that went CONFLICTING because other PRs merged into main ahead of
it. A CONFLICTING PR creates no workflow run at all, so it presents as
"CI never fired" and nobody investigates.
Step 4 becomes 4a (fleet sweep) + 4b (worktree + branch). The sweep keeps
the merged-prune arm unchanged and adds an open-PR arm that merges
origin/main into stale branches, auto-resolves mechanical conflicts only,
and reports everything else. A skip gate runs before both arms so the
sweep never touches a worktree another agent owns — any live session at
that cwd, uncommitted tracked changes, or a HEAD under 30 minutes old.
The same section is what to run under /loop for continuous fleet
maintenance, rather than forking a second skill that drifts.
Step 9 gains the matching pre-push `git merge origin/main`: Step 4b cuts
the branch from a current origin/main, but Steps 5-8 take hours and other
PRs merge during them.
Two behaviours found by dry-running the sweep against the real fleet:
GitHub computes `mergeable` lazily, so the first query on a cold PR
returns UNKNOWN and only triggers the computation — a single pass reports
UNKNOWN for precisely the stale PRs the sweep exists to find, hence the
retry. And 11 worktrees hold branches with local commits and no PR; those
are reported, never deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYV1WdkadzkeUUgULhC3Dk
* feat: split fleet PR maintenance out of implement-issue into sweep-prs
implement-issue owns exactly one PR — the issue it was invoked for — and
had no idea whether that PR ever went green. It stopped at draft-PR-open,
so a PR that failed the CI matrix (which quality-check.sh does not
reproduce) or went CONFLICTING minutes later sat there unreviewed.
Step 9 now merges origin/main before pushing: Step 4 cuts the branch from
a current origin/main, but Steps 5-8 take hours and other PRs merge during
them. Opening an already-CONFLICTING PR is worse than it sounds, because
GitHub creates no workflow run at all for one — it presents as "CI never
fired" rather than as a conflict.
New Step 10 watches that PR to green via `gh pr checks --watch`, fixing
failures in the worktree, and is explicitly scoped to this PR alone. Step 4
is unchanged: it still prunes merged worktrees only.
Fleet-wide maintenance moves to a new sweep-prs skill, which walks every
worktree, prunes merged ones, merges main into stale branches, and reports
red CI. It has the piece implement-issue must not grow: an ownership skip
gate. A live session at that cwd (any status — idle and blocked included),
uncommitted tracked changes, or a HEAD under 30 minutes old means hands
off, because merging under a running implement-issue moves its HEAD and
puts two sessions on one branch.
Two behaviours found by dry-running the sweep against the real fleet:
GitHub computes `mergeable` lazily, so the first query on a cold PR returns
UNKNOWN and only triggers the computation — a single pass reports UNKNOWN
for precisely the stale PRs the sweep exists to find, hence the retry. And
11 worktrees hold branches with local commits and no PR; those are
reported, never deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYV1WdkadzkeUUgULhC3Dk
* feat: make sweep-prs report-only by default, selectable per PR
The sweep acted on everything that survived the skip gate, in one pass,
with no way to choose. Merging and pushing to a PR the user has not looked
at should not be what happens because they forgot to pass a flag.
Three modes: bare `/sweep-prs` classifies the fleet and reports what it
would do, changing nothing; `/sweep-prs 437 579` acts on the listed PRs
only; `/sweep-prs --all` acts on everything eligible and is the one to pair
with /loop. The safe mode is now the default.
Pruning merged worktrees stays automatic in every mode. It deletes only
work already merged into main, so nothing is at risk, and gating it behind
a flag recreates the failure it was added to fix -- a cleanup nobody
chooses to run doesn't run, which is how 39 worktrees accumulated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYV1WdkadzkeUUgULhC3Dk
* fix: distinguish the two causes of "no checks reported" in Step 10
Step 10 told you to run `gh pr checks --watch` and read the result. On
this skill's own PR that command returned "no checks reported on the
'feat/sweep-prs-skill' branch" about 8 seconds after the push -- the run
existed and was in_progress, but --watch returned before it registered.
That string has two causes that need opposite responses. A CONFLICTING PR
has no run and never will, because GitHub does not build one; the fix is
to merge origin/main. A just-pushed PR has a run that hasn't appeared yet;
the fix is to wait on the run id. Reading either as green is how a red PR
gets handed over as finished.
Step 10 now requires telling them apart via `gh pr view --json mergeable`
plus `gh run list --branch`, then `gh run watch <id> --exit-status`. Same
correction in sweep-prs' rationalization table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYV1WdkadzkeUUgULhC3Dk
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent e81574b commit 74c346b
2 files changed
Lines changed: 272 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
337 | 338 | | |
338 | 339 | | |
339 | 340 | | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
340 | 355 | | |
341 | 356 | | |
342 | 357 | | |
| |||
389 | 404 | | |
390 | 405 | | |
391 | 406 | | |
392 | | - | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
393 | 456 | | |
394 | 457 | | |
395 | 458 | | |
| |||
409 | 472 | | |
410 | 473 | | |
411 | 474 | | |
412 | | - | |
| 475 | + | |
413 | 476 | | |
414 | 477 | | |
415 | 478 | | |
| |||
454 | 517 | | |
455 | 518 | | |
456 | 519 | | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
457 | 523 | | |
458 | 524 | | |
459 | 525 | | |
| |||
482 | 548 | | |
483 | 549 | | |
484 | 550 | | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
485 | 557 | | |
486 | 558 | | |
487 | 559 | | |
| |||
497 | 569 | | |
498 | 570 | | |
499 | 571 | | |
500 | | - | |
| 572 | + | |
| 573 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
0 commit comments