Skip to content

Commit 74c346b

Browse files
johanzanderclaude
andauthored
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

File tree

.claude/skills/implement-issue/SKILL.md

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ runners — only repo-level `.claude/skills/` and `.claude/agents/` exist there.
4747
| 7. Confirm gate 2 | Replaced by the draft PR itself — the owner reviews the draft before anything merges. |
4848
| 8. Local run & observe | Structurally unavailable in CI — this is the documented reason the local flow exists. Skip, and say so in the PR body's test plan so the reviewer knows verification is still owed. |
4949
| 9. Commit + draft PR | Applies verbatim, including the `CHANGELOG.md` `## [Unreleased]` entry and the documentation check. Add the `## Scope assessment` section (Step 3 above). The workflow file owns CI-only mechanics: issue comment with the PR link, `has-fix-pr` label. |
50-
| 10. Hard constraints | Apply verbatim. |
50+
| 10. Watch this PR to green | Applies verbatim — `gh pr checks --watch` on the PR just opened, fix failures, never widen to other PRs. |
51+
| 11. Hard constraints | Apply verbatim. |
5152

5253
## Process
5354

@@ -337,6 +338,20 @@ to guess whether it was checked.
337338
Commit per `docs/agents/workflow.md` format (subject + blank line + body
338339
explaining WHY).
339340

341+
**Then bring the branch up to date before pushing — not after.**
342+
343+
```bash
344+
git fetch origin && git merge origin/main
345+
```
346+
347+
Resolve any conflicts here, in the worktree, where you have the context; if
348+
the merge brought changes in, re-run `./scripts/quality-check.sh` before
349+
pushing. Step 4 cut this branch from a current `origin/main`, but Steps 5–8
350+
take hours (slow suite, `verify`) and other PRs merge during them. Opening a
351+
PR that is already `CONFLICTING` is worse than it sounds: GitHub creates **no
352+
workflow run at all** for it, so the PR shows no checks rather than a
353+
conflict, and the first reader concludes CI dropped the event.
354+
340355
**Frontend diffs only:** before pushing, show the user the diff and the
341356
Step 8 verification output (screenshot/dev-server observation) and wait for
342357
explicit go-ahead. This is the one point in the fully-automatic flow where a
@@ -389,7 +404,55 @@ If you cannot produce a mutation that reddens your test, you have not
389404
demonstrated the bug — say so in the PR and stop, rather than filling the
390405
section in with the suite result.
391406

392-
### 10. Hard constraints
407+
### 10. Watch this PR to green (and only this PR)
408+
409+
The draft PR is open, but CI has not run yet. Local `quality-check.sh` and
410+
the slow suite are not the same as the CI matrix, and a PR left red or
411+
`CONFLICTING` is a PR the user cannot review.
412+
413+
```bash
414+
gh pr checks <n> --watch --fail-fast # blocks until the run settles
415+
gh pr view <n> --json mergeable,mergeStateStatus
416+
```
417+
418+
**`no checks reported on the '<branch>' branch` is not a result.** It has
419+
two entirely different causes and you must tell them apart before doing
420+
anything else:
421+
422+
```bash
423+
gh pr view <n> --json mergeable,mergeStateStatus # CONFLICTING -> merge origin/main
424+
gh run list --branch <branch> --limit 3 # in_progress -> --watch just raced it
425+
gh run watch <run-id> --exit-status # then wait on the run directly
426+
```
427+
428+
If `mergeable` is `CONFLICTING`, there is genuinely no run and never will
429+
be — GitHub does not build a conflicted PR. If a run is `in_progress`,
430+
`--watch` simply returned before the run was registered (observed on this
431+
skill's own PR, ~8s after the push) and you wait on the run id instead.
432+
Reading "no checks" as green is how a red PR gets handed over as finished.
433+
434+
Then, on this PR only:
435+
436+
- **Checks fail:** read `gh run view --log-failed`, fix in the worktree,
437+
re-run `./scripts/quality-check.sh`, push. This is your diff, so a real
438+
test failure is yours to fix — not merely to report.
439+
- **Went `CONFLICTING`** (another PR merged in the minutes since Step 9):
440+
`git merge origin/main`, resolve, `quality-check.sh`, push.
441+
- **Green and mergeable:** report the PR link and stop. Do not merge, do not
442+
take it out of draft — Step 11 still holds.
443+
444+
**Scope: this issue's PR, nothing else.** If the sweep in Step 4 or your own
445+
`gh pr list` shows other PRs red or conflicted, that is not this session's
446+
job — hand it to the `sweep-prs` skill, which owns fleet-wide maintenance
447+
and has the ownership skip gate needed to touch a worktree another agent may
448+
be sitting in. Widening a single-issue session into fleet cleanup is how two
449+
sessions end up pushing to the same branch.
450+
451+
`gh pr checks --watch` blocks rather than polls, so this costs one wait, not
452+
a re-read of the whole session context every 60s. If CI is badly backed up,
453+
say so and leave the PR — don't hold the session open indefinitely.
454+
455+
### 11. Hard constraints
393456

394457
- Draft PR only. Never auto-merge.
395458
- Never push directly to `main`.
@@ -409,7 +472,7 @@ section in with the suite result.
409472

410473
A **separate, later invocation** — often a different session, sometimes days
411474
later once CI is green and the user has reviewed. Not part of the numbered
412-
flow above, which stops at draft-PR-open per the Step 10 constraints.
475+
flow above, which stops at draft-PR-green per the Step 11 constraints.
413476

414477
**Treat this as best-effort, not the cleanup mechanism.** Because it depends
415478
on someone returning after the merge, it reliably does not happen; Step 4's
@@ -454,6 +517,9 @@ net is upstream, not this section.
454517
| "I'll clean up this other thing while I'm in here" | Out of scope. Minimal fix only. |
455518
| "code review can wait until after I've verified it works" | Reordered on purpose — catch cheap issues before spending time on manual verification, not after. |
456519
| "there's already a bot diagnosis, let me re-derive it anyway to be safe" | Re-verify the cited evidence; don't redo the whole investigation. |
520+
| "the branch was current when I cut it, no need to merge before pushing" | Steps 5–8 take hours and other PRs merge during them. And a CONFLICTING PR gets no workflow run at all, so it reads as "CI never fired" — the conflict stays invisible until someone digs. |
521+
| "while I'm watching my PR I may as well fix the other red ones" | That's `sweep-prs`, which has the ownership skip gate this skill doesn't. Another agent may be sitting in that worktree; merging under it puts two sessions on one branch. |
522+
| "the PR is open, my job is done" | Open isn't green. CI runs a matrix `quality-check.sh` doesn't, and the user can't review a red or conflicted PR. Step 10 finishes the job. |
457523
| "the plan doc is useful context, keep it in the PR" | Once code and tests exist, the plan only drifts — it's not the source of truth. Delete it before Step 9; keep the spec if one exists. |
458524
| "the user is in a hurry, just open the PR" | Time pressure from the user is not permission to skip Step 8 — it's the reason to say so explicitly and give a real ETA instead. |
459525
| "I'll clean up the worktree after it merges" | You won't — that's the postcondition that already failed 24 times. Prune at Step 4, before creating the next one. |
@@ -482,6 +548,12 @@ net is upstream, not this section.
482548
in `docs/agents/bess-knowledge.md` or `docs/SOFTWARE_DESIGN.md`.
483549
- About to write only a synthetic-input unit test for a DP/intent/control-
484550
mapping change instead of a plan-faithfulness (`R == P`) scenario test.
551+
- About to push the branch without having merged `origin/main` since Step 4.
552+
- About to stop at "draft PR opened" without watching CI settle (Step 10).
553+
- About to read `no checks reported` as green. It means either a conflict
554+
or a run that hasn't registered yet — distinguish before reporting.
555+
- About to touch another PR or another worktree from inside this session —
556+
that is `sweep-prs`, not this skill.
485557
- About to write a repro test from hand-built data when a user debug log/
486558
bundle is available and `from_debug_log.py` could build it from real data.
487559

@@ -497,4 +569,5 @@ net is upstream, not this section.
497569
| 6. Quality gate + code review | `quality-check.sh` + slow suite + `code-review` (background agent) | No |
498570
| 7. Confirm gate 2 || Conditional (auto-continue if backend-only + clean; otherwise No) |
499571
| 8. Local run & observe | `verify` | **Never** |
500-
| 9. Commit + PR | `finishing-a-development-branch` | No |
572+
| 9. Commit + PR | `finishing-a-development-branch` (incl. pre-push `git merge origin/main`) | No |
573+
| 10. Watch this PR to green | `gh pr checks --watch` — this PR only | No |

.claude/skills/sweep-prs/SKILL.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
name: sweep-prs
3+
description: Use when asked to check, refresh, or keep the open bess-manager PRs green and mergeable across the whole fleet — pruning merged worktrees, merging main into stale branches, and reporting red CI. Reports only by default; pass PR numbers to act on those, or --all (pair with /loop) for unattended maintenance.
4+
---
5+
6+
# Sweep PRs
7+
8+
## Overview
9+
10+
Walk every git worktree in the repo and bring the fleet back to a known
11+
state: prune the ones whose PR has merged, refresh the ones whose PR has
12+
gone stale, report everything else.
13+
14+
This exists because a PR rots without anyone touching it. A branch that was
15+
green and mergeable when its session ended goes `CONFLICTING` the moment two
16+
or three others merge into `main` ahead of it — and a `CONFLICTING` PR
17+
**creates no workflow run at all**, so it presents as "CI never fired", not
18+
as a conflict. Nobody investigates a PR that appears to have no checks. The
19+
first dry run of this sweep found two such PRs (#437, #579) sitting
20+
conflicted with no one aware.
21+
22+
## When to Use
23+
24+
- Asked to check whether the open PRs are green/mergeable, or to fix up the
25+
ones that aren't.
26+
- Under `/loop /sweep-prs --all` to hold the fleet green between issues.
27+
Self-paced is right: a fully-green fleet costs a noop tick. `--all` is
28+
required here — a bare `/sweep-prs` on a loop just re-reports the same
29+
fleet forever without fixing anything.
30+
- **Not** from inside an `implement-issue` session. That skill owns exactly
31+
one PR — its own — and deliberately does not widen into fleet cleanup
32+
(see its Step 10). Run this separately.
33+
34+
## Modes
35+
36+
Classification always covers every worktree in `git worktree list`. What
37+
differs is **what gets acted on**, and that is chosen by the invocation:
38+
39+
| Invocation | Behaviour |
40+
|---|---|
41+
| `/sweep-prs` | **Report only — the default.** Classify the whole fleet, print what each PR *would* get, change nothing. No merges, no pushes. |
42+
| `/sweep-prs 437 579` | Act, restricted to the listed PR numbers. Everything else is classified and reported, untouched. This is the normal way to use it: read the report, pick. |
43+
| `/sweep-prs --all` | Act on every PR that survives the skip gate. Pair this one with `/loop` for unattended maintenance. |
44+
45+
**Report-only is the default on purpose.** Merging and pushing to a PR the
46+
user has not looked at is not something to do because they forgot to pass a
47+
flag. The safe mode is the one you get when you don't think about it; acting
48+
is the one you have to ask for.
49+
50+
**Pruning merged worktrees runs in every mode, including report-only.** It
51+
is not gated, because it deletes only work that is already merged into
52+
`main` — nothing is at risk — and because gating it recreates exactly the
53+
failure it was introduced to fix: a cleanup that depends on someone choosing
54+
to run it doesn't run. That is how 39 worktrees accumulated on disk, 24 of
55+
them long merged. The uncommitted-changes and live-session guards still
56+
apply to it.
57+
58+
Whatever the mode, a branch is only ever *acted on* when it survives the
59+
skip gate below; everything else is reported with its reason.
60+
61+
## Process
62+
63+
### 1. Skip gate (before anything else)
64+
65+
Another agent may be mid-`implement-issue` in any of these worktrees.
66+
Merging `origin/main` underneath it moves its HEAD mid-run and puts two
67+
sessions on the same branch pushing to the same PR. Leave a worktree alone
68+
when any of these hold:
69+
70+
| Condition | Why |
71+
|---|---|
72+
| A live Claude session has its `cwd` at or under the worktree | Owned. **Any status counts — `idle` and `blocked` included.** An agent parked between Step 6 and Step 8 reads as idle and still holds that branch. |
73+
| `git status --porcelain -uno` is non-empty | Uncommitted tracked work. One such worktree held a 375-line module that existed nowhere else. |
74+
| HEAD commit is under 30 minutes old | Likely active even if its session already exited. |
75+
| Detached or locked | Almost always another agent's live session. |
76+
77+
Read ownership with `claude agents --json` run **unscoped** (from `~`) —
78+
per `CLAUDE.md`'s Worktree Conventions, sibling worktrees never appear in
79+
the project-scoped view, and missing one is exactly the collision this gate
80+
exists to prevent.
81+
82+
### 2. Classify
83+
84+
```bash
85+
git fetch origin --prune
86+
# NOTE: `git branch --merged` / `rev-list origin/main..branch` DO NOT WORK
87+
# here -- this repo squash-merges, so a merged branch's commits are never
88+
# reachable from main and every worktree looks unmerged forever. PR state is
89+
# the only authoritative signal.
90+
merged=$(gh pr list --state merged --limit 200 --json headRefName -q '.[].headRefName')
91+
owned=$(cd ~ && claude agents --json 2>/dev/null | jq -r '.[].cwd')
92+
93+
git worktree list | awk 'NR>1 {print $1}' | while read -r wt; do
94+
b=$(git -C "$wt" branch --show-current 2>/dev/null)
95+
[ -n "$b" ] || { echo "SKIP (detached): $wt"; continue; }
96+
if echo "$owned" | grep -qF "$wt"; then
97+
echo "SKIP (live session): $b"; continue
98+
fi
99+
if [ -n "$(git -C "$wt" status --porcelain -uno)" ]; then
100+
echo "SKIP (uncommitted changes): $b"; continue
101+
fi
102+
if echo "$merged" | grep -qx "$b"; then
103+
echo "PRUNE: $b"; continue
104+
fi
105+
age=$(( ($(date +%s) - $(git -C "$wt" log -1 --format=%ct)) / 60 ))
106+
if [ "$age" -lt 30 ]; then
107+
echo "SKIP (HEAD ${age}m old, likely active): $b"; continue
108+
fi
109+
# GitHub computes `mergeable` LAZILY: the first query on a cold PR returns
110+
# UNKNOWN *and* triggers the computation, so a single pass reports UNKNOWN
111+
# for every stale PR -- i.e. exactly the ones this sweep exists to find.
112+
# Ask again until it resolves. Verified: all three resolved on retry.
113+
for _ in 1 2 3; do
114+
pr=$(gh pr view "$b" --json number,mergeable,mergeStateStatus,statusCheckRollup \
115+
-q '"#\(.number) \(.mergeable) \(.mergeStateStatus) " +
116+
([.statusCheckRollup[]?|select(.conclusion=="FAILURE").name]|join(","))' \
117+
2>/dev/null)
118+
[ -z "$pr" ] && { echo "NO PR (never pushed): $b"; break; }
119+
case "$pr" in *UNKNOWN*) sleep 3; continue;; esac
120+
echo "OPEN $pr <- $wt"; break
121+
done
122+
done
123+
```
124+
125+
### 3. Act
126+
127+
**`PRUNE`** — the PR merged:
128+
129+
```bash
130+
git worktree remove "$wt" && git branch -D "$b"
131+
```
132+
133+
Force-delete is expected, not a warning sign: squash-merge means the
134+
branch's commits never become reachable from `main`, so `git branch -d`'s
135+
ancestry check always refuses.
136+
137+
**`OPEN`** — in report-only mode (the default), print the row from the table
138+
below that this PR matches and take no action. In `--all` mode, or when the
139+
PR number was listed explicitly, act on it. This is judgment, not script:
140+
141+
| State | Action |
142+
|---|---|
143+
| `CONFLICTING`, or `mergeStateStatus: BEHIND` | In that worktree: `git merge origin/main`. Auto-resolve **mechanical** conflicts only — `CHANGELOG.md` under `## [Unreleased]` (keep both bullets), import ordering, lockfiles. Then `./scripts/quality-check.sh`, then push. |
144+
| Conflict is semantic | `git merge --abort` and report it. Do not guess at someone else's fix. |
145+
| Checks `FAILURE` | Read the failing job log (`gh run view --log-failed`). Auto-fix only Black/Ruff formatting and a missing `## [Unreleased]` changelog entry — the mechanical CI failures. A real test failure belongs to whoever wrote the diff; report it, never patch it blind. |
146+
| Green and mergeable | Nothing. |
147+
| `NO PR (never pushed)` | Report only, never delete. A branch with local commits and no PR is abandoned work or someone's parked experiment; the first dry run found 11. The sweep is not what decides their fate. |
148+
149+
### 4. Report
150+
151+
In report-only mode, end with the invocation that would act on what you
152+
found — `/sweep-prs 437 579` — so choosing is one paste, not a re-read.
153+
154+
Print what was pruned, what was refreshed and pushed (or *would* be), what
155+
failed, and **what was skipped with the reason** — "3 skipped: #589 owned by live
156+
session `bess-manager-34`" is the useful output. A sweep that silently does
157+
nothing is indistinguishable from a broken one.
158+
159+
## Hard constraints
160+
161+
- Never push without `./scripts/quality-check.sh` green.
162+
- Never `git stash` — denied repo-wide, one shared stack (`CLAUDE.md`).
163+
- Never `--force` / `--force-with-lease` on someone else's branch.
164+
- Never merge a PR, take it out of draft, or close it.
165+
- One commit per PR per sweep: `chore: merge main into <branch>`.
166+
- Never resolve a semantic conflict or fix a real test failure on a PR that
167+
isn't yours to diagnose. Report and move on.
168+
- **Run one sweep at a time.** Two collide with each other for exactly the
169+
reason the skip gate exists, and nothing enforces it.
170+
- Never act on a PR the invocation didn't select. A bare `/sweep-prs`
171+
reports; it does not merge or push.
172+
173+
## Rationalizations — Reality
174+
175+
| Excuse | Reality |
176+
|---|---|
177+
| "that worktree's session is idle, so nobody's using it" | Idle and blocked both mean owned. An agent parked between Step 6 and Step 8 is idle and still holds that branch. |
178+
| "the PR shows no CI checks, so the workflow must have failed to trigger" | Two causes, never a dropped event. A CONFLICTING PR creates no run at all; a just-pushed PR has a run that hasn't registered yet. Check `mergeable`, then `gh run list --branch`. |
179+
| "`mergeable` came back UNKNOWN, so gh can't tell" | It's computed lazily — the first query only triggers the computation. Ask again. A single pass reports UNKNOWN for precisely the stale PRs you're looking for. |
180+
| "`git branch --merged` will tell me what's safe to delete" | Not in this repo. Squash-merge means a merged branch is never an ancestor of main, so that check reports everything as unmerged and cleanup never fires. Use `gh pr list --state merged`. |
181+
| "they asked me to sweep, obviously they want it fixed" | A bare `/sweep-prs` reports. Merging and pushing to a PR the user hasn't looked at is not something to infer from a missing flag — hand back the report and the `/sweep-prs <numbers>` line. |
182+
| "they named #437, and #579 has the same problem, I'll do both" | They picked one. Selecting PRs is the whole point of the mode; extending the selection silently makes it meaningless. |
183+
| "this conflict is small, I can see what they meant" | Mechanical means CHANGELOG/imports/lockfiles. Anything touching logic is a guess at someone else's fix — abort and report. |
184+
| "I'm already in an implement-issue session, I'll sweep while I'm here" | That session owns one PR and lacks the skip gate. Widening it is how two sessions end up pushing to one branch. |
185+
186+
## Red Flags — Stop and Go Back
187+
188+
- About to act on a worktree with a live session, uncommitted tracked
189+
changes, or a HEAD under 30 minutes old.
190+
- About to treat `mergeable: UNKNOWN` as a final answer.
191+
- About to push a merge without `quality-check.sh` green.
192+
- About to fix a real test failure on a PR you didn't write.
193+
- About to run this from inside an `implement-issue` session.
194+
- About to merge or push on a bare `/sweep-prs` — that mode reports only.
195+
- About to act on a PR the user didn't list.

0 commit comments

Comments
 (0)