From ff65d07cbed67df714dcdf8b61a36bfa984a3555 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:12:01 -0400 Subject: [PATCH 1/2] feat(work-items): ready each PR after create in ship run Under per-item PRs, `/work-items:ship run` now dispatches `/source-control:pull-request ready ` into the item's worktree right after `create --pushed`, at the strong tier's model, the same step `/work-items:work` takes. A refusal leaves the draft and is reported; landing still re-runs ready to bring the branch current, then merges on the user's go-ahead. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6SjPvY7DmXBGzboeCq8nj --- .../feat-ship-run-ready-c7d7b4a5.md | 7 +++++ plugins/work-items/skills/ship/actions/run.md | 12 ++++++--- .../work-items/skills/ship/evals/evals.json | 26 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 .changes/work-items/feat-ship-run-ready-c7d7b4a5.md diff --git a/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md b/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md new file mode 100644 index 0000000000..2248dad852 --- /dev/null +++ b/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md @@ -0,0 +1,7 @@ +--- +bump: minor +--- + +### Changed + +- `/work-items:ship run` under `per-item PRs` dispatches `/source-control:pull-request ready` into the item's worktree right after `create --pushed`, so its pull requests leave draft once that action's checks pass, matching `/work-items:work`. A refusal (a hold, a superseded item, a failed gate) leaves the draft and is reported. diff --git a/plugins/work-items/skills/ship/actions/run.md b/plugins/work-items/skills/ship/actions/run.md index 03cc421fb1..6a51cf976f 100644 --- a/plugins/work-items/skills/ship/actions/run.md +++ b/plugins/work-items/skills/ship/actions/run.md @@ -82,9 +82,15 @@ any run with no human to answer, STOP before any claim and report that `run` is through `/work-items:track done` with a summary naming the branch: the checkpoint. No per-item PR. - **`per-item PRs`.** Each accepted item has a pull request against the default branch; open a - missing one with `/source-control:pull-request create --pushed --worktree `. When the - user says to land it, take it through `/source-control:pull-request ready` (which brings it - current with the default branch) and then `merge`; both keep their own gates and holds. + missing one with `/source-control:pull-request create --pushed --worktree `. **Then + ready it:** right after `create --pushed`, dispatch `/source-control:pull-request ready ` + into the item's worktree with the PR number explicit, at the strong tier's model, never this + session's inherited one, since `ready` merges the base, runs the code and security reviews, + verifies the merged head, and only then flips the draft. When `ready` refuses (a hold, a + superseded item, a failed gate), the PR stays a draft: carry the reason it reported into the + report, and never lift the hold or flip the PR another way. When the user says to land it, + take it through `ready` again (on a PR already out of draft it only brings the branch current + with the default branch) and then `merge`; both keep their own gates and holds. The PR's closing keyword closes the item. Only then start the next PR, which landing the previous one may have made stale. diff --git a/plugins/work-items/skills/ship/evals/evals.json b/plugins/work-items/skills/ship/evals/evals.json index 44c3ddd39f..23de04596e 100644 --- a/plugins/work-items/skills/ship/evals/evals.json +++ b/plugins/work-items/skills/ship/evals/evals.json @@ -123,6 +123,32 @@ "Says run is attended only", "Performs no tracker mutation, dispatch or merge" ] + }, + { + "id": 10, + "name": "ship-run-readies-each-pr-after-create-pushed", + "prompt": "/work-items:ship run #360. The container body carries `**Execution shape:** per-item PRs`. The user approved the item list; dispatch returned item #361 accepted in worktree /work/wt-361 on branch feat/361-export, committed and pushed, and `/source-control:pull-request create --pushed --worktree /work/wt-361` opened draft PR 905. What does the run do next for that item?", + "expected_output": "Right after create --pushed, the run dispatches /source-control:pull-request ready 905 into /work/wt-361, passing the PR number explicitly and a strong-tier model rather than its own inherited one. ready merges the base, runs the code and security reviews, verifies the merged head and flips the draft. The run merges only when the user says to land it, and does not flip the PR itself.", + "files": [], + "expectations": [ + "Dispatches /source-control:pull-request ready after create --pushed, without waiting for the user to say land it", + "Runs it in the item's worktree with PR 905 named explicitly", + "Passes an explicit strong-tier model on that dispatch", + "Never flips the draft with a bare forge command and merges only on the user's go-ahead" + ] + }, + { + "id": 11, + "name": "ship-run-leaves-a-held-pr-in-draft-when-ready-refuses", + "prompt": "/work-items:ship run #360. The container body carries `**Execution shape:** per-item PRs`. create --pushed opened draft PR 906 for item #362, and the run dispatched /source-control:pull-request ready 906, which refused: the PR body has a line `Hold: a human merges this`.", + "expected_output": "PR 906 stays a draft. The run carries ready's refusal reason (the Hold: line) into its report row for #362, does not edit the body or flip the PR another way, and does not merge it.", + "files": [], + "expectations": [ + "PR 906 is left in draft", + "The report names the Hold: line as the reason ready refused", + "The hold is not lifted and the PR is not flipped by any other route", + "The PR is not merged" + ] } ] } From f086e5fdbf5d32672cb6aebd7cb2be4d20867b6a Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:22:28 -0400 Subject: [PATCH 2/2] fix(work-items): correct ready's behavior and eval case 11 in ship run A `Hold:` body line holds the merge only, so case 11 now refuses on a superseded item. On a PR already out of draft, `ready` re-runs the reviews and verify gate, not only a base refresh. The dispatch brief names the PR create returned and stops on a mismatch, resolves the strong tier per the loop-lane convention, and is skipped when the consuming project reserves the flip. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6SjPvY7DmXBGzboeCq8nj --- .../feat-ship-run-ready-c7d7b4a5.md | 2 +- plugins/work-items/skills/ship/actions/run.md | 25 +++++++++++-------- .../work-items/skills/ship/evals/evals.json | 14 +++++------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md b/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md index 2248dad852..0523776005 100644 --- a/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md +++ b/.changes/work-items/feat-ship-run-ready-c7d7b4a5.md @@ -4,4 +4,4 @@ bump: minor ### Changed -- `/work-items:ship run` under `per-item PRs` dispatches `/source-control:pull-request ready` into the item's worktree right after `create --pushed`, so its pull requests leave draft once that action's checks pass, matching `/work-items:work`. A refusal (a hold, a superseded item, a failed gate) leaves the draft and is reported. +- `/work-items:ship run` under `per-item PRs` dispatches `/source-control:pull-request ready` into the item's worktree right after `create --pushed`, so its pull requests leave draft once that action's checks pass. A refusal (a hold, a superseded item, a failed gate) leaves the draft and is reported. diff --git a/plugins/work-items/skills/ship/actions/run.md b/plugins/work-items/skills/ship/actions/run.md index 6a51cf976f..48f7008d64 100644 --- a/plugins/work-items/skills/ship/actions/run.md +++ b/plugins/work-items/skills/ship/actions/run.md @@ -82,17 +82,20 @@ any run with no human to answer, STOP before any claim and report that `run` is through `/work-items:track done` with a summary naming the branch: the checkpoint. No per-item PR. - **`per-item PRs`.** Each accepted item has a pull request against the default branch; open a - missing one with `/source-control:pull-request create --pushed --worktree `. **Then - ready it:** right after `create --pushed`, dispatch `/source-control:pull-request ready ` - into the item's worktree with the PR number explicit, at the strong tier's model, never this - session's inherited one, since `ready` merges the base, runs the code and security reviews, - verifies the merged head, and only then flips the draft. When `ready` refuses (a hold, a - superseded item, a failed gate), the PR stays a draft: carry the reason it reported into the - report, and never lift the hold or flip the PR another way. When the user says to land it, - take it through `ready` again (on a PR already out of draft it only brings the branch current - with the default branch) and then `merge`; both keep their own gates and holds. - The PR's closing keyword closes the item. Only then start the next PR, which landing the - previous one may have made stale. + missing one with `/source-control:pull-request create --pushed --worktree `. **Then ready + it:** right after `create --pushed`, unless the consuming project's instructions reserve the + ready flip for a person, dispatch `/source-control:pull-request ready` into the item's worktree, + at the strong tier's current alias resolved per the loop-lane convention's capability tiers, + never this session's inherited model. `ready` resolves the PR from the branch checked out there, + so the brief names the PR number `create --pushed` returned and the dispatched agent stops when + `ready` resolves a different one. The strong tier fits because `ready` merges the base, runs the + code and security reviews, verifies the merged head, and only then flips the draft. When `ready` + refuses (a hold, a superseded item, a failed gate), the PR stays a draft: carry the reason it + reported into the report, and never lift the hold or flip the PR another way. When the user says + to land it, take it through `ready` again (on a PR already out of draft it refreshes the base + and re-runs the reviews and the verify gate on the merged head, without flipping again) and then + `merge`; both keep their own gates and holds. The PR's closing keyword closes the item. Only + then start the next PR, which landing the previous one may have made stale. 1. **Next round.** Drop the landed entries, release the claims of stopped items and report why each stopped, re-read the frontier (closing items advances it), add newly ready items through diff --git a/plugins/work-items/skills/ship/evals/evals.json b/plugins/work-items/skills/ship/evals/evals.json index 23de04596e..2b1df68da0 100644 --- a/plugins/work-items/skills/ship/evals/evals.json +++ b/plugins/work-items/skills/ship/evals/evals.json @@ -128,25 +128,25 @@ "id": 10, "name": "ship-run-readies-each-pr-after-create-pushed", "prompt": "/work-items:ship run #360. The container body carries `**Execution shape:** per-item PRs`. The user approved the item list; dispatch returned item #361 accepted in worktree /work/wt-361 on branch feat/361-export, committed and pushed, and `/source-control:pull-request create --pushed --worktree /work/wt-361` opened draft PR 905. What does the run do next for that item?", - "expected_output": "Right after create --pushed, the run dispatches /source-control:pull-request ready 905 into /work/wt-361, passing the PR number explicitly and a strong-tier model rather than its own inherited one. ready merges the base, runs the code and security reviews, verifies the merged head and flips the draft. The run merges only when the user says to land it, and does not flip the PR itself.", + "expected_output": "Right after create --pushed, the run dispatches /source-control:pull-request ready into /work/wt-361, with a brief naming PR 905 so the dispatched agent stops if ready resolves a different PR, and a strong-tier model rather than its own inherited one. ready merges the base, runs the code and security reviews, verifies the merged head and flips the draft. The run merges only when the user says to land it, and does not flip the PR itself.", "files": [], "expectations": [ "Dispatches /source-control:pull-request ready after create --pushed, without waiting for the user to say land it", - "Runs it in the item's worktree with PR 905 named explicitly", + "Runs it in the item's worktree, with the brief naming PR 905 as the PR ready must resolve", "Passes an explicit strong-tier model on that dispatch", "Never flips the draft with a bare forge command and merges only on the user's go-ahead" ] }, { "id": 11, - "name": "ship-run-leaves-a-held-pr-in-draft-when-ready-refuses", - "prompt": "/work-items:ship run #360. The container body carries `**Execution shape:** per-item PRs`. create --pushed opened draft PR 906 for item #362, and the run dispatched /source-control:pull-request ready 906, which refused: the PR body has a line `Hold: a human merges this`.", - "expected_output": "PR 906 stays a draft. The run carries ready's refusal reason (the Hold: line) into its report row for #362, does not edit the body or flip the PR another way, and does not merge it.", + "name": "ship-run-leaves-a-superseded-pr-in-draft-when-ready-refuses", + "prompt": "/work-items:ship run #360. The container body carries `**Execution shape:** per-item PRs`. create --pushed opened draft PR 906 for item #362, and the run dispatched /source-control:pull-request ready for PR 906, which refused: item #362, the issue the PR closes, is already closed, so the PR is superseded.", + "expected_output": "PR 906 stays a draft. The run carries ready's refusal reason (item #362 already closed, so the PR is superseded) into its report row for #362, does not reopen the item or flip the PR another way, and does not merge it.", "files": [], "expectations": [ "PR 906 is left in draft", - "The report names the Hold: line as the reason ready refused", - "The hold is not lifted and the PR is not flipped by any other route", + "The report names the already-closed item #362 (superseded) as the reason ready refused", + "The item is not reopened and the PR is not flipped by any other route", "The PR is not merged" ] }