ship-issues: batch orchestrator
Drive a set of work-units to open PRs concurrently, chaining the ship-it stage skills per work-unit, then keep the project's living docs current. Generic: every project specific lives in ship-it.config; the flow does not. Load the resolved config once up front with ${CLAUDE_PLUGIN_ROOT}/scripts/load-config.sh (defaults applied, @FILE refs like houseRules: @AGENTS.md inlined) and read keys with jq. Read references/workflow.md before Phase 5 (the lane algorithm and the Workflow template to adapt).
Phase 1: Resolve the work-units
If the trigger names no issue selection, ask first (do not default to a status like "all todo"): use AskUserQuestion to ask what to ship, a status, a range, specific ids, or the current changes, then resolve. When a selection is given, read config.source and expand it into concrete work-units:
tracker: list/get issues via the tracker adapter (config.source.tracker: type + project/team/idPrefix, or a custom resolver skill). A status ("all todo"), a range ("111-120"), or an explicit list. Each work-unit carries id, title, desc, branch (the tracker's branch name, which carries the auto-close link), url.
working-tree / branch / pr / describe: build a single work-unit from local state or a description.
Read each item's full intent. If any is ambiguous in a way that changes the implementation, batch the open questions into one AskUserQuestion now. All interactivity is front-loaded here and at the Phase 3 checkpoint. See references/sources.md for the source dispatch, the built-in tracker adapters (github-issues, linear), the local sources (working-tree / branch / pr / describe), and the custom-tracker extension point.
Trigger tokens. Three optional switches, anywhere in the trigger: a skip-confirmation token (skip confirm, no confirm, no checkpoint, without asking, just do it, yolo, -y, --yes) turns off the Phase 3 checkpoint; a skip-docs token (no docs, skip docs, code only, no specs) turns off the whole documentation phase (Phase 6) for this run, regardless of config.docs.enabled; a skip-planning token (no plan, skip plan, skip planning) turns off the Phase 2 plan pass (it falls back to a thin scope pass), regardless of config.planning.enabled.
Phase 2: Plan and group into lanes
- Plan pass (parallel, read-only): unless
config.planning.enabled is false (default true) or a skip-planning token is in the trigger, produce one comprehensive plan per work-unit, concurrently. Spawn each as a Plan agent (the built-in architect subagent: subagent_type: 'Plan', read-only, high reasoning effort) invoking ship-it:plan-one-issue, so each plan rivals an interactive plan-mode plan (it reads the real code, weighs alternatives, names reuse, and lays out decisions, edge cases, verification, and risks) at the depth set by config.planning.depth. Each returns { plan, predictedFiles, docNeed, complexity, openQuestions }. If planning is off, fall back to a thin scope pass: one Explore subagent per work-unit for predicted files, a one-line approach, and the doc classification. Either way, read to plan, do not implement.
- Post each plan as a proposal (if
config.planning.postBack): post each work-unit's plan back to its source as a proposed plan now, so it is reviewable on the tracker and not only in the terminal (idempotent, marker-keyed; see references/sources.md). Local sources have no tracker, so the plan stays on screen.
- Group into lanes by the overlap-graph algorithm in
references/workflow.md (connected components of "edit the same file"), using each work-unit's predictedFiles. Lanes run concurrently; within a lane, work-units run sequentially on stacked branches.
- Branch + base per work-unit: lane head off
config.repo.mainBranch; a stacked child off its parent branch.
Fold any openQuestions the plan pass surfaced into the Phase 3 checkpoint, or resolve blocking ones with one AskUserQuestion before it.
Phase 3: Checkpoint on the plan
Unless a skip-confirmation token is in the trigger, show the plan and wait: the resolved work-units, each work-unit's plan (already posted to its tracker issue as a proposal in Phase 2, so you can review it there or in the terminal), the lanes (concurrent vs stacked), the predicted files, the predicted doc jobs, and a note that verification is config.verify and any config.safety rails apply.
Handle the response:
- Feedback: revise the affected work-unit's
plan, update its proposal comment in place via postPlan (same marker, so no duplicate), and re-show the change. Iterate until approved.
- Approval: finalize each plan's comment (
postPlan with status approved) and proceed.
- Skip-confirmation token: the plans are already posted as proposals; finalize them and proceed without pausing.
postPlan is idempotent and non-blocking: the proposal, every revision, and the final approval all update the same comment; a failed post is reported but never blocks the run. Local sources (working-tree / branch / pr / describe) have no tracker, so the plan stays on screen.
Phase 4: Pre-create lane-head worktrees
If config.worktree.enabled, create each lane head's worktree under config.worktree.root, sequentially (concurrent git worktree add races the index lock), and make each runnable with config.worktree.prepare. Stacked children are created lazily by their own fix-one-issue step off the parent branch, after the parent commits. Use ${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktrees.sh --root <config.worktree.root> --prepare "<config.worktree.prepare>", feeding one <id>|<branch>|<base> line per lane head on stdin.
Phase 5: Run the batch
Adapt the Workflow template in references/workflow.md (fill in the resolved lanes, the path to ship-it.config, config.concurrency.maxLanes as the lane-concurrency cap, and the author-reconcile entries of config.docs.jobs so their artifacts are authored on-branch and ride the PR), then launch it with the Workflow tool. Per work-unit the pipeline chains the stage skills (it never reimplements them):
ship-it:fix-one-issue (creating the worktree first if a stacked child), implementing against the work-unit's plan when present (a stacked child first reconciles its plan with the parent's changes already on its base), then commit. Returns addedComments and docNeed.
ship-it:comment-cleanup in apply mode, scoped to the work-unit's commit range, only if addedComments. Code-modifying, so sequential within a lane; concurrent across lanes.
ship-it:review-and-address over the work-unit's diff (fans out config.review.reviewers, merges findings, applies warranted per config.review.applyWarranted).
- Author-reconcile docs, only when a
config.docs.jobs entry with mechanic author-reconcile matches the work-unit's docNeed: author the artifact on-branch by invoking the job's ref (e.g. openspec-propose) and commit it, so it rides the PR rather than being bolted on later. See references/doc-jobs.md.
ship-it:open-pr: push the branch and open a PR per config.prTemplate (base main for a lane head, the parent branch for a stacked child; GitHub auto-retargets a stacked PR to main when its parent merges).
Each result carries docNeed (the doc jobs it triggers) for Phase 6.
Phase 6: Documentation (doc-job fan-out)
Skip this entire phase if a skip-docs token was in the trigger, or if config.docs.enabled is false. Otherwise read references/doc-jobs.md for the mechanics and built-in jobs. After the Workflow returns, run the doc phase: for each job in config.docs.jobs, run the ones whose trigger (appliesWhen) matched a shipped work-unit, in parallel (each owns a different file). By mechanic:
- regenerate: deferred to Phase 7 (post-merge), because the doc is re-derived from the merged code, not the pre-merge base. Do not run it here.
- author-reconcile: the per-work-unit artifact was already authored on its branch in Phase 5 (it rides the PR); only the reconcile into canonical docs is deferred to post-merge (Phase 7).
- curate-serial: update the shared prose doc once, serially (e.g.
impeccable for DESIGN.md). If exactly one work-unit triggered it, commit it onto that work-unit's open PR (rides the PR, atomic doc + code); if several, one consolidated pass on a separate docs branch.
Skip jobs with no matching change. Do not over-document.
Phase 7: Post-PR watchers (in-session, optional)
If config.ci.watch, spawn one ship-it:ci-fix background watcher per PR. If the post-merge doc work produces a tracked change (an author-reconcile archive that edits committed specs, or a regenerate whose target is tracked), launch the merge watcher ${CLAUDE_PLUGIN_ROOT}/scripts/watch-merges.sh --prs <csv> --reconcile "<cmd>", where <cmd> composes the post-merge work joined with &&: any author-reconcile reconcile (e.g. ${CLAUDE_PLUGIN_ROOT}/scripts/openspec-archive.sh <change-ids>) and any regenerate command (e.g. graphify update .). It polls until the PRs are merged (timeout), then runs that command against the merged code (archiving specs, regenerating derived docs) and opens the batched docs PR. On timeout, it prints the manual command. If the only post-merge job is a regenerate to a gitignored target (e.g. graphify-out/), there is nothing to PR, so skip the watcher and just note the manual graphify update . follow-up. See references/doc-jobs.md.
Phase 8: Summarize
Print a table: work-unit, PR link, lane, what changed, review items applied/skipped, doc outcome, blockers. If config.worktree.enabled, note that each worktree is left runnable for visual/runtime QA and surface any config.worktree.qaNotes (run caveats). Point the user at the post-merge follow-ups, both merged-gated: the doc reconcile (Phase 7) and worktree cleanup via ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-worktrees.sh --all-merged.
Guardrails
- Edit only inside each work-unit's worktree; never the main checkout.
- Honor
config.houseRules + config.safety everywhere (commits, PR text, edits).
- Verification is
config.verify.
- Create worktrees sequentially, then fan out.
- Front-load all interactivity to Phase 1 and Phase 3; the Workflow workers run non-interactively.
- Invoke the stage skills by their namespaced names (
ship-it:fix-one-issue, ship-it:comment-cleanup, ship-it:review-and-address, ship-it:open-pr); never reimplement their logic inline.
Bundled files
references/sources.md - how Phase 1 resolves the trigger into work-units (source dispatch, the built-in tracker adapters, the custom-tracker extension point) and the tracker adapter's postPlan used in Phase 3. Read before Phase 1.
references/workflow.md - the lane-grouping algorithm and the Workflow script template to adapt. Read before Phase 5.
references/doc-jobs.md - the three doc-job mechanics, the built-in jobs, and the post-merge reconcile flow. Read before Phase 6.
${CLAUDE_PLUGIN_ROOT}/scripts/openspec-archive.sh - the openspec author-reconcile reconcile (merged-gated openspec archive, batched docs PR).
${CLAUDE_PLUGIN_ROOT}/scripts/watch-merges.sh - the in-session merge watcher that runs a --reconcile command once the PRs merge.
${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktrees.sh - pre-create lane-head worktrees (stdin <id>|<branch>|<base>), running the project prepare per worktree. Read before Phase 4.
${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-worktrees.sh - merged-gated worktree + branch teardown. Phase 8 / post-merge.
${CLAUDE_PLUGIN_ROOT}/scripts/load-config.sh - locate + resolve ship-it.config (defaults + @FILE inlining) to JSON. Load once up front.
1---2name: ship-issues3description: ship-issues: batch orchestrator4---56# ship-issues: batch orchestrator78Drive a set of work-units to open PRs concurrently, chaining the ship-it stage skills per work-unit, then keep the project's living docs current. Generic: every project specific lives in `ship-it.config`; the flow does not. Load the resolved config once up front with `${CLAUDE_PLUGIN_ROOT}/scripts/load-config.sh` (defaults applied, `@FILE` refs like `houseRules: @AGENTS.md` inlined) and read keys with `jq`. Read `references/workflow.md` before Phase 5 (the lane algorithm and the Workflow template to adapt).910## Phase 1: Resolve the work-units1112**If the trigger names no issue selection, ask first** (do not default to a status like "all todo"): use `AskUserQuestion` to ask what to ship, a status, a range, specific ids, or the current changes, then resolve. When a selection is given, read `config.source` and expand it into concrete work-units:13- **`tracker`**: list/get issues via the tracker adapter (`config.source.tracker`: type + project/team/idPrefix, or a custom resolver skill). A status ("all todo"), a range ("111-120"), or an explicit list. Each work-unit carries `id`, `title`, `desc`, `branch` (the tracker's branch name, which carries the auto-close link), `url`.14- **`working-tree` / `branch` / `pr` / `describe`**: build a single work-unit from local state or a description.1516Read each item's full intent. If any is ambiguous in a way that changes the implementation, batch the open questions into one `AskUserQuestion` now. All interactivity is front-loaded here and at the Phase 3 checkpoint. See `references/sources.md` for the source dispatch, the built-in tracker adapters (`github-issues`, `linear`), the local sources (working-tree / branch / pr / describe), and the custom-tracker extension point.1718**Trigger tokens.** Three optional switches, anywhere in the trigger: a **skip-confirmation** token (`skip confirm`, `no confirm`, `no checkpoint`, `without asking`, `just do it`, `yolo`, `-y`, `--yes`) turns off the Phase 3 checkpoint; a **skip-docs** token (`no docs`, `skip docs`, `code only`, `no specs`) turns off the whole documentation phase (Phase 6) for this run, regardless of `config.docs.enabled`; a **skip-planning** token (`no plan`, `skip plan`, `skip planning`) turns off the Phase 2 plan pass (it falls back to a thin scope pass), regardless of `config.planning.enabled`.1920## Phase 2: Plan and group into lanes21221. **Plan pass** (parallel, read-only): unless `config.planning.enabled` is false (default true) or a skip-planning token is in the trigger, produce one comprehensive plan per work-unit, concurrently. Spawn each as a **`Plan` agent** (the built-in architect subagent: `subagent_type: 'Plan'`, read-only, high reasoning effort) invoking **`ship-it:plan-one-issue`**, so each plan rivals an interactive plan-mode plan (it reads the real code, weighs alternatives, names reuse, and lays out decisions, edge cases, verification, and risks) at the depth set by `config.planning.depth`. Each returns `{ plan, predictedFiles, docNeed, complexity, openQuestions }`. If planning is off, fall back to a thin **scope pass**: one `Explore` subagent per work-unit for predicted files, a one-line approach, and the doc classification. Either way, read to plan, do not implement.232. **Post each plan as a proposal** (if `config.planning.postBack`): post each work-unit's plan back to its source as a **proposed** plan now, so it is reviewable on the tracker and not only in the terminal (idempotent, marker-keyed; see `references/sources.md`). Local sources have no tracker, so the plan stays on screen.243. **Group into lanes** by the overlap-graph algorithm in `references/workflow.md` (connected components of "edit the same file"), using each work-unit's `predictedFiles`. Lanes run concurrently; within a lane, work-units run sequentially on stacked branches.254. Branch + base per work-unit: lane head off `config.repo.mainBranch`; a stacked child off its parent branch.2627Fold any `openQuestions` the plan pass surfaced into the Phase 3 checkpoint, or resolve blocking ones with one `AskUserQuestion` before it.2829## Phase 3: Checkpoint on the plan3031Unless a skip-confirmation token is in the trigger, show the plan and wait: the resolved work-units, **each work-unit's plan** (already posted to its tracker issue as a proposal in Phase 2, so you can review it there or in the terminal), the lanes (concurrent vs stacked), the predicted files, the predicted doc jobs, and a note that verification is `config.verify` and any `config.safety` rails apply.3233Handle the response:34- **Feedback**: revise the affected work-unit's `plan`, **update its proposal comment in place** via `postPlan` (same marker, so no duplicate), and re-show the change. Iterate until approved.35- **Approval**: **finalize** each plan's comment (`postPlan` with status `approved`) and proceed.36- **Skip-confirmation token**: the plans are already posted as proposals; finalize them and proceed without pausing.3738`postPlan` is idempotent and non-blocking: the proposal, every revision, and the final approval all update the same comment; a failed post is reported but never blocks the run. Local sources (working-tree / branch / pr / describe) have no tracker, so the plan stays on screen.3940## Phase 4: Pre-create lane-head worktrees4142If `config.worktree.enabled`, create each lane head's worktree under `config.worktree.root`, **sequentially** (concurrent `git worktree add` races the index lock), and make each runnable with `config.worktree.prepare`. Stacked children are created lazily by their own `fix-one-issue` step off the parent branch, after the parent commits. Use `${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktrees.sh --root <config.worktree.root> --prepare "<config.worktree.prepare>"`, feeding one `<id>|<branch>|<base>` line per lane head on stdin.4344## Phase 5: Run the batch4546Adapt the Workflow template in `references/workflow.md` (fill in the resolved lanes, the path to `ship-it.config`, `config.concurrency.maxLanes` as the lane-concurrency cap, and the **author-reconcile** entries of `config.docs.jobs` so their artifacts are authored on-branch and ride the PR), then launch it with the **Workflow** tool. Per work-unit the pipeline chains the stage skills (it never reimplements them):47481. **`ship-it:fix-one-issue`** (creating the worktree first if a stacked child), implementing against the work-unit's `plan` when present (a stacked child first reconciles its plan with the parent's changes already on its base), then commit. Returns `addedComments` and `docNeed`.492. **`ship-it:comment-cleanup`** in apply mode, scoped to the work-unit's commit range, only if `addedComments`. Code-modifying, so sequential within a lane; concurrent across lanes.503. **`ship-it:review-and-address`** over the work-unit's diff (fans out `config.review.reviewers`, merges findings, applies warranted per `config.review.applyWarranted`).514. **Author-reconcile docs**, only when a `config.docs.jobs` entry with mechanic `author-reconcile` matches the work-unit's `docNeed`: author the artifact on-branch by invoking the job's `ref` (e.g. `openspec-propose`) and commit it, so it **rides the PR** rather than being bolted on later. See `references/doc-jobs.md`.525. **`ship-it:open-pr`**: push the branch and open a PR per `config.prTemplate` (base `main` for a lane head, the parent branch for a stacked child; GitHub auto-retargets a stacked PR to main when its parent merges).5354Each result carries `docNeed` (the doc jobs it triggers) for Phase 6.5556## Phase 6: Documentation (doc-job fan-out)5758**Skip this entire phase if a skip-docs token was in the trigger**, or if `config.docs.enabled` is false. Otherwise read `references/doc-jobs.md` for the mechanics and built-in jobs. After the Workflow returns, run the doc phase: for each job in `config.docs.jobs`, run the ones whose trigger (`appliesWhen`) matched a shipped work-unit, in parallel (each owns a different file). By mechanic:59- **regenerate**: deferred to Phase 7 (post-merge), because the doc is re-derived from the merged code, not the pre-merge base. Do not run it here.60- **author-reconcile**: the per-work-unit artifact was already authored on its branch in Phase 5 (it rides the PR); only the reconcile into canonical docs is deferred to post-merge (Phase 7).61- **curate-serial**: update the shared prose doc once, serially (e.g. `impeccable` for DESIGN.md). If exactly one work-unit triggered it, commit it onto that work-unit's open PR (rides the PR, atomic doc + code); if several, one consolidated pass on a separate docs branch.6263Skip jobs with no matching change. Do not over-document.6465## Phase 7: Post-PR watchers (in-session, optional)6667If `config.ci.watch`, spawn one **`ship-it:ci-fix`** background watcher per PR. If the post-merge doc work produces a **tracked** change (an author-reconcile archive that edits committed specs, or a regenerate whose `target` is tracked), launch the merge watcher `${CLAUDE_PLUGIN_ROOT}/scripts/watch-merges.sh --prs <csv> --reconcile "<cmd>"`, where `<cmd>` composes the post-merge work joined with `&&`: any author-reconcile reconcile (e.g. `${CLAUDE_PLUGIN_ROOT}/scripts/openspec-archive.sh <change-ids>`) and any regenerate command (e.g. `graphify update .`). It polls until the PRs are merged (timeout), then runs that command against the merged code (archiving specs, regenerating derived docs) and opens the batched docs PR. On timeout, it prints the manual command. If the only post-merge job is a regenerate to a **gitignored** target (e.g. `graphify-out/`), there is nothing to PR, so skip the watcher and just note the manual `graphify update .` follow-up. See `references/doc-jobs.md`.6869## Phase 8: Summarize7071Print a table: work-unit, PR link, lane, what changed, review items applied/skipped, doc outcome, blockers. If `config.worktree.enabled`, note that each worktree is left runnable for visual/runtime QA and surface any `config.worktree.qaNotes` (run caveats). Point the user at the post-merge follow-ups, both merged-gated: the doc reconcile (Phase 7) and worktree cleanup via `${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-worktrees.sh --all-merged`.7273## Guardrails7475- Edit only inside each work-unit's worktree; never the main checkout.76- Honor `config.houseRules` + `config.safety` everywhere (commits, PR text, edits).77- Verification is `config.verify`.78- Create worktrees sequentially, then fan out.79- Front-load all interactivity to Phase 1 and Phase 3; the Workflow workers run non-interactively.80- Invoke the stage skills by their namespaced names (`ship-it:fix-one-issue`, `ship-it:comment-cleanup`, `ship-it:review-and-address`, `ship-it:open-pr`); never reimplement their logic inline.8182## Bundled files8384- `references/sources.md` - how Phase 1 resolves the trigger into work-units (source dispatch, the built-in tracker adapters, the custom-tracker extension point) and the tracker adapter's `postPlan` used in Phase 3. Read before Phase 1.85- `references/workflow.md` - the lane-grouping algorithm and the Workflow script template to adapt. Read before Phase 5.86- `references/doc-jobs.md` - the three doc-job mechanics, the built-in jobs, and the post-merge reconcile flow. Read before Phase 6.87- `${CLAUDE_PLUGIN_ROOT}/scripts/openspec-archive.sh` - the openspec author-reconcile reconcile (merged-gated `openspec archive`, batched docs PR).88- `${CLAUDE_PLUGIN_ROOT}/scripts/watch-merges.sh` - the in-session merge watcher that runs a `--reconcile` command once the PRs merge.89- `${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktrees.sh` - pre-create lane-head worktrees (stdin `<id>|<branch>|<base>`), running the project prepare per worktree. Read before Phase 4.90- `${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-worktrees.sh` - merged-gated worktree + branch teardown. Phase 8 / post-merge.91- `${CLAUDE_PLUGIN_ROOT}/scripts/load-config.sh` - locate + resolve `ship-it.config` (defaults + `@FILE` inlining) to JSON. Load once up front.