From 695cec720e33934e0e04bd818ba82dfff9315354 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:36:25 -0400 Subject: [PATCH 1/3] fix(playbooks): repo-sweep state.sh warns when the sweep PR conflicts Read mergeable from the existing gh pr list call and print "mergeable CONFLICTING" after pr-state for an open PR only. No new exit code. Refs #5206 Co-Authored-By: Claude Opus 5.5 --- .../skills/repo-sweep/scripts/state.sh | 4 +++- .../skills/repo-sweep/scripts/state.test.sh | 23 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/plugins/playbooks/skills/repo-sweep/scripts/state.sh b/plugins/playbooks/skills/repo-sweep/scripts/state.sh index 3c0b787cb3..9a5992b94f 100755 --- a/plugins/playbooks/skills/repo-sweep/scripts/state.sh +++ b/plugins/playbooks/skills/repo-sweep/scripts/state.sh @@ -12,6 +12,7 @@ # pr # branch # pr-state OPEN|MERGED|CLOSED +# mergeable CONFLICTING a warning: printed only when the open PR conflicts with its base # playbook # dirty yes|no any change, tracked or untracked, outside .work/ # untick-committed ... @@ -35,7 +36,7 @@ set -euo pipefail prefix=chore/repo-sweep- -fields=number,state,headRefName,baseRefName,body,isCrossRepository +fields=number,state,mergeable,headRefName,baseRefName,body,isCrossRepository # Branch names, playbook names, and step ids reach commands the agent builds, so all three are # restricted to safe characters, and fork PRs never count as a sweep. safe_branch='^chore/repo-sweep-[A-Za-z0-9._-]+$' @@ -71,6 +72,7 @@ fi jq -r '"pr \(.number)\nbranch \(.headRefName)\npr-state \(.state)"' "$tmp/pr" [[ $(jq -r .state "$tmp/pr") == OPEN ]] || exit 11 +jq -r 'select(.mergeable == "CONFLICTING") | "mergeable CONFLICTING"' "$tmp/pr" jq -r .body "$tmp/pr" | tr -d '\r' | awk ' /^/ { if (inb) exit; next } inb { print; next } diff --git a/plugins/playbooks/skills/repo-sweep/scripts/state.test.sh b/plugins/playbooks/skills/repo-sweep/scripts/state.test.sh index f2a18d5eff..f7692ffb22 100755 --- a/plugins/playbooks/skills/repo-sweep/scripts/state.test.sh +++ b/plugins/playbooks/skills/repo-sweep/scripts/state.test.sh @@ -58,9 +58,9 @@ body() { # printf "%s$eol\n" "$@" printf '%s\n\nNot run:%s\n- tidy: not selected%s\n' "$eol" "$eol" "$eol" } -pr() { # [] [] - jq -n --arg s "$1" --arg b "$2" --argjson n "${3:-7}" --arg h "${4:-$sweep}" \ - '{number: $n, state: $s, headRefName: $h, baseRefName: "main", body: $b}' +pr() { # [] [] [] + jq -n --arg s "$1" --arg b "$2" --argjson n "${3:-7}" --arg h "${4:-$sweep}" --arg m "${5:-}" \ + '{number: $n, state: $s, headRefName: $h, baseRefName: "main", body: $b} + (if $m == "" then {} else {mergeable: $m} end)' } serve() { jq -s . >"$TMP/gh/$1.json"; } run() { # sets out, rc @@ -132,6 +132,23 @@ pr MERGED "$(body '' '- [ ] a: p:a')" | serve head run assert_eq "merged PR: exit 11" "11 pr-state MERGED" "$rc $(grep '^pr-state' <<<"$out")" +pr OPEN "$(body '' '- [ ] a: p:a')" 7 "$sweep" CONFLICTING | serve head +run +assert_eq "CONFLICTING open PR: warning line, normal exit" "0 mergeable CONFLICTING" "$rc $(grep '^mergeable' <<<"$out")" +assert_eq "CONFLICTING: line sits between pr-state and playbook" "pr-state OPEN +mergeable CONFLICTING +playbook fixture" "$(sed -n 3,5p <<<"$out")" + +for m in MERGEABLE UNKNOWN; do + pr OPEN "$(body '' '- [ ] a: p:a')" 7 "$sweep" "$m" | serve head + run + assert_eq "$m open PR: no mergeable line" "0 " "$rc $(grep '^mergeable' <<<"$out")" +done + +pr MERGED "$(body '' '- [ ] a: p:a')" 7 "$sweep" CONFLICTING | serve head +run +assert_eq "CONFLICTING but merged PR: exit 11, no mergeable line" "11 " "$rc $(grep '^mergeable' <<<"$out")" + serve head Date: Tue, 29 Sep 2026 14:40:01 -0400 Subject: [PATCH 2/3] fix(playbooks): repo-sweep next stops on a conflicting sweep PR Refs #5206 Co-Authored-By: Claude Opus 5.5 --- plugins/playbooks/.claude-plugin/plugin.json | 2 +- plugins/playbooks/CHANGELOG.md | 9 +++++++++ plugins/playbooks/skills/repo-sweep/SKILL.md | 2 +- plugins/playbooks/skills/repo-sweep/evals/evals.json | 12 ++++++++++++ .../playbooks/skills/repo-sweep/reference/next.md | 8 ++++++-- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/plugins/playbooks/.claude-plugin/plugin.json b/plugins/playbooks/.claude-plugin/plugin.json index b14bedb440..566b8e572c 100644 --- a/plugins/playbooks/.claude-plugin/plugin.json +++ b/plugins/playbooks/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "playbooks", - "version": "0.14.1", + "version": "0.14.2", "description": "Doctrine and knowledge playbooks as on-demand skills, repo-sweep for running a catalog of hygiene skills through a repository one commit per step, plus a maintainer-facing update skill. boris carries Boris Cherny's Claude Code workflow tips (howborisusesclaudecode.com), skill-authoring carries Anthropic's internal skill-authoring playbook, and fable-5 carries Claude Fable 5's operating doctrine (self-authored, no upstream). The boris and skill-authoring packs vendor a verbatim upstream baseline; /playbooks:update drift-checks and syncs those baselines centrally (maintainers).", "author": { "name": "Melodic Software", diff --git a/plugins/playbooks/CHANGELOG.md b/plugins/playbooks/CHANGELOG.md index 4b6955c5f3..38f859405f 100644 --- a/plugins/playbooks/CHANGELOG.md +++ b/plugins/playbooks/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to the `playbooks` plugin are recorded here. The `version` i `.claude-plugin/plugin.json` is the delivery vehicle. A consumer receives a change only after that version increases. +## [0.14.2] - 2026-09-29 + +### Fixed + +- **`repo-sweep next` stops when the sweep PR conflicts with its base** ([#5206](https://github.com/melodic-software/claude-code-plugins/issues/5206)). + GitHub runs no `pull_request` workflows on a conflicting PR, so step commits pushed to it got no + CI. `state.sh` now prints `mergeable CONFLICTING`, and `next` stops before any step and asks the + user to merge the base branch into the sweep branch and push. + ## [0.14.1] - 2026-09-29 ### Fixed diff --git a/plugins/playbooks/skills/repo-sweep/SKILL.md b/plugins/playbooks/skills/repo-sweep/SKILL.md index e69d317185..dc78f4015c 100644 --- a/plugins/playbooks/skills/repo-sweep/SKILL.md +++ b/plugins/playbooks/skills/repo-sweep/SKILL.md @@ -61,7 +61,7 @@ error everywhere; codes 10 and up carry the meanings below; any other non-zero c | `history.sh ` | TSV id, recommendation (`run`, `rerun`, `rerun-optional`), reason, from merged sweep PRs then `Playbook-Step` trailers | 1 catalog error | | `render.sh --checklist []` | The PR checklist block plus `Not run:` | 1 bad id, selection, or TSV | | `render.sh --page ` | The filled selection page on stdout | 1 as above, or template missing | -| `state.sh` | `key value` lines: `pr`, `branch`, `pr-state`, `playbook`, `dirty`, `untick-committed ...`, `done-unverified `, `next in-progress\|pending`, `sweep ` | 0 next step found; 1 no markers; 10 no sweep PR; 11 PR merged or closed; 12 dirty tree, step pending; 13 all done; 14 one open sweep on another branch; 15 several open sweeps | +| `state.sh` | `key value` lines: `pr`, `branch`, `pr-state`, `mergeable CONFLICTING`, `playbook`, `dirty`, `untick-committed ...`, `done-unverified `, `next in-progress\|pending`, `sweep ` | 0 next step found; 1 no markers; 10 no sweep PR; 11 PR merged or closed; 12 dirty tree, step pending; 13 all done; 14 one open sweep on another branch; 15 several open sweeps | | `tick.sh in-progress` / `[--partial ] committed ...` / `no-findings ...` / `partial ...` / `not-applicable ...` / `[--partial ] report-only ...` / `[--partial ] declined ...` | Sets that checklist line, re-reads the body to confirm | 1 line missing, already done, or edit did not land | | `guard.sh ` | Checks a step stayed on the branch and opened no PR | 10 stop (prints `branch-changed`, `base-not-ancestor`, `new-pr` lines); 11 prints `squash git reset --soft ` | diff --git a/plugins/playbooks/skills/repo-sweep/evals/evals.json b/plugins/playbooks/skills/repo-sweep/evals/evals.json index d297d27b43..d6586dbea5 100644 --- a/plugins/playbooks/skills/repo-sweep/evals/evals.json +++ b/plugins/playbooks/skills/repo-sweep/evals/evals.json @@ -75,6 +75,18 @@ "Invokes /plugin-quality:audit for the skill defect", "Files nothing with gh issue create until the user approves each draft" ] + }, + { + "id": 7, + "name": "next-stops-on-conflicting-pr", + "prompt": "/playbooks:repo-sweep next (state.sh exits 0 with a pending step and prints mergeable CONFLICTING)", + "expected_output": "The skill stops before running any step, says GitHub runs no pull_request workflows on a conflicting PR so pushed commits get no CI, and asks the user to merge the base branch into the sweep branch and push, then rerun next.", + "files": [], + "expectations": [ + "Stops on the mergeable CONFLICTING line without running the pending step", + "Names merging the base branch into the sweep branch as the fix", + "Does not stop when mergeable is UNKNOWN" + ] } ] } diff --git a/plugins/playbooks/skills/repo-sweep/reference/next.md b/plugins/playbooks/skills/repo-sweep/reference/next.md index fa317db111..e1f1650a23 100644 --- a/plugins/playbooks/skills/repo-sweep/reference/next.md +++ b/plugins/playbooks/skills/repo-sweep/reference/next.md @@ -31,11 +31,15 @@ never combined with `&&` or other commands in one call. The record for these sha - 15: several open sweeps. List them and ask which. Stop. - 1: the PR body has no checklist markers. Show the body and stop. - 0: continue. -3. For each `untick-committed ...` line, run `S/tick.sh committed +3. If `state.sh` printed `mergeable CONFLICTING` (exit 0, 12, or 13), stop before section 2 and any + step. GitHub runs no `pull_request` workflows on a conflicting PR, so pushed step commits get no + CI. Ask the user to merge the base branch into the sweep branch and push, then rerun `next`. + `UNKNOWN` never stops. +4. For each `untick-committed ...` line, run `S/tick.sh committed ...`; that step already landed. Report each `done-unverified ` line: it was ticked in the web UI and no commit backs it. Run `S/state.sh` again if you ticked anything. -4. The `next in-progress|pending` line names the step. `in-progress` with a dirty tree means +5. The `next in-progress|pending` line names the step. `in-progress` with a dirty tree means resume: the earlier session stopped partway and its changes are the step's work so far. ## 2. Prepare From 4a4905404bf89c1aaa7a5de5d69a0ba262246ffa Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:57:37 -0400 Subject: [PATCH 3/3] fix(playbooks): check the conflicting-PR stop before the exit-code branches Exit 12 and exit 13 branch out of step 2 before the old step 3 ran, so a conflicting sweep PR at the finalize point skipped the stop. Fold the check into step 2 and attach the verification record for the GitHub behavior. Refs #5206 Co-Authored-By: Claude Opus 5.5 --- .../skills/repo-sweep/reference/next.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/playbooks/skills/repo-sweep/reference/next.md b/plugins/playbooks/skills/repo-sweep/reference/next.md index e1f1650a23..b40e97ab72 100644 --- a/plugins/playbooks/skills/repo-sweep/reference/next.md +++ b/plugins/playbooks/skills/repo-sweep/reference/next.md @@ -16,7 +16,14 @@ never combined with `&&` or other commands in one call. The record for these sha 1. On a `chore/repo-sweep-*` branch, bring it current first: `git fetch origin ''`, then `git merge --ff-only 'origin/'`. A step committed on another machine is only reconciled when its commit is local. -2. Run `S/state.sh` and act on its exit code: +2. Run `S/state.sh`. If it printed `mergeable CONFLICTING` (exit 0, 12, or 13), stop before the exit + code handling below and before any step. GitHub runs no `pull_request` workflows on a + conflicting PR, so pushed step commits get no CI. Ask the user to merge the base branch into + the sweep branch and push, then rerun `next`. `UNKNOWN` never stops. Basis: the `pull_request` + section of + + says workflows do not run on pull_request activity while the pull request has a merge conflict. + As of: 2026-09-29. Recheck: that section changes. Otherwise act on its exit code: - 10: no sweep PR. Point to `/playbooks:repo-sweep plan`. Stop. - 11: the sweep PR is merged or closed. Refuse to continue and point to `plan`. Stop. - 12: the tree is dirty and no step is in progress. Show `git status --short`, ask the user @@ -31,15 +38,11 @@ never combined with `&&` or other commands in one call. The record for these sha - 15: several open sweeps. List them and ask which. Stop. - 1: the PR body has no checklist markers. Show the body and stop. - 0: continue. -3. If `state.sh` printed `mergeable CONFLICTING` (exit 0, 12, or 13), stop before section 2 and any - step. GitHub runs no `pull_request` workflows on a conflicting PR, so pushed step commits get no - CI. Ask the user to merge the base branch into the sweep branch and push, then rerun `next`. - `UNKNOWN` never stops. -4. For each `untick-committed ...` line, run `S/tick.sh committed +3. For each `untick-committed ...` line, run `S/tick.sh committed ...`; that step already landed. Report each `done-unverified ` line: it was ticked in the web UI and no commit backs it. Run `S/state.sh` again if you ticked anything. -5. The `next in-progress|pending` line names the step. `in-progress` with a dirty tree means +4. The `next in-progress|pending` line names the step. `in-progress` with a dirty tree means resume: the earlier session stopped partway and its changes are the step's work so far. ## 2. Prepare