cleanup
Clean up after a merged pull request — return to the base branch, remove the worktree, delete the branch, sweep other stale branches and worktrees, and report…
Dispatch a night's batch of Todo tickets — one worktree and one pull request each — and leave a morning summary. Run explicitly; never inferred.
$ npx -y skills add mikestankavich/claude-ship-workflow --skill batch --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/batchContext preview
The summary Claude sees to decide when to auto-load this skill.
Dispatch a night's batch of Todo tickets — one worktree and one pull request each — and leave a morning summary. Run explicitly; never inferred.
name: batch description: Dispatch a night's batch of Todo tickets — one worktree and one pull request each — and leave a morning summary. Run explicitly; never inferred. argument-hint: "[max-tickets] [--dry-run]" disable-model-invocation: true
**Announce at start:** "Using csw:batch to dispatch tonight's tickets."
Tonight's invocation carried: $ARGUMENTS
Two modifiers, and they compose:
Step 3.
So `/csw:batch 2 --dry-run` shows tonight's plan cut at 2 without dispatching anything. Anything else in $ARGUMENTS is not a modifier: say what you ignored and why, rather than guessing at what it meant.
This loop's first filter is "drop blocked tickets," and it only works if the tracker knows what blocks what. If `blockedBy` is empty on most tickets while `relatedTo` carries three or four links each, the dependency information exists but is in the wrong field and in prose.
**Backfill blocking relations on the Todo column before relying on this.** Check a sample first, and if `blockedBy` is empty across the board, say so and stop rather than dispatching a batch whose ordering constraints are invisible.
**Prepped tickets dispatch better.** `/csw:prep <ticket>` leaves a spec and its open questions on the ticket, and `csw:work` Step 2 reads them, so a question that would have cost a whole dispatch to discover is already answered when the loop reaches it. It is not required, and this loop **does not skip unprepped** tickets — an optional command that silently became a gate would be a new manual step in front of every ticket in the column.
Either way, this step ends with `candidates_json` holding a JSON array in the shape Step 2 pipes into `csw-batch-filter`:
[
{
"id": "ENG-1075",
"state": "Todo",
"priority": 2,
"labels": ["migration"],
"blockedBy": [],
"relatedTo": ["ENG-1080", "ENG-1081"]
}
]Which way depends on one key:
csw-config get trackerCommand
**Empty — read the tracker.** Read every Todo ticket from the tracker named by `csw-config get tracker` and shape it into the array above yourself. This is the default, and it is what `tracker: linear` does through MCP.
**Non-empty — run it, and its stdout _is the filter's input_.** Do not reshape it, do not re-read the tracker, do not merge anything into it. Skipping the in-context reshaping across a whole column of tickets is the entire reason the key exists — reshaping the output anyway keeps the failure mode it was added to remove:
candidates_json=$(bash -c "$(csw-config get trackerCommand)")
same way `validate` and `gates[].run` are already run.
csw-batch-filter` would report the *filter's* status, not the command's, so a failing command that printed a partial array reads as a selection rather than a failure. A non-zero exit is a **failed selection**: quote its stderr verbatim, say plainly that selection failed and no tickets were dispatched, and stop — exactly as Step 2 does for the filter itself. Do not continue to Step 3.
naming the offending field, and Step 2's existing arm turns that into a failed selection. A second shape check here only drifts from the one that matters. Pass the stdout through unread and let the filter speak.
filter's `no input on stdin` and exits 2, which is a failed selection. A command with nothing to report must print `[]`.
`csw-batch-filter` keeps reading `tracker` to rank by priority, so someone shelling out to a Linear GraphQL query still sets `tracker: linear` or gets the wrong order silently.
command too — skipping it would leave a dry run with no plan to report. Step 3's "no side effects" promise covers CSW's own actions, not what an arbitrary configured command does.
printf '%s' "$candidates_json" | csw-batch-filter
Three filters, none of which is guesswork:
1. **Blocked and not-Todo** tickets are dropped. 2. **Same-surface clusters** keep their highest-priority member. Two tickets on one nav surface do not conflict textually, but a same-surface audit rule means the agent doing one properly lands in the other's copy. 3. **Single-writer labels** admit one ticket per batch. Two migration-adding tickets each write the next number in a global sequence and both regenerate the checksums file; neither PR is wrong, both validated against the base, and one still has to be redone rather than rebased.
A fourth exclusion, read off the ticket rather than computed by the filter:
4. **A decide-shaped ticket with no answer on it is not dispatched.** Where the description asks for a decision — "decide whether", "choose between", "work out what X is for" — and the ticket carries no `**CSW prep**` comment, or carries one whose `## Open questions` is not `_None._`, move it to `skipped` with that as its reason.
It is a candidate for `/csw:prep` or `/csw:work interactive`, not for a dispatch at 3am. Prep already ends by declaring a ticket dispatchable or not; this is the step that reads the verdict, and without it the verdict is computed and thrown away. An unattended dispatch handed a decision to make will make one, and nobody agreed to it — the expensive versi
CSW takes a ticket from your tracker to a merged pull request, then cleans up after itself. Superpowers owns how the work gets done. CSW owns *how it gets shipped and closed out*. This is one person's idiosyncratic workflow, and it is probably not yours.
Clean up after a merged pull request — return to the base branch, remove the worktree, delete the branch, sweep other stale branches and worktrees, and report…
Merge the reviewed pull request for the current branch, then hand straight off to cleanup. A sentence of editorial direction alongside the green light is read…
Spec a ticket before it is dispatched. Brainstorms it, decides everything it can recommend an answer for, asks a present human about the little that survives,…
Dispatch a tracker ticket into an isolated worktree and drive it autonomously to an open pull request, then stop for review. Pass `interactive` to brainstorm…