--- name: multi-spec description: "Turn a shape-decision decisions file into a batch of OpenSpec change proposals, committed one change at a time, reviewed once as a batch, then opened as one PR. Proposals only, no implementation. Triggers: /cla:multi-spec, 'turn these decisions into changes', 'batch-propose the shaped decisions'." argument-hint: "[decisions-file-path | (empty = most recent under cla.io/decisions/)]" --- # /cla:multi-spec — decisions file → batch of committed OpenSpec proposals Automates a manual precedent: one shape-decision output authored out into N full OpenSpec change proposals, reviewed once as a batch, opened as one PR. **Proposals only** — implementation is `/cla:spec-to-pr` (per-change) or `/cla:multi-pr` (the whole produced sequence), invoked separately afterward. **Resolving `${CLAUDE_PLUGIN_ROOT}`.** This `SKILL.md` arrives with the placeholder substituted, but a `references/` file opened with `Read` carries it literally, and it is not an environment variable in Bash — an unset variable silently runs the command against `/skills/...`. Before running a command that contains the literal text, replace it with the plugin root: the `Base directory for this skill` path with `/skills/` removed, or the absolute path of any plugin file you have read, cut at `.../plugins/cla`. If neither works, say so and stop. Detail: `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/plugin-root.md`. **The durability requirement is the reason this skill exists as more than a loop over `openspec-propose`.** A batch of N proposal directories sitting uncommitted for the whole run is a real, already-realized loss mode while it sits untracked. Every phase below is ordered to keep the loss window to at most one change. ## Skill-level rules (hoisted — read first) - **`` means THIS repo's default branch, resolved — never assumed.** See `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/base-branch-resolution.md` (shared verbatim with `multi-lite`/`multi-pr`) for the resolution rule every command below that names `` depends on. - **Commit each change immediately after authoring and validating it — never batch commits to the end.** One `git commit` (and `git push`) per change, before starting the next. This is not a style preference; it is the actual anti-data-loss mechanism. - **Never end a turn with nothing in flight.** This skill has no "ready to continue?" rule to lean on, and its seam is sharper than the other chains': the per-change dispatch is awaited, so something IS pending for most of the loop — but the window between one change's commit/push post-check and the next change's dispatch has nothing pending at all, and that is where a turn ends silently. The distinction that matters is not "did I ask a question" but **"is there a pending event that will re-invoke this session?"** A backgrounded `Agent` or `Bash(run_in_background: true)` is safe, because its completion notification wakes the session; an awaited `Agent` call is safe because a tool call does not end the turn. An idle turn has no wake signal. **The mechanical form: status text and the next tool call go in the SAME message.** If you have no tool call to pair the text with, the change is not over — start the next one instead of narrating the last. Ending a turn is legitimate in exactly two cases, the same two `${CLAUDE_PLUGIN_ROOT}/skills/spec-to-pr/SKILL.md` names: a backgrounded dispatch is genuinely in flight, or the run is complete. Nothing else — **announcing the next step is not a mechanism**, and "Is there a tool call in this message?" needs no judgement. A blocker is not a third case: it goes out as an `AskUserQuestion`, which is itself a tool call. Source for the cost of this gap: GitHub issue #130, on a sibling chain skill. - **Never run `git add -A`.** Path-scope every stage (per Phase, name the exact paths: the plan JSON, one `openspec/changes//`, or the whole `openspec/changes/` only in Phase 4's fixes commit — see `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/bash-discipline.md`, the same hard rules bind here). - **Verify `git_state.py --expect-branch docs/propose-` before every commit multi-spec makes** (the plan-persist commit, each per-change commit, the review-fixes commit) — any non-zero exit halts and surfaces via `AskUserQuestion`, never a special-cased "probably fine.". Resolving an exit-2 in-progress op: `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/conflict-resolution.md`. Working on a worktree the session is not in: `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/bash-discipline.md`, "Working on a worktree you are not in". - **Verify every push actually reached the remote.** After each push anywhere in this skill, compare `git rev-parse HEAD` to `git ls-remote origin docs/propose-` in Claude's own context. Mismatch → halt via `AskUserQuestion`, never silently retry. This skill's whole value is "a pushed commit survives a dead disk" — an unverified push would silently reproduce the exact loss scenario it exists to prevent. Full rationale: `references/phases.md`. - **Per-change authoring dispatches to `Agent(subagent_type: "claude", model: "opus")`, one call per change, awaited sequentially — never parallel.** Sequential is what makes "commit before starting the next" possible; proposal authoring is judgment-critical (same tier as a Review verdict), not mechanical rubric work. Route every other dispatched agent per `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/model-routing.md` (the single source of truth for model/effort). Effort is not independently dialed on the `Agent(...)` mechanism — do not introduce `Workflow` as a workaround. - **This is a proposals-only skill.** Never invoke `Skill(cla:spec-to-pr)`, `Skill(cla:lite-pr)`, or `Skill(cla:multi-pr)` from inside `multi-spec` — point at them in the final report instead. Likewise, the batch review gate (Phase 4) always runs BEFORE the PR opens (Phase 5) — never the reverse. - **Ignore harness `TaskCreate` nudges for the per-change authoring loop.** It is sequential by design — the loop itself is already linear, single-file progress tracking; duplicating it in `TaskCreate` adds noise, same rule `/cla:spec-to-pr` applies to its own Implement loop. - **Run thin — keep raw material out of the parent context.** `multi-spec` IS an orchestrator (Phase 1's grouping judgment, Phase 3's per-change dispatch loop, Phase 4's batch review dispatch): delegate bulk raw-material handling to a sub-agent so only conclusions return, read file slices not whole files, `tail`/`head` large command output, batch independent tool calls into one message, prefer terse schema'd agent output over prose. Standing discipline across every phase below, not a per-trigger step. Full rules: `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/runtime-rules.md`. Cost rules for dispatch, tests and debris: `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/orchestration-economy.md`; the same rules reach other agents through the file in `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/economy-rules-file.md`. ## Input resolution Parse `$ARGUMENTS`: - **A path is given** → use it directly; halt and report if it doesn't exist. - **Empty, `cla.io/decisions/*.md` has ≥1 file** → default to the most-recently-modified file there. Announce `Mode: file (, most recent)`. - **Empty, `cla.io/decisions/` missing or empty** → infer the topic from the conversation, then run (or emulate) `Skill(cla:shape-decision)` on it and let it WRITE a real decisions file **with the user's explicit confirmation, per that skill's own "Persisting the decision" step.** Never proceed to Phase 1 from transient conversation state — the durability guarantee only holds if there is a committed decisions file to resume from. Announce `Mode: inferred-then-shaped ()`. Read the resolved decisions file in full before Phase 1. ## Phase 1 — Discover the change plan Derive an ordered change plan (each change's `name`, decisions covered, one-line scope, `depends_on`) from the file's own Sequencing section, or by grouping judgment when no such section exists — do not default to one change per decision. Full mechanics + the sub-Opus escalate-up rule: `references/phases.md`. Print the plan before authoring starts; hold it in working context only — **do not** write or commit it here (Phase 2 persists it, once the branch that its `batch_slug`/`branch` fields depend on actually exists). ## Phase 2 — Batch branch preflight, then persist the plan All N changes land in **one** PR on **one** dedicated branch, `docs/propose-` (not one branch per change). Full branch-slug derivation, preflight, and plan-persist-commit recipe: `references/phases.md`. This phase's first push **must** set the upstream (`-u`) — every push after it is bare. ## Phase 3 — Author each change, sequentially, committing as you go For each change in the plan, in dependency order: resume-check it, dispatch the Opus authoring agent, post-check the evidence regardless of what it reports, then **commit and push immediately, before starting the next change** — this one-at-a-time commit is the load-bearing anti-data-loss property this skill exists for (a crash mid-batch loses at most the change in flight, never the whole batch). Full resume-check, dispatch prompt, post-check, and commit/push recipe: `references/authoring-brief.md`. ## Phase 4 — Batch review gate Once every change is authored, validated, and committed, run **one** review over the **whole batch**. The checklist's size gate grades each change, and the batch's summed claim count (`e`) is graded too: if any change is large, or the batch makes 25 or more claims in total, dispatch the three agents once over the batch (same model routing as `review-change`'s checklist: Design Reviewer → opus, Task/Spec-and-Codebase → sonnet); otherwise, review inline. **A batch of exactly one change** goes straight to the single-change `review-change` checklist instead — there's no cross-change staleness class to catch. Apply every Critical/Important finding, re-validate, and commit the fixes as one follow-up commit **before Phase 5 opens the PR** — this ordering (review-then-PR) is load-bearing, never the reverse. Full dispatch mechanics, agent prompts, and the fix-application/commit recipe: `references/review-gate.md`. ## Phase 5 — Open the PR Opening a PR is not a deployment action (same boundary `/cla:spec-to-pr`'s Ship phase relies on) — only merge/push-to-``/publish are out of scope. Full PR-create recipe + the pre-create resume check: `references/phases.md`. ## Report Emit a terminal report: batch slug, decisions file, PR URL, the change list (name + one-line scope + dependency order), the review verdict per change, and — the load-bearing next step — **point explicitly at `/cla:multi-pr` for the produced change list** (or `/cla:spec-to-pr ` / `/cla:lite-pr ` per-change) as the follow-on that actually implements them. `multi-spec` stopping here is deliberate, not a truncated run. ## Resume behavior Every phase resumes from git-tracked repo state, never from a log or the plan file — the plan file is a convenience, not the durability backstop (same "derive from repo state" philosophy `/cla:spec-to-pr`'s `probe_state.py` already uses). Re-invoking `/cla:multi-spec` on the same decisions file picks up cleanly: - Phase 1/2 — `references/plan-schema.md`'s "Resume read path" (plan file exists → load it, skip re-deriving) for the plan file, and `references/phases.md` Phase 2 for the branch-preflight resume (branch already exists/checked-out → resume, not collision). - Phase 3 — `references/authoring-brief.md`'s resume check (tracked change dir → skip; untracked → resume authoring in place). - Phase 4/5 — the resume notes in `references/review-gate.md` (Step 7) and `references/phases.md` (Phase 5). ## What this skill deliberately does not do - Does not implement anything — no `Skill(cla:spec-to-pr)`, `Skill(cla:lite-pr)`, or `Skill(cla:multi-pr)` call. Proposing and implementing are different unattended-run shapes with different risk profiles; bundling them here would blur that boundary for no benefit. - Does not merge the PR it opens, or push to `` directly. - Does not build a bespoke resumable-state script — Phase 1's fallback grouping is genuinely open-ended reasoning over prose, so it stays inline/dispatched reasoning rather than a script. Nor does it keep a run ledger: its own was deleted unread, and resume re-derives state from `openspec/changes/` itself (see "Resume read path" in `references/plan-schema.md`). - Does not build a `multi-spec-retro` analyzer skill yet — same "wait for enough runs" posture noted above. ## References - `references/phases.md` — Phase 1/2/5 full mechanics, and the shared push post-check procedure used everywhere a push happens. - `references/plan-schema.md` — the change-plan JSON shape, where it lives, and exactly what resume reads from it vs. re-derives from git. - `references/authoring-brief.md` — Phase 3's full mechanics: the resume check, the per-change Opus dispatch prompt template, the post-check, and the commit/push recipe. - `references/review-gate.md` — Phase 4's full mechanics: the batch adaptation of `review-change`'s checklist and `dispatch.md` (agent prompts, model routing, verdict rubric) plus the fix-application/commit recipe. - `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/model-routing.md` — model/effort routing single source of truth (reused verbatim, not forked). - `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/bash-discipline.md` — the hard bash-shape rules binding every commit/stage call in this skill. - `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/runtime-rules.md` — the thin-orchestrator runtime disciplines this skill follows throughout. - `${CLAUDE_PLUGIN_ROOT}/skills/_shared/references/base-branch-resolution.md` — the `` resolution rule, shared verbatim with `multi-lite`/`multi-pr`. - `${CLAUDE_PLUGIN_ROOT}/skills/review-change/references/checklist.md` — the single-change review workflow `references/review-gate.md` adapts to batch scope. - `cla.io/overlays/multi-spec.md`, if present — rules specific to this skill in this repo.