skills/iterate-on-plan/SKILL.md
Iteratively refine an OpenSpec proposal by identifying and fixing completeness, clarity, feasibility, scope, consistency, testability, parallelizability, and assumptions issues
npx skillsauth add jankneumann/agentic-coding-tools iterate-on-planInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Iteratively refine an OpenSpec proposal after /plan-feature creates it. Each iteration reviews the proposal documents, identifies plan quality issues, implements fixes, and commits — repeating until only low-criticality findings remain or max iterations are reached.
$ARGUMENTS - OpenSpec change-id (required), optionally followed by:
--max <N> (default: 3)--threshold <level> (default: "medium"; values: "critical", "high", "medium", "low")--vendor-review — dispatch multi-vendor review after iterate loop converges; automatic in coordinated tier--prototype-context <change-id> — convergence mode (added by add-prototyping-stage / D1). When present, the skill loads prototype-findings.md, variant branch diffs, and validation reports as additional context, then emits convergence.* findings to refine design.md and tasks.md based on the picks captured by /prototype-feature. The change-id MUST match the iteration target. Fails fast if no findings file exists.openspec/changes/<change-id>/ with at least proposal.md, tasks.md, and one spec delta/plan-feature first if no proposal exists--prototype-context: /prototype-feature <change-id> must have been run first and produced openspec/changes/<change-id>/prototype-findings.mdWhen this skill delegates analysis work, treat the provider-neutral dispatch adapter
as the canonical cross-provider path. Claude Code, Codex, and
Gemini/Jules are first-class providers when configured; Claude-style Task(...)
or Agent(...) snippets are provider-specific examples, with inline execution
as the fallback.
Use OpenSpec-generated runtime assets first, then CLI fallback:
.claude/commands/opsx/*.md or .claude/skills/openspec-*/SKILL.md.codex/skills/openspec-*/SKILL.md.gemini/commands/opsx/*.toml or .gemini/skills/openspec-*/SKILL.mdopenspec CLI commandsUse docs/coordination-detection-template.md as the shared detection preamble.
CAN_* flag is truePlan iteration writes proposal, design, task, spec, findings, and session-log artifacts. In local CLI execution, those writes MUST run in a managed worktree and MUST NOT commit directly to local main.
After parsing CHANGE_ID, enter or verify the feature worktree before baseline
validation, findings generation, or edits:
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" setup "$CHANGE_ID")"
cd "$WORKTREE_PATH"
skills/.venv/bin/python skills/shared/checkout_policy.py require-mutation
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" resolve-branch "$CHANGE_ID" --parent)"
FEATURE_BRANCH="$BRANCH"
All commits from this skill land on $FEATURE_BRANCH for PR review.
At skill start, run the coordination detection preamble and set:
COORDINATOR_AVAILABLECOORDINATION_TRANSPORT (mcp|http|none)CAN_LOCK, CAN_QUEUE_WORK, CAN_HANDOFF, CAN_MEMORY, CAN_GUARDRAILSIf CAN_HANDOFF=true, read recent handoff context:
read_handoff"<skill-base-dir>/../coordination-bridge/scripts/coordination_bridge.py" try_handoff_read(...)If CAN_MEMORY=true, recall relevant plan-iteration memories:
recall"<skill-base-dir>/../coordination-bridge/scripts/coordination_bridge.py" try_recall(...)On recall/handoff failure, continue with standalone iteration and log informationally.
# Parse change-id from argument
CHANGE_ID=${ARGUMENTS%% *}
# Defaults
MAX_ITERATIONS=3
THRESHOLD="medium" # critical > high > medium > low
Parse optional flags from $ARGUMENTS:
--max <N> overrides MAX_ITERATIONS--threshold <level> overrides THRESHOLD--vendor-review sets VENDOR_REVIEW=true# Vendor review: explicit flag OR auto-enable in coordinated tier
VENDOR_REVIEW=false
if [[ "$ARGUMENTS" == *"--vendor-review"* ]] || [[ "$COORDINATOR_AVAILABLE" == "true" ]]; then
VENDOR_REVIEW=true
fi
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" setup "$CHANGE_ID")"
cd "$WORKTREE_PATH"
skills/.venv/bin/python skills/shared/checkout_policy.py require-mutation
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" resolve-branch "$CHANGE_ID" --parent)"
FEATURE_BRANCH="$BRANCH"
All subsequent steps run inside the worktree. Do not switch back to the shared checkout for file writes, validation artifacts, or commits.
# Verify proposal exists
openspec show $CHANGE_ID
# Verify core files exist
ls openspec/changes/$CHANGE_ID/proposal.md
ls openspec/changes/$CHANGE_ID/tasks.md
ls openspec/changes/$CHANGE_ID/specs/
If any core files are missing, abort and recommend running /plan-feature first.
# Strict validation as starting point
openspec validate $CHANGE_ID --strict
Record any validation failures. These become automatic critical-level findings in the first iteration.
Preferred path:
opsx:continue equivalent) to create or extend plan-findings.CLI fallback path:
openspec instructions plan-findings --change "$CHANGE_ID"
openspec status --change "$CHANGE_ID"
Ensure openspec/changes/<change-id>/plan-findings.md exists and append each iteration's findings there.
Skipped when --prototype-context is NOT in argv. The convergence path is always explicit per D1 / spec; iterate-on-plan does NOT auto-discover prototype-findings.md.
When --prototype-context <change-id> is present, load the artifacts the /prototype-feature skill produced:
from prototype_context import PrototypeContextMissing, load_prototype_context
try:
ctx = load_prototype_context(
change_dir=Path("openspec/changes") / PROTOTYPE_CONTEXT_CHANGE_ID
)
except PrototypeContextMissing as exc:
# Fail fast — the user explicitly asked for prototype-aware refinement,
# so silently downgrading to non-convergence iteration would be wrong.
raise SystemExit(f"--prototype-context: {exc}")
ctx.descriptors carries the parsed VariantDescriptors; ctx.synthesis_plan carries the per-aspect picks and pre-classified convergence.* recommended findings (computed via parallel-infrastructure.synthesize_variants).
In step 5 (Review and Analyze), seed the iteration's finding list with ctx.synthesis_plan["recommended_findings"] so they appear alongside the standard clarity/feasibility/etc findings the analyzer produces. The convergence findings drive design.md and tasks.md refinements that synthesize the picked aspects from the variant branches.
Also load the per-variant branch diffs as context (read-only — diffs are inputs, not edits):
for desc in $(ctx.descriptors); do
git diff main..."${desc.branch}" > /tmp/prototype-diff-"${desc.variant_id}".patch
done
Refinement commits land on $FEATURE_BRANCH — never on prototype branches. The prototype branches stay untouched until /cleanup-feature deletes them.
Spec scenarios covered: ConvergenceViaIterateOnPlan.convergence-mode-activated, convergence-without-context (the negative — not loading anything), missing-prototype-artifacts (fail fast).
ITERATION=1
Read all proposal documents to understand intent and current quality. For complex proposals, use parallel Task(Explore) agents to analyze different quality dimensions:
Sequential approach (default for simple proposals):
openspec/changes/<change-id>/proposal.mdopenspec/changes/<change-id>/tasks.mdopenspec/changes/<change-id>/design.md (if exists)openspec/changes/<change-id>/specs/*/spec.mdopenspec/specs/ for capabilities referenced in the proposal's Impact sectionParallel approach (for complex proposals with 5+ tasks or 3+ spec deltas):
Resolve the analyst archetype before dispatching:
from src.agents_config import load_archetypes_config, resolve_model
archetypes = load_archetypes_config()
analyst = archetypes.get("analyst")
analyst_model = resolve_model(analyst, {}) if analyst else "sonnet"
# Launch parallel analysis agents (single message, multiple Task calls)
Task(subagent_type="Explore", model=analyst_model, prompt="Analyze openspec/changes/$CHANGE_ID/ for COMPLETENESS issues: missing requirements, unaddressed edge cases, gaps in impact analysis, requirements without scenarios", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Analyze openspec/changes/$CHANGE_ID/ for CLARITY and CONSISTENCY issues: ambiguous wording, vague scenarios, contradictions between documents", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Analyze openspec/changes/$CHANGE_ID/tasks.md for FEASIBILITY and PARALLELIZABILITY: task size, dependencies, file overlap that would cause merge conflicts", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Analyze openspec/changes/$CHANGE_ID/ for TESTABILITY: scenarios that can't be verified, subjective language like 'properly' or 'correctly'", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Analyze openspec/changes/$CHANGE_ID/ for SECURITY and PERFORMANCE issues: missing auth/authorization for endpoints, secrets in config, unvalidated inputs, unbounded queries, missing pagination, sync where async needed", run_in_background=true)
Analysis Synthesis:
Produce a structured plan analysis with findings in this format:
| # | Type | Criticality | Description | Proposed Fix | |---|------|-------------|-------------|--------------| | 1 | completeness/clarity/feasibility/scope/consistency/testability/parallelizability/assumptions/security/performance | critical/high/medium/low | What the issue is | How to fix it |
Type categories:
/parallel-implement. Evaluates whether tasks have explicit dependency declarations, whether task scopes are isolated to separate modules/files (no shared-file overlap that would cause merge conflicts), whether tasks are granular enough for independent agent assignment, and whether sequencing maximizes concurrent execution widthCriticality levels:
openspec validate --strict failures, missing spec deltas for capabilities listed in Impact, requirements without any scenarios, proposal.md missing required sections (Why, What Changes, Impact), authentication bypass or missing auth on endpoints handling sensitive dataPlan smells to check for:
/parallel-implement and the coordinator's blocked_by field)Schema type mapping (for translating plan findings to review-findings.schema.json types at the dispatch/consensus boundary):
| Plan Dimension | Schema Type(s) | Notes |
|---|---|---|
| completeness | spec_gap | Missing requirements = spec gap. Use observability/resilience/compatibility when the missing content is specifically about those concerns. |
| clarity | spec_gap, style | Ambiguous wording = spec_gap; formatting = style |
| feasibility | architecture, performance | Infeasible designs are usually architectural or performance-bound |
| scope | spec_gap, correctness | Scope creep = spec_gap; scope leak = correctness |
| consistency | contract_mismatch, correctness | Cross-document contradictions |
| testability | spec_gap | Untestable requirement = incomplete spec |
| parallelizability | architecture | Task decomposition is architectural |
| assumptions | architecture, security, compatibility | Map to the schema type matching the assumption's subject |
| security | security | Direct mapping |
| performance | performance | Direct mapping |
Convergence finding taxonomy (only emitted in convergence mode — when --prototype-context was supplied; produced by parallel-infrastructure.synthesize_variants and seeded into the iteration's findings list at step 3.7):
| Finding Type | When Emitted | Resolution Hint |
|---|---|---|
| convergence.merge-<aspect>-<vA>-and-<vB> | Multiple variants picked for the same aspect (data_model / api / tests / layout) — humans wanted bits of both | Refine design.md to combine the picked elements; don't silently pick one |
| convergence.rewrite-<aspect> | Zero variants picked for an aspect — none of them got it right | Rewrite the aspect in design.md from other context (proposal, spec deltas) — don't carry forward any variant's take |
| convergence.prefer-variant-<aspect> | Single variant picked for an aspect (default source recorded in synthesis_plan) | The pick is unambiguous; refine to use that variant's approach |
| workflow.prototype-recommended | NOT a convergence finding — the inverse advisory emitted by step 6.5 when this iteration produces ≥3 high-criticality clarity+feasibility findings (D8) | Suggest running /prototype-feature BEFORE the next iteration; never auto-trigger |
Stop iterating if:
If stopping, skip to the After Loop section below.
Otherwise, continue to step 7.
After the standard finding analysis in step 5 produces this iteration's findings list, run the prototype-recommended emitter:
from prototype_recommended import maybe_emit_prototype_recommended
advisory = maybe_emit_prototype_recommended(findings, change_id=CHANGE_ID)
if advisory is not None:
findings.append(advisory)
The emitter returns a single workflow.prototype-recommended finding when this iteration produced ≥3 high-criticality findings in the clarity or feasibility dimensions (combined). The advisory:
criticality=low so it sorts to the bottom of the report (it's a hint, not a fix-required item)/prototype-feature <change-id> as the next command — but never invokes it automatically (D8 is opt-in)If the threshold is not met, the emitter returns None and nothing is appended.
Spec scenarios covered: PrototypeRecommendationSignal.threshold-met, threshold-not-met, advisory-only.
Fix all findings at or above the criticality threshold by modifying the proposal documents:
When to create design.md (if one does not exist):
For findings that are outside the scope of the current proposal:
# Validate proposal structure
openspec validate $CHANGE_ID --strict
Additionally verify:
Independent: N tasks | Sequential chains: M | Max parallel width: W/parallel-implementFix any failures before proceeding. If fixes introduce new issues, address them within this iteration.
Construct a PhaseRecord for the Plan Iteration <N> phase and call write_both(). The iteration number is auto-computed from prior Plan Iteration entries in the session-log so the agent does not have to count manually.
Capture from this iteration:
Persist via PhaseRecord.write_both():
This step MUST run BEFORE the git add in Step 9 so the session-log entry is included in that commit.
python3 - <<'EOF'
import sys
sys.path.insert(0, "skills/session-log/scripts")
from phase_record import PhaseRecord, Decision, Alternative, TradeOff
from extract_session_log import count_phase_iterations
n = count_phase_iterations(
"Plan Iteration", "openspec/changes/<change-id>/session-log.md"
) + 1
record = PhaseRecord(
change_id="<change-id>",
phase_name=f"Plan Iteration {n}",
agent_type="<agent-type>",
summary="<2-3 sentences: findings addressed, what changed>",
decisions=[
Decision(title="<title>", rationale="<rationale>"),
],
alternatives=[Alternative(alternative="<approach>", reason="<rejection reason>")],
trade_offs=[TradeOff(accepted="<X>", over="<Y>", reason="<reason>")],
open_questions=["<question>"],
completed_work=["<finding addressed>"],
)
result = record.write_both()
print(f"markdown_path={result.markdown_path}")
print(f"sanitized={result.sanitized}")
print(f"handoff_id={result.handoff_id or '(local fallback)'}")
print(f"handoff_local_path={result.handoff_local_path}")
for w in result.warnings:
print(f"WARN: {w}", file=sys.stderr)
EOF
write_both() runs three best-effort steps internally: append rendered markdown → sanitize in-place → coordinator handoff (or local fallback at openspec/changes/<change-id>/handoffs/plan-iteration-<n>-<N>.json). Each step logs warnings on failure but does not raise — the workflow continues even if the coordinator is unreachable. The session-log.md is inside openspec/changes/$CHANGE_ID/ so it will be picked up by the existing git add in Step 9.
# Review all changes
git status
git diff
# Stage proposal document changes only
git add openspec/changes/$CHANGE_ID/
# Commit with structured message
git commit -m "$(cat <<'EOF'
refine(plan): iteration <N> - <summary of key changes>
Iterate-on-plan: <change-id>, iteration <N>/<max>
Findings addressed:
- [<criticality>] <type>: <description>
- [<criticality>] <type>: <description>
Co-Authored-By: Claude <[email protected]>
EOF
)"
# Increment and loop
ITERATION=$((ITERATION + 1))
Loop back to Step 5.
Skip this step if VENDOR_REVIEW=false.
After the iterate loop converges (all findings below threshold) or max iterations are reached, dispatch a multi-vendor review for a final independent validation pass.
/parallel-review-planWrite a review prompt and dispatch to other vendor CLIs:
# Create review prompt for vendor dispatch
mkdir -p openspec/changes/$CHANGE_ID/reviews
cat > openspec/changes/$CHANGE_ID/reviews/review-prompt.md <<'PROMPT'
Review the OpenSpec plan artifacts in openspec/changes/$CHANGE_ID/.
Read proposal.md, tasks.md, design.md (if present), and all spec deltas.
Output ONLY valid JSON conforming to review-findings.schema.json.
Focus on: specification completeness, contract consistency, architecture alignment, security, and work package validity.
PROMPT
# Dispatch to other vendors (excludes current agent's vendor)
python3 "<skill-base-dir>/../parallel-infrastructure/scripts/review_dispatcher.py" \
--review-type plan \
--mode review \
--prompt-file "openspec/changes/$CHANGE_ID/reviews/review-prompt.md" \
--cwd "$(pwd)" \
--output-dir "openspec/changes/$CHANGE_ID/reviews" \
--exclude-vendor claude_code \
--timeout 600
Also produce your own findings as the primary reviewer (Steps 1-5 of /parallel-review-plan): read plan artifacts, evaluate against the review checklist, and write findings to openspec/changes/$CHANGE_ID/review-findings-plan.json.
python3 "<skill-base-dir>/../parallel-infrastructure/scripts/consensus_synthesizer.py" \
--review-type plan \
--target "$CHANGE_ID" \
--findings "openspec/changes/$CHANGE_ID/review-findings-plan.json" \
"openspec/changes/$CHANGE_ID/reviews/findings-"*"-plan.json" \
--output "openspec/changes/$CHANGE_ID/reviews/consensus-plan.json"
Present consensus summary:
If no other vendors are available (CLIs not installed), skip dispatch and proceed with single-vendor findings only.
The remediation threshold is the user's --threshold setting if provided, otherwise medium.
If the consensus or vendor review surfaces new findings at or above the remediation threshold:
openspec/changes/$CHANGE_ID/plan-findings.mdrefine(plan): vendor-review remediation - <summary>If all vendor review findings are below the remediation threshold, proceed to the summary.
Present a summary of all iterations:
If `CAN_MEMORY=true`, remember iteration outcomes (for example findings counts, key fixes, and residual risks):
- MCP path: `remember`
- HTTP path: `"<skill-base-dir>/../coordination-bridge/scripts/coordination_bridge.py"` `try_remember(...)`
If `CAN_HANDOFF=true`, write a completion handoff containing:
- Iterations performed and findings addressed
- Remaining below-threshold findings (if any)
- Validation status and proposal readiness
- Recommended next command
## Plan Iteration Summary
### Iteration 1
- Findings: <count> (<count by criticality>)
- Fixed: <list>
### Iteration 2
- Findings: <count> (<count by criticality>)
- Fixed: <list>
...
### Final State
- Total iterations: <N>
- Total findings addressed: <count>
- Remaining findings (below threshold): <list or "none">
- Termination reason: <threshold met | max iterations reached>
- Validation status: <openspec validate --strict result>
### Vendor Review (if dispatched)
- Vendors dispatched: <list or "skipped">
- Consensus findings: <confirmed count> confirmed, <unconfirmed count> unconfirmed, <disagreement count> disagreements
- Remediation cycle: <ran / not needed>
- New findings addressed in remediation: <count or "N/A">
### Parallelizability Assessment
- Independent tasks: <N>
- Sequential chains: <M>
- Max parallel width: <W>
- File overlap conflicts: <list or "none">
### Proposal Readiness
- [ ] openspec validate --strict passes
- [ ] All requirements have success + failure scenarios
- [ ] All tasks are traceable to requirements
- [ ] All tasks are single-commit sized
- [ ] Impact section matches spec deltas
- [ ] design.md present if complexity warrants it
- [ ] Task dependencies are explicit (ready for /parallel-implement)
- [ ] No file-overlap conflicts between independent tasks
openspec/changes/<change-id>/ documents--vendor-review or coordinated tier): openspec/changes/<change-id>/reviews/consensus-plan.jsonPresent the refined proposal for approval. After approval:
/implement-feature <change-id>
development
Open the artifacts relevant to a review (OpenSpec proposal, branch changes, or explicit paths) in VS Code, in a curated read-order, in the right worktree.
tools
Render and seed coordinator-owned task status block in OpenSpec tasks.md
testing
User-invocable skill that omits the tail block
tools
Missing several required keys