| name | openspec-story-plan-converge |
| description | Run fresh plan-review and plan-resume sessions against one OpenSpec change until its Plan lane is approved, blocked, or the loop reaches a hard stop. Use when a story needs repeated independent plan feedback and feedback absorption before implementation or rework continues. |
| disable-model-invocation | true |
| argument-hint | <initiative-slug> <story-slug> [MAX_CYCLES=5] |
| allowed-tools | Read Edit Grep Glob Task Bash(git status:*) Bash(git worktree list:*) |
OpenSpec Story Plan Converge
Coordinate the planning-side iteration loop for exactly one OpenSpec change workspace, independent of implementation status. This command is an orchestrator only: it starts fresh subagent sessions for /openspec-story-plan-review and /openspec-story-plan-resume, preserves their ownership boundaries, keeps parent-session notebook context with sourced research plus neutral operational notes, and stops when the Plan lane is approved, blocked, no longer eligible, or out of cycle budget.
Argument: $ARGUMENTS — <initiative_slug> <story_slug> [MAX_CYCLES=5]. The initiative slug and story slug are required. MAX_CYCLES is optional and defaults to 5; it counts full review/resume cycles, not individual subagents.
Workflow
- Resolve the requested initiative and change workspace through
openspec/initiatives/<slug>/initiative.md and openspec/changes/<story-slug>/story.md.
- Confirm the story is non-archived and has a planning contract that can be reviewed or resumed.
- Choose the first planning pass from the story shape: resume first for incomplete specs, otherwise review first.
- Run up to
MAX_CYCLES fresh-agent planning cycles.
- Pass compact notebook references for sourced research, plus neutral operational notes, into later fresh agents.
- Stop on approval, blocker, no-progress, invalid state, or cycle budget exhaustion.
- Print the convergence trace, notebook context summary, and optional operator follow-ups without writing coordination files directly.
Resolution Model
<workspace_root> = <cwd> and remains the worktree-discovery base.
<openspec_root> starts as <workspace_root> and is the transient artifact anchor selected during resolution; never persist an OpenSpec root: field.
<initiative_dir> = <openspec_root>/openspec/initiatives/<initiative>.
<initiative_file> = <initiative_dir>/initiative.md.
<change_dir> = <openspec_root>/openspec/changes/<story-slug>.
<story_file> = <change_dir>/story.md.
<progress_file> = <change_dir>/progress.md.
- The
Plan: header field in <story_file> is the authoritative planning lane.
- The
Status: header field in <story_file> is the authoritative implementation lane.
- The
## Plan Review Log section in <story_file> is the authoritative review history.
- New workspaces created by
/openspec-story-plan must seed the convergence scaffold: Plan: 🟡 PLAN DRAFT, Status: ⚪ TODO, exactly one top-level Initiative: <initiative-slug> binding, and an empty ## Plan Review Log section.
There is no MASTER.md, no tracker table, no step/number row, and no implementation Status column in this flow. The change workspace is self-contained under openspec/changes/<story-slug>/.
Phase 1 — Parse and Resolve
- Parse
$ARGUMENTS:
<initiative>: required first positional token.
<story-slug>: required second positional token.
MAX_CYCLES=<n>: optional positive integer; default 5.
- Reject unknown flags. This command does not accept
WORKTREE= because planning may read source code for evidence but never writes source code.
- Validate
<initiative> and <story-slug> before resolving paths. Each must match ^[a-z0-9]+(?:-[a-z0-9]+)*$; if either fails, abort with: invalid slug; use lowercase hyphenated slug characters only.
- Set
<workspace_root> = <cwd> and <openspec_root> = <workspace_root>, then run git worktree list --porcelain from <workspace_root>. This planning command accepts no WORKTREE= selector, so resolve the remaining tiers in order: inspect registered worktrees other than <workspace_root> on refs/heads/<initiative>/<story-slug> and require both openspec/initiatives/<initiative>/initiative.md and openspec/changes/<story-slug>/story.md. Exactly one qualifying branch worktree outranks launch even when launch has stale matching artifacts; multiple qualifying branch worktrees halt for operator selection. Only when no branch worktree qualifies, fall back to <workspace_root> and require both artifacts there. Ignore unrelated/non-branch worktree copies; never select an arbitrary root merely because it contains the same slug. Recompute every artifact path from <openspec_root>.
- Resolve
<initiative_file> = <openspec_root>/openspec/initiatives/<initiative>/initiative.md.
- If missing, abort with the exact path and
/openspec-initiative-plan recovery.
- Resolve
<change_dir> = <openspec_root>/openspec/changes/<story-slug>/.
- If absent, check
<openspec_root>/openspec/changes/archive/<story-slug>/; if archived, halt and require moving it back first.
- If absent from both after worktree discovery, rule out relocation, then abort with the singular creation route
/openspec-story-plan INITIATIVE=<initiative>. The converger never sends a nonexistent workspace to plan-resume.
- Resolve
<story_file> = <change_dir>/story.md. If it is missing or unreadable, halt with the exact path and require the operator to restore it from version control or backup; do not route a colliding incomplete workspace to story creation.
- Validate the durable initiative binding before lifecycle or scaffold routing:
- Inventory the complete top-level header region before the first
## heading for every unindented Initiative or Initiative-like field line. Exactly one present line is valid only when its whole line matches ^Initiative: ([a-z0-9]+(?:-[a-z0-9]+)*)$. Duplicate canonical headers, an empty value, whitespace before the colon (for example Initiative : foo), a non-canonical value, or any other malformed Initiative-like line halts without editing or launching a child and reports every offending line. Never reinterpret malformed present input as zero-header legacy.
- The one valid header must equal
<initiative>. On an Initiative mismatch, halt and report both values; do not proceed.
- This command's two required positional slugs are an operator-explicit initiative+story pair. Only zero Initiative or Initiative-like lines is legacy. For that case, scan active
<openspec_root>/openspec/initiatives/*/initiative.md files for exact <story-slug> associations in ## Story Candidates. With no associations, the explicit pair may target the legacy story because the selected initiative file exists. With candidate evidence, continue only when exactly one association exists and it equals <initiative>; a different or multiple association conflicts and halts. Print a compatibility warning and never backfill the header. An auto-defaulted or menu-selected initiative alone would not be an explicit pair and could not authorize this zero-reference fallback.
- Check
<change_dir>/blocked.md before reading lifecycle fields or scaffold routing. If it exists, abort with the singular operator action to resolve the blocker and remove the file; do not offer wrapper/direct choices.
- Read
<story_file>.
- Read
Status: first. If it is ✅ DONE, defer all Plan/scaffold routing to the DONE gate in Phase 2; never recommend plan-resume/review for a completed story.
- If it is
🟣 IN REVIEW, defer every Plan/scaffold decision to the Phase 2 hard stop. Neither planning child accepts IN REVIEW, so do not route to or launch plan-review/plan-resume from this state.
- For every other non-DONE status, confirm the
Plan: header field, Status: header field, and ## Plan Review Log section are present. A legacy story may remain without Initiative: only under step 8's bounded fallback. Plan: and ## Plan Review Log may be default-valued or empty.
- If a repairable scaffold anchor is missing, abort with the singular recovery route:
story.md is missing the /openspec-story-plan convergence scaffold — run /openspec-story-plan-resume <initiative> <story-slug> to repair missing Plan:, Status:, or ## Plan Review Log anchors. Re-check authoritative state after repair; do not pre-offer a wrapper choice for missing, incomplete, malformed, or non-reviewable scaffold.
Phase 2 — Eligibility Gate
Before starting the loop, abort with a clear next action if any condition is true, in this order:
- A
blocked.md file exists at <change_dir>/blocked.md: enforce the Phase 1 hard singular operator-action gate; never offer wrapper/direct choices first.
- The implementation
Status: header is 🟣 IN REVIEW: always stop before the planning loop. /openspec-story-plan-review and /openspec-story-plan-resume both reject this implementation state, so this converger must never launch or recommend either as the immediate owner. For a named implementation/proof deficiency, offer the implementation choice—Converge wrapper /openspec-story-converge <initiative> <story-slug> or Non-looped pass /openspec-story-resume <initiative> <story-slug>—and say fresh review follows repair. For a planning/contract deficiency or malformed scaffold, give one explicit operator repair action to reconcile the aborted-review IN REVIEW state and named artifact defect before rerunning planning; do not route to a planning child that will reject it. For unresolved external evidence, give one concrete operator action. Only when no repair condition exists and review has not run against the current ready evidence use the singular fresh, oblivious /openspec-story-review <initiative> <story-slug> handoff.
- The implementation
Status: header field in story.md is ✅ DONE: completed stories are not contract-reviewed or contract-reworked in place. Inventory all <progress_file> → ## Implementation Review Receipt headings. When any receipt is present, require exactly one section/body with every canonical required field exactly once, Decision: APPROVE, Approval gate: PASS, and a Status transition ending in ✅ DONE; duplicate, truncated, malformed, contradictory, stale, or non-approving content routes only to Open a completely fresh, oblivious session and run /openspec-story-review <initiative> <story-slug>. Receipt absence is legacy compatibility only for a true unbound pre-v3 story with zero Initiative or Initiative-like header lines and zero receipt sections: warn and do not synthesize one. A bound modern DONE story without a receipt routes to the same fresh oblivious review, never legacy compatibility. After that receipt gate, if Plan: is anything other than unambiguous 🟢 PLAN APPROVED, stop with only Operator action: investigate and reconcile the contradictory durable Status: ✅ DONE and Plan: <value> state before delivery or archive. Do not recommend planning commands that reject DONE and do not invent a lifecycle owner. If the receipt qualifies but bounded task/evidence state still contradicts DONE, use the same executable fresh-review route; never resume. Only a consistent DONE with approved Plan and a qualifying receipt or the exact zero-Initiative/zero-receipt pre-v3 exception routes new feedback through /openspec-feedback as a candidate, initiative-level decision, defer/reject entry, or explicit lifecycle reopen decision.
- The
Plan: header field is 🟢 PLAN APPROVED and there are no unresolved ## Plan Review Log findings and the ## Plan Review Log contains at least one entry with Verdict: approve (or a legacy equivalent) from an independent /openspec-story-plan-review pass: stop successfully; planning is already complete. Route by authoritative implementation Status: using the implementation choice below for TODO/IN PROGRESS, the singular fresh oblivious review route for IN REVIEW, and singular state-owner routes otherwise.
- The
Plan: header field is 🟢 PLAN APPROVED but ## Plan Review Log is empty or lacks an approve entry: treat as orphaned approval. Set Plan: to 🟠 PLAN CHANGES REQUESTED and route through the normal review cycle.
- The
Plan: header field is ⛔ PLAN BLOCKED: stop with blocked status. The operator must resolve the blocker before convergence can proceed.
- The existing story is missing the
/openspec-story-plan shape expected by /openspec-story-plan-review (required spec sections, Plan/Status/log anchors, proposal.md, design.md, or tasks.md): stop with /openspec-story-plan-resume <initiative> <story-slug> for repairable drift. A genuinely absent workspace already routed to /openspec-story-plan INITIATIVE=<initiative> in Phase 1.
- The existing story is so malformed that
/openspec-story-plan-resume cannot identify spec sections to continue: use the singular operator action to restore planning artifacts from version control/backup or remove the invalid workspace before creating it again.
The planning-lane authority is the Plan: header field in story.md. No other source is consulted for the plan state.
Phase 3 — Fresh-Agent Loop
Run at most MAX_CYCLES cycles. A planning cycle is one opportunity to get the plan approved; depending on current story shape it may include one fresh /openspec-story-plan-resume pass and one fresh /openspec-story-plan-review pass.
For each cycle:
-
Re-read <initiative_file> and <story_file> and recheck <change_dir>/blocked.md before choosing the next pass. If the blocker file exists, stop with the singular operator action before launching a child.
-
Before any fresh subagent launch in this phase, build the task prompt in this order: notebook context when present, operational context when present, then the exact slash command as the final line.
-
If a newer unaddressed plan-review finding exists (a request_changes or not_reviewable entry in ## Plan Review Log without a matching addressed entry), prepare and launch a fresh subagent whose task prompt ends with:
/openspec-story-plan-resume <initiative> <story-slug>
The resume child routes to Mode A (unresolved-review-entry absorption) automatically. If this resume pass asks an operator question, pause the convergence run, ask the operator, then resume the same subagent for that pass only.
-
Else if required spec sections are missing or structurally incomplete and there is no newer unaddressed plan-review finding, prepare and launch a fresh resume subagent the same way (Mode B — missing-section completion). Immediately after any resume child launched by step 3 or 4 finishes, recheck <change_dir>/blocked.md before accepting its result, launching review, or continuing. If the file exists, stop with the singular operator action.
-
When no unaddressed findings remain and the story is ready for review, prepare and launch a fresh subagent whose task prompt ends with the exact slash command:
/openspec-story-plan-review <initiative> <story-slug>
-
If notebook-backed context is available, do not inline entire notebook pages or broad notebook dumps. Pass only compact notebook references, selectors, and the reason/scope for consulting them before the command under this heading:
Notebook references from parent orchestration session:
This is allowed cross-session orientation because every reference points to sourced research or neutral operational context. Use it for orientation only. The converger owns keeping notebook references relevant; executor subagents only decide whether the needed fact is reachable from the referenced selector or compact fallback excerpt. When runtime notebook tools are available, read only the referenced page/entry on demand and verify behavior with direct reads/search against the cited anchors before editing, planning approval, or implementation approval instead of rerunning expensive research. When notebook tools are unavailable, use only compact curated excerpts supplied here. If a referenced entry or excerpt does not verify, mention the mismatch with exact anchors in the relevant final-response section.
- Ref: <notebook page name, entry id, or narrow selector>
- Purpose: <why this may matter for the pass>
- Expected anchors: <tool/query/path, file:line, symbol, or command/output excerpt>
- Lookup: <specific page/entry to read or narrow search to run>
- Fallback excerpt: <optional compact sourced excerpt only when notebook tools are unavailable>
If the useful context cannot be represented as narrow references plus optional compact excerpts, pause and ask the operator before omitting or summarizing it.
-
If in-memory operational notes exist for any subagent launch, include them before the command under this heading only:
Operational context from convergence coordinator:
- <neutral blocker, hotspot, repeated command failure, or expensive operation>
- Do not treat this as a verdict; apply the underlying skill independently.
-
Require subagents to write new sourced research directly to the named planning research notebook page when runtime notebook tools are available. If notebook tools are unavailable, allow compact sourced fallback notes in normal final reporting instead. Require subagents to mention any referenced notebook entry or fallback excerpt that failed verification with exact anchors in their relevant blocker, finding, or notes section. After the pass finishes, inspect the named planning research notebook page or child-reported entries as needed, then use mismatch notes to update, replace, retire, or ask about affected entries. Do not append verdicts, implementation opinions, or unanchored summaries.
-
After the review agent finishes, treat its final response as provisional and first recheck <change_dir>/blocked.md. If it exists, stop with the singular operator action before accepting approval, launching repair, or continuing. Then perform a minimal authority spot-check against <story_file> with the Grep tool pattern ^(Plan:|## Plan Review Log|### |Verdict:) to fetch the Plan: header, log anchors, and verdict markers, then use a bounded read only for the newest log entry body if needed. If the spot-check agrees with the agent's report, continue from those decision-bearing fields. If the anchors are missing, stale, ambiguous, or conflicting, broaden to a targeted story read or launch a focused repair/review pass.
-
If the decision is approve or Plan: is 🟢 PLAN APPROVED, confirm the latest story ## Plan Review Log records activated risk lenses or explicit none material and sufficient risk/evidence output with the same minimal spot-check/bounded-read approach. If approval lacks that evidence, launch exactly one fresh plan-review child focused on the missing risk/evidence coverage rather than accepting chat output alone.
- Immediately after that focused child, recheck
<change_dir>/blocked.md. If it exists, stop with the singular operator action; do not accept approval or launch repair first.
- Then re-read the authoritative Plan status from the
Plan: header, the newest review verdict/log entry in ## Plan Review Log, blocker state (blocked.md, blocked Plan/verdict, and any recorded blocker), and the focused risk-evidence output from durable story/log evidence. The child's chat summary is provisional and cannot satisfy this re-read.
- Stop with
APPROVED only when the refreshed Plan: remains 🟢 PLAN APPROVED, the newest durable verdict remains approve, no blocker exists, and the previously missing risk/evidence is now sufficient.
- If the refreshed verdict is
request_changes or not_reviewable, or Plan: is 🟠 PLAN CHANGES REQUESTED, route through step 12 to a fresh plan-resume child when the scaffold remains complete/reviewable; if the finding made the scaffold non-reviewable, stop with the singular /openspec-story-plan-resume <initiative> <story-slug> owner route.
- If the refreshed verdict or
Plan: is blocked, stop with the singular blocker-resolution route.
- If the focused child leaves the same risk/evidence deficiency unchanged, do not report approval and do not launch another focused review. Stop for no progress with the singular
/openspec-story-plan-resume <initiative> <story-slug> route to repair the named deficiency; fresh plan review occurs only after repair.
- If the refreshed fields are missing, ambiguous, or conflict, stop with a concrete singular operator repair action rather than guessing.
Only after the approval conditions above pass, stop successfully. Do not claim or resume the story. Recommend
/openspec-story-claim <initiative> <story-slug> or /openspec-story-resume <initiative> <story-slug> to begin implementation.
-
If the decision is blocked or Plan: is ⛔ PLAN BLOCKED, stop with blocked planning status.
-
If the decision is request_changes or not_reviewable, prepare and launch a different fresh subagent whose task prompt ends with:
/openspec-story-plan-resume <initiative> <story-slug>
-
If the resume agent asks an operator question, pause the convergence run, ask the operator, then resume the same subagent for that resume pass only. The next review still starts in a new fresh subagent.
-
After the resume agent finishes, trust its report only provisionally and first recheck <change_dir>/blocked.md. If it exists, stop with the singular operator action before launching review or continuing. Then run a minimal Plan: header spot-check with the Grep tool pattern ^Plan: against <story_file>. Broaden to a targeted story read only if the header is missing, unexpected, ambiguous, or conflicts with the agent's report.
-
Run the no-progress gate before starting the next cycle.
Phase 4 — Operational Notes and Stops
Maintain a convergence notebook containing neutral operational notes and sourced research entries. Do not write notebook content or the transient <openspec_root> value to story.md, initiative.md, progress.md, or any coordination file as a duplicate source of lifecycle, proof, review, or root authority.
Record neutral operational facts only:
- command failures and their exact command names;
- missing environment or worktree prerequisites;
- story sections or proof rows that repeatedly block progress;
- files or concepts that multiple agents identify as hotspots;
- time-consuming operations that later fresh agents should avoid repeating blindly.
Do not record persuasive verdict framing. Never tell a later reviewer that a previous reviewer was wrong, that approval is expected, or that a finding should be ignored.
Sourced notebook references and compact fallback excerpts are the allowed cross-subagent research orientation. Each referenced entry or excerpt must be sourced by an exact anchor: file path plus line range or symbol, command plus relevant output excerpt, or tool name plus query/action/resource/path/URL and relevant output excerpt for any sourced tool. Notebook entries are an orientation aid, not authority. The converger owns keeping references relevant for later passes; executor subagents only decide whether the needed fact is reachable from the referenced selector or compact excerpt. If present, the executor reads only the relevant notebook page/entry on demand when available and verifies behavior with direct reads/search against the cited anchors before editing or approving instead of rerunning expensive research. If absent, the executor follows the underlying skill's normal research rules. If a referenced entry or fallback excerpt does not verify, the executor mentions the mismatch with exact anchors in the relevant final-response section; the converger decides how to update, replace, retire, or ask about that reference. Do not pass broad notebook dumps; if needed context cannot be represented by narrow selectors and compact excerpts, ask the operator before omitting or summarizing it.
Stop early for conservative no-progress when all are true:
- the latest review requested changes or said not reviewable;
- the subsequent resume pass did not add a newer addressed-feedback entry or materially edit the targeted spec sections;
- the same blocker or finding would be handed to another review unchanged.
Do not use repeated cycles to paper over an under-specified or over-large story; newly discovered risk-lens or proof-contract gaps must be edited into the story contract or explicitly excluded.
Other hard stops:
MAX_CYCLES reached;
- latest decision is
blocked;
blocked.md appears in <change_dir> during the convergence run;
- subagent cannot resolve the story or command;
- the operator declines an interactive decision required by resume.
Phase 5 — Final Response
Return only the compact report below. Do not include internal deliberation, analysis prose, "Thinking:" blocks, private rationale, or comments about what you are considering before or after the report. Include every section in the template; use None. or unavailable rather than omitting a section.
**Convergence Result**: APPROVED | BLOCKED | STOPPED | MAX_CYCLES
**Initiative**: <initiative-slug>
**Story**: <story-slug>
**Cycles Used**: <n>/<MAX_CYCLES>
**Final Plan Lane**: <plan>
**Change Workspace**: openspec/changes/<story-slug>/
## Trace
- Cycle 1: plan-review -> <decision>; plan-resume -> <completed/skipped>
- Cycle 2: ...
## Notebook Context
- References passed: <n>
- Hotspots: <paths/symbols surfaced by sourced research, or none>
- Research notebook updates: <entries added/updated/retired this run, or none>
- Referenced entries verified: <summary or none>
- Stale reference handling: <referenced entries/excerpts not verified, needed facts absent from referenced notebook selectors, or none>
- Persistence: <notebook page references, compact excerpt fallback, runtime-specific notebook pages, or none>; no coordination-file cache written
## Operational Notes
- <neutral operational note>
- None.
## Optional Operator Follow-Ups
- <proposed future improvement surfaced by repeated friction, including recurring risk/miss category worth automating or adding to future planning>
- None.
Suggested next action: <scalar route; leave empty only for a dual route>
- Converge wrapper: <command; dual routes only>
- Non-looped pass: <state-correct review/resume or claim/resume command; dual routes only>
Choose one; do not run both.
Select that block from authoritative final state. When APPROVED and implementation is TODO or IN PROGRESS, offer Converge wrapper: /openspec-story-converge <initiative> <story-slug> and Non-looped pass: TODO -> /openspec-story-claim <initiative> <story-slug>, IN PROGRESS -> /openspec-story-resume <initiative> <story-slug>. Say to choose one and not run both because the wrapper delegates direct claim/resume passes.
For a scalar route, put its value on the label line and omit the three dual-route lines. For a dual route, leave the label empty and render those three lines immediately after it.
When MAX_CYCLES leaves a valid, complete/reviewable planning lane active without an unresolved operator question/resolution failure, use the planning Converge wrapper plus Non-looped plan-review when no unresolved finding remains, or Non-looped plan-resume when findings remain and converge can orchestrate that lane. Plan: 🟠 PLAN CHANGES REQUESTED with unresolved findings uses plan-resume when the scaffold stays complete/reviewable; when every finding is fully blended/addressed and the scaffold is reviewable, it uses fresh plan-review. Missing anchors or any incomplete/non-reviewable scaffold always use singular /openspec-story-plan-resume <initiative> <story-slug> (or story-plan when the workspace is absent), never the wrapper.
Generic STOPPED due to an operator question, declined decision, unresolved command/story resolution, unchanged focused risk/evidence deficiency, or other resolution failure is singular: state the exact operator or repair-owner action needed and do not also suggest rerunning the wrapper/direct pass. When implementation Status is IN REVIEW, never route to planning review/resume: implementation/proof rework may use the implementation converge/resume choice, while contract/scaffold or external-evidence defects require one explicit repair action; fresh oblivious /openspec-story-review is valid only when no repair remains and review has not run against current ready evidence. DONE with a non-approved Plan uses only the operator action to investigate/reconcile the contradictory durable state and names no lifecycle owner. For BLOCKED, incomplete/non-reviewable scaffold outside IN REVIEW, malformed/ambiguous, DONE with consistent evidence, DONE/evidence contradiction, PR, archive, wait, or terminal states, give only the state-owning singular route.
Do not run /memorize automatically. If the nice-to-haves are valuable, the operator can decide whether to promote them later.