| name | plan-feature |
| description | Create OpenSpec proposal with tiered execution (coordinated / local-parallel / sequential) |
| category | Git Workflow |
| tags | ["openspec","planning","proposal","contracts","work-packages"] |
| triggers | ["plan feature","plan a feature","design feature","propose feature","start planning","linear plan feature","parallel plan feature","parallel plan","plan parallel feature"] |
Plan Feature
Create an OpenSpec proposal for a new feature. Automatically selects execution tier based on coordinator availability and feature complexity. Uses interactive discovery questions and two-gate approval to ensure the plan reflects user intent before detailed artifacts are generated.
Arguments
$ARGUMENTS - Feature description (e.g., "add user authentication")
Optional flags:
--explore -- Deep-dive mode: more discovery questions (5-8 vs 2-5), more approaches (3-5 vs 2-3), web search for prior art when available
--interview -- Confidence-gated discovery: replaces the fixed question budget with an adaptive loop that keeps asking (in batches of 2-4) until self-assessed confidence reaches ≥ 0.95 on a five-dimension rubric or the user asks to move on. Prioritizes latent intent ("what you actually want") over surface request ("what you think you should want"). Auto-enabled when $ARGUMENTS is short/vague (≤ 3 words or no verb)
Provider-Neutral Dispatch
When this skill delegates context gathering or review 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; any
Claude harness Agent(...) usage is a provider-specific adapter internal or
example, with inline execution as the fallback.
OpenSpec Execution Preference
Use OpenSpec-generated runtime assets first, then CLI fallback:
- Claude:
.claude/commands/opsx/*.md or .claude/skills/openspec-*/SKILL.md
- Codex:
.codex/skills/openspec-*/SKILL.md
- Gemini:
.gemini/commands/opsx/*.toml or .gemini/skills/openspec-*/SKILL.md
- Fallback: direct
openspec CLI commands
Interactive Planning
This skill uses AskUserQuestion to gather user input at discovery and approval gates. If AskUserQuestion is unavailable in the current runtime, present questions as a numbered list in regular output and instruct the user to respond inline.
Planning gates:
- Step 3: Discovery questions -- gather scope, constraints, and preferences before any artifacts are created
- Step 5: Gate 1 (Direction) -- user selects an approach before detailed specs/tasks are generated
- Step 12: Gate 2 (Plan) -- user approves the complete plan before implementation begins
Steps
0. Detect Coordinator, Select Tier, Parse Flags [all tiers]
Run the coordinator detection script:
python3 "<skill-base-dir>/../coordination-bridge/scripts/check_coordinator.py" --json
Parse JSON output and set capability flags. Then select tier:
If COORDINATOR_AVAILABLE and CAN_DISCOVER and CAN_QUEUE_WORK and CAN_LOCK:
TIER = "coordinated"
Else if user invoked via "parallel plan" trigger OR feature touches 2+ architectural boundaries:
TIER = "local-parallel"
Else:
TIER = "sequential"
Parse optional flags from $ARGUMENTS:
EXPLORE_MODE=false
INTERVIEW_MODE=false
if [[ "$ARGUMENTS" == *"--explore"* ]]; then
EXPLORE_MODE=true
fi
if [[ "$ARGUMENTS" == *"--interview"* ]]; then
INTERVIEW_MODE=true
fi
# Auto-enable interview mode when the request is short/vague.
# Heuristic: ≤ 3 words remaining after stripping flags, OR no action verb.
# A short request without a verb ("role based access matrix") is just as
# vague as a single-word one — the user named a noun phrase but didn't say
# what to DO with it.
STRIPPED=$(echo "$ARGUMENTS" | sed -E 's/--(explore|interview)//g' | xargs)
WORD_COUNT=$(echo "$STRIPPED" | wc -w | tr -d ' ')
if echo "$STRIPPED" | grep -iqE '\b(add|create|build|make|implement|refactor|fix|update|remove|delete|change|migrate|extract|replace|rename|move|convert|improve|enhance|support|enable|disable|allow|prevent|expose|hide|introduce|ship|merge|split|consolidate|wire|validate|verify|check|test|document|explain|clarify|restructure|rewrite|cleanup|clean|audit|review|rebuild|resolve|investigate|explore|analyze|optimize|tune|polish|harden|stabilize|deprecate)\b'; then
HAS_VERB=true
else
HAS_VERB=false
fi
if [[ "$WORD_COUNT" -le 3 ]] || [[ "$HAS_VERB" == "false" ]]; then
INTERVIEW_MODE=true
fi
# Discovery question bounds
if [[ "$EXPLORE_MODE" == "true" ]]; then
MIN_QUESTIONS=5; MAX_QUESTIONS=8
MIN_APPROACHES=3; MAX_APPROACHES=5
else
MIN_QUESTIONS=2; MAX_QUESTIONS=5
MIN_APPROACHES=2; MAX_APPROACHES=3
fi
# Interview mode treats MAX_QUESTIONS as a soft ceiling, not a hard cap.
# Confidence threshold for exiting the discovery loop:
CONFIDENCE_THRESHOLD=0.95
Emit tier notification:
Tier: <tier> -- <rationale>
Mode: <standard | explore>[, interview]
If CAN_HANDOFF=true, read recent handoff context. If CAN_MEMORY=true, recall relevant memories.
1. Setup Planning Worktree [all tiers]
The shared checkout is read-only -- never commit or modify files there. All planning work happens in a feature-level worktree.
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" setup "<change-id>")"
cd "$WORKTREE_PATH"
FEATURE_BRANCH="$WORKTREE_BRANCH"
git rev-parse --show-toplevel
git branch --show-current
If the worktree already exists (e.g., from a previous session), reuse it. All subsequent steps happen inside the worktree.
Operator branch override: When OPENSPEC_BRANCH_OVERRIDE is set (e.g. by the Claude cloud harness with claude/fix-branch-mismatch-9P9o1), worktree.py uses that branch instead of openspec/<change-id>. Downstream push/PR steps must reference $FEATURE_BRANCH rather than hardcoding the openspec prefix. If implement-feature later dispatches parallel work-package agents, they will each branch off $FEATURE_BRANCH with --<agent-id> suffixes (e.g. claude/fix-branch-mismatch-9P9o1--wp-backend) and merge back into $FEATURE_BRANCH at integration time.
2. Review Existing Context (Parallel Exploration) [all tiers]
Gather context from multiple sources concurrently using Task(Explore) agents.
Resolve the analyst archetype before dispatching:
import sys
from pathlib import Path
bridge_scripts = Path("<skill-base-dir>").parent / "coordination-bridge" / "scripts"
sys.path.insert(0, str(bridge_scripts))
import coordination_bridge
resolved = coordination_bridge.try_resolve_archetype_for_phase("PLAN", {})
analyst_model = resolved["model"] if resolved else None
If resolution is unavailable, omit model= from Task so the harness uses
its configured default.
Task(subagent_type="Explore", model=analyst_model, prompt="Read openspec/project.md and summarize the project purpose, tech stack, and conventions", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Run 'openspec list --specs' and summarize existing specifications", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Run 'openspec list' and identify in-progress changes that might conflict with: $ARGUMENTS", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Search the codebase for existing implementations related to: $ARGUMENTS", run_in_background=true)
Task(subagent_type="Explore", model=analyst_model, prompt="Read docs/architecture-analysis/architecture.summary.json and parallel_zones.json for component inventory and safe parallel zones", run_in_background=true)
Wait for all results and synthesize into unified context summary.
Before creating artifacts, ensure architecture artifacts are current:
if [ ! -f docs/architecture-analysis/architecture.summary.json ] || \
[ "$(git log -1 --format=%ct main)" -gt "$(stat -f %m docs/architecture-analysis/architecture.summary.json 2>/dev/null || echo 0)" ]; then
make architecture
fi
3. Present Context and Discovery Questions [all tiers]
This step ensures the user has visibility into what was discovered and shapes the plan before any artifacts are generated.
3a. Present Discovered Context
Format the Explore agent results from Step 2 as a structured summary and present it to the user via regular message output:
## What I Found
### Related Specs
- <spec-name>: <one-line summary of relevance>
### Related Code
- <file-path>: <what it does and how it relates to this feature>
### Potential Conflicts
- <in-progress change-id>: <what it touches that overlaps with this feature>
(or "None found" if no conflicts)
### Architectural Constraints
- <constraint from architecture analysis or parallel_zones.json>
### Prior Art (--explore mode only)
- <pattern, library, or approach found via web search>
(Skip this section if EXPLORE_MODE=false or web search is unavailable)
This gives the user the same information you have, enabling better answers to the questions that follow.
3b. Discovery Questions
Ask clarifying questions using the AskUserQuestion tool. Draw from these six categories, selecting the most relevant ones based on the discovered context:
-
Motivation / latent intent -- Use AskUserQuestion without preset options (open-ended). Ask FIRST when INTERVIEW_MODE=true.
Goal: separate the user's surface request from the underlying need. A user who asks for "a microservice" may actually want "deployment isolation" -- surfacing that opens better approaches.
Examples: "What made you reach for this idea today -- what recent moment or pain triggered it?" / "If this feature existed and worked perfectly, what would you stop having to do?" / "What's the closest thing to this that already exists, and why isn't it enough?"
-
Scope boundaries -- Use AskUserQuestion with preset options.
Examples: "Should be included in scope?" Options: "Yes, include it" / "No, out of scope" / "Defer to a follow-up proposal"
-
Trade-off preferences -- Use AskUserQuestion with preset options presenting 2-3 positions.
Examples: "I see a trade-off between <simplicity vs flexibility / speed vs correctness / etc>. Which direction?" Options describe each position.
-
Constraint discovery -- Use AskUserQuestion without preset options (open-ended).
Examples: "Are there timeline, compatibility, or performance constraints I should know about?" / "Are there any rejected approaches or past attempts I should avoid?"
-
Existing decisions -- Use AskUserQuestion with preset options when specific decisions are discoverable from context.
Examples: "The codebase currently uses for similar features. Should we follow this pattern or introduce a new one?" Options: "Follow existing pattern" / "New approach because "
-
Success criteria -- Use AskUserQuestion without preset options (open-ended).
Examples: "What does success look like for this feature?" / "How will you know this feature is working correctly?"
Rules for question generation:
- Questions MUST reference specific discoveries from Step 3a (e.g., "I found spec X covers Y. Should this feature extend that spec or create a new one?")
- Skip categories that are already clearly answered by
$ARGUMENTS (but in INTERVIEW_MODE, never skip category 1 -- latent intent)
- If
EXPLORE_MODE=true, add prior-art questions: "I found <pattern/library X> is commonly used for this. Should we adopt it, adapt it, or build custom?"
- Ask questions in batches of 2-4 via AskUserQuestion to avoid overwhelming the user
- Prioritize questions where the answer would significantly change the approach
3b.i. Standard Discovery Loop (INTERVIEW_MODE=false)
Ask MIN_QUESTIONS to MAX_QUESTIONS clarifying questions in one or two batches, then proceed to Step 4.
STOP -- Wait for the user to answer all discovery questions before proceeding to Step 4. Do NOT generate any artifacts until all answers are received.
3b.ii. Confidence-Gated Interview Loop (INTERVIEW_MODE=true)
Replaces the fixed question budget with an adaptive loop. Keeps asking -- in batches of 2-4 -- until self-assessed confidence reaches CONFIDENCE_THRESHOLD (default 0.95) on all five dimensions of the rubric, or the user explicitly asks to move on.
Confidence rubric. After each answer batch, score each dimension from 0.0 to 1.0:
| Dimension | What it means | Raises score | Lowers score |
|---|
problem_clarity | I understand the real problem, not just the stated request | User articulated a concrete pain and "what they'd stop doing" | Request is solution-shaped with no underlying problem named |
success_criteria | I know how we'll verify this worked | User gave measurable/observable outcomes | Only vague adjectives ("better", "faster") |
scope_edges | I know what's in vs. out | User confirmed or excluded related capabilities | Adjacent capabilities remain ambiguous |
hidden_constraints | I know the non-obvious restrictions | User surfaced timeline, compat, rejected approaches | User gave none and my probing didn't surface any |
why_now | I understand the triggering context | User named a recent event or deadline | "It just seemed useful" |
Loop:
questions_asked = 0
batch = generate_opening_batch(category=1 first, then 2-4 relevant categories)
while True:
ask(batch) via AskUserQuestion
wait for answers
questions_asked += len(batch)
scores = self_score_confidence(answers_so_far, rubric)
min_score = min(scores.values())
log("Confidence: " + format_scores(scores))
# Exit conditions (checked in order)
if min_score >= CONFIDENCE_THRESHOLD and questions_asked >= MIN_QUESTIONS:
break
if user_said_move_on(answers):
break
if questions_asked >= MAX_QUESTIONS:
# Soft ceiling -- surface confidence and ask permission to continue
ask_user_whether_to_continue(scores)
if user declines: break
# else: continue loop
# Contradiction check: compare new answers against Step 3a discoveries
# and prior answers. A contradiction always spawns a follow-up.
contradictions = detect_contradictions(answers, context_summary)
# Generate next batch targeted at the weakest dimension(s) or contradictions.
batch = generate_followup_batch(
weakest_dimensions=[d for d,s in scores.items() if s < CONFIDENCE_THRESHOLD],
contradictions=contradictions,
size=min(4, max(2, num_remaining_gaps)),
)
Surfacing contradictions. When a user answer conflicts with a Step 3a discovery (e.g., user says "we have no auth today" but the explore agent found an existing auth spec), do NOT paper over it. The next batch includes a direct question: "I found which seems to conflict with your answer that . Can you reconcile these for me?"
Confidence report. When the loop exits, print a one-line summary: Interview complete: <N> questions asked, confidence {problem: 0.95, success: 0.97, scope: 0.92, constraints: 1.0, why_now: 0.95}. If any dimension is below threshold at exit (because the user moved on), flag it as an explicit assumption in Step 4's proposal.
STOP -- Do NOT generate any artifacts until the loop exits.
4. Create Proposal with Approaches [all tiers]
Preferred path: Use runtime-native fast-forward/new workflow.
CLI fallback path:
openspec new change "<change-id>"
openspec instructions proposal --change "<change-id>"
Generate ONLY proposal.md at this stage. Incorporate user answers from Step 3b into the Why and What Changes sections.
Mandatory: Approaches Considered section. Generate MIN_APPROACHES to MAX_APPROACHES genuinely distinct approaches. For each approach, include:
- Name: Short descriptive name
- Description: 1-2 sentences on how it works
- Pros: Bullet list
- Cons: Bullet list
- Effort: S / M / L
Mark one approach as Recommended with a rationale that references specific pros/cons.
If EXPLORE_MODE=true, reference prior art discoveries in approach descriptions where relevant.
Expected artifact (this step only):
openspec/changes/<change-id>/proposal.md
5. Gate 1: Direction Approval [all tiers]
Present proposal.md to the user and ask them to select an approach.
Latent-intent check (INTERVIEW_MODE=true only). Before presenting the approach-selection question, surface the trade-off explicitly against the motivation captured in Step 3b category 1:
"Approach N optimizes for <X> and accepts <trade-off Y>. Earlier you said the underlying need was <latent goal Z>. Does this approach actually get you Z, or are we optimizing for the surface request instead?"
Ask this as a short open-ended AskUserQuestion before the selection question. If the user's answer reveals a mismatch, loop back to Step 4 with the corrected objective rather than letting them pick an approach that misses their real goal.
Use AskUserQuestion with these options:
- One option per approach: "Proceed with Approach N: " (description: brief summary of what this means)
- "Modify approaches" (description: "I'll revise the approaches based on your feedback")
- "Need more detail before deciding" (description: "I'll research further and ask follow-up questions")
STOP -- Wait for the user to select an approach.
After selection:
- If "Modify approaches": gather feedback, loop back to Step 4
- If "Need more detail": ask follow-up questions, loop back to Step 4
- If an approach is selected: update
proposal.md with a ### Selected Approach subsection recording the choice and any modifications the user requested. Demote unselected approaches to brief entries under the Recommended subsection.
Task Sizing Reference
Use the table below when generating tasks.md (Step 6) and work-packages.yaml (Step 8). Sizing is about agent attention budget, not raw effort — a task should fit inside one focused implementation session.
| Size | Time budget | Typical scope | Action |
|---|
| XS | ≤ 30 min | One-line edit, doc tweak, single test | Ship as-is. |
| S | 30 min – 2 hr | Single function/method change, narrow refactor | Ship as-is. |
| M | 2 hr – 1 day | Multi-file change in one module, new test suite | Ship as-is; checkpoint after. |
| L | 1 – 3 days | Cross-module change, new component, schema migration | CONSIDER SPLITTING. Try to decompose into 2–3 M-sized tasks. If splitting doesn't reduce risk, keep but flag. |
| XL | > 3 days | Cross-system, multiple subsystems, new bounded context | MUST SPLIT. Do not generate a tasks.md entry at XL — go back to Step 6 and decompose. |
"And" splitting heuristic
If a task title contains the word "and" OR describes two distinct outcomes, split it. The conjunction is almost always a sign that two tasks have been collapsed into one.
| Original title | Decision | Result |
|---|
| "Add caching and metrics" | Split — two outcomes | "Add caching" + "Add metrics" |
| "Wire EventBus and retire polling" | Split — two outcomes | "Wire EventBus" + "Retire polling fallback" |
| "Refactor user auth" | Keep — single outcome | (no change) |
| "Investigate flaky test and propose fix" | Split — investigation and fix have different completion criteria | "Investigate flaky test" + "Apply fix from investigation" |
| "Stand up Postgres and Redis" | Split — two independent dependencies | "Stand up Postgres" + "Stand up Redis" |
The heuristic is mechanical on purpose: it catches almost all hidden multi-tasks without requiring deep context.
Checkpoint cadence
Every 2–3 tasks, the implementing agent should pause for a self-check:
- Run the test suite (or the relevant subset) and confirm it's green.
- Review the cumulative diff (
git diff main...HEAD) and confirm every change maps to a completed task.
- Verify no scope creep: the diff touches only files inside the package's
write_allow.
- Update
tasks.md checkboxes for completed tasks (per /implement-feature Step 4 discipline).
This cadence is recorded in tasks.md as explicit - [ ] Checkpoint: run tests, review diff, verify scope markers between groups of 2–3 implementation tasks. The checkpoint isn't decorative — it's the integration point where Rules 0.5 (scope) and 2 (compilable) get verified.
6. Generate Specs, Tasks, and Design [all tiers]
Now that the direction is confirmed, generate the remaining planning artifacts.
CLI fallback path:
openspec instructions specs --change "<change-id>"
openspec instructions tasks --change "<change-id>"
openspec instructions design --change "<change-id>"
Critical: The selected approach from Gate 1 MUST drive all artifact content. Tasks must implement the selected approach specifically, not a generic solution. Specs must reflect the chosen approach's behavior.
Spec format — strict delta headers: The openspec validate CI check will reject specs that don't follow this exact format. Every spec file MUST use:
## ADDED Requirements (or MODIFIED/REMOVED/RENAMED) as the top-level section — NOT ## Requirements
### Requirement: <Name> for each requirement block — NOT ### REQ-ID: or other heading styles
#### Scenario: <name> with WHEN/THEN/AND structure under each requirement — at least one per requirement
SHALL or MUST language for normative statements
Verify after generating: openspec validate <change-id>. If it fails, fix the spec before proceeding.
Task ordering — TDD test-first: Within each phase of tasks.md, list test
tasks before the implementation tasks they verify. Each implementation task
should declare a dependency on its corresponding test task. This ensures
/implement-feature writes tests (RED) before writing code to make them pass
(GREEN).
Test tasks must reference the artifacts they validate:
- Spec scenarios: Each test task lists the spec scenario IDs (e.g.,
agent-coordinator.3) it encodes. Every SHALL/MUST scenario in specs must be covered by at least one test task.
- Contracts: If contracts exist (local-parallel+ tiers), test tasks reference the contract files they assert against — OpenAPI endpoint schemas, DB schema constraints, event payload JSON schemas. This ensures tests validate the contracted interface, not just internal behavior.
- Design decisions: If
design.md exists, test tasks reference the decision IDs (e.g., D3) they verify. This catches cases where the implementation accidentally uses an approach that was explicitly rejected.
Example:
- [ ] 1.1 Write tests for EventBusService — callback dispatch, reconnection
**Spec scenarios**: agent-coordinator.1 (event delivery), agent-coordinator.2 (reconnection)
**Contracts**: contracts/events/coordinator.schema.json
**Design decisions**: D3 (pg_notify over polling)
**Dependencies**: None
- [ ] 1.2 Create event_bus.py — EventBusService with on_event() registration
**Dependencies**: 1.1
Expected artifacts:
openspec/changes/<change-id>/specs/<capability>/spec.md
openspec/changes/<change-id>/tasks.md
- Optional
openspec/changes/<change-id>/design.md
7. Generate Contracts [all tiers]
For sequential-tier plans, include only contract sub-types applicable to the feature:
- OpenAPI contracts if the feature introduces or modifies API endpoints
- Database contracts if the feature introduces or modifies database schemas
- Event contracts if the feature introduces or modifies events
- Type generation stubs derived from the contracts above
If no contract sub-types are applicable (e.g., pure documentation or skill-definition changes), create contracts/README.md with a stub documenting which sub-types were evaluated and why none apply. Consuming skills treat a contracts/ directory containing only README.md as "no contracts applicable".
Produce machine-readable interface definitions in contracts/. Contracts become the coordination boundary between parallel agents.
7a. OpenAPI Contracts
For API features, generate from openspec/schemas/feature-workflow/templates/openapi-stub.yaml:
contracts/openapi/v1.yaml
Requirements: every endpoint has request/response schemas with example fields, discriminator fields for polymorphic responses, error response schemas following RFC 7807.
7b. Database Contracts
contracts/db/schema.sql
contracts/db/seed.sql
7c. Event Contracts
contracts/events/user.created.schema.json
7d. Type Generation Stubs
contracts/generated/models.py
contracts/generated/types.ts
8. Generate Work Packages [all tiers]
For sequential-tier plans, generate a single wp-main package encompassing the full feature scope:
packages:
- id: wp-main
name: "<feature-name> — full scope"
description: "<feature description>"
tasks: [all task IDs from tasks.md]
priority: 1
dependencies: []
scope:
write_allow: ["**"]
read_allow: ["**"]
verification: tier_b
This provides a uniform artifact format across all tiers. The wp-main package can later be split into parallel packages if the feature is upgraded to local-parallel tier.
For local-parallel and coordinated tiers, decompose tasks into agent-scoped work packages as described below.
Decompose tasks into agent-scoped work packages in work-packages.yaml. Follow the schema at openspec/schemas/work-packages.schema.json.
8a. Package Decomposition
Group tasks by architectural boundary:
wp-contracts -- Generate/validate contracts (always first, priority 1)
wp-<backend-module> -- Backend implementation per bounded context
wp-<frontend-module> -- Frontend implementation per component group
wp-integration -- Merge worktrees and run full test suite (always last)
8b. Scope Assignment
For each package, declare explicit file scope:
scope:
write_allow:
- "src/api/**"
read_allow:
- "src/**"
- "contracts/**"
deny:
- "src/frontend/**"
Rule: Parallel packages MUST have non-overlapping write_allow scopes.
8c. Lock Declaration
locks:
files:
- "src/api/users.py"
keys:
- "api:GET /v1/users"
- "db:schema:users"
- "event:user.created"
ttl_minutes: 120
reason: "Backend API implementation"
Follow canonicalization rules from docs/lock-key-namespaces.md.
8d. Dependency DAG
wp-contracts has no dependencies (root)
- Implementation packages depend on
wp-contracts
wp-integration depends on all implementation packages
8e. Verification Steps
Assign verification tier per package: Tier A (full), Tier B (CI), Tier C (static).
9. Validate All Artifacts [all tiers]
All tiers:
openspec validate <change-id> --strict
Local-parallel+ tiers (additionally):
python3 "<skill-base-dir>/../validate-packages/scripts/validate_work_packages.py" \
openspec/changes/<change-id>/work-packages.yaml
python3 "<skill-base-dir>/../refresh-architecture/scripts/parallel_zones.py" \
--validate-packages openspec/changes/<change-id>/work-packages.yaml --json
Fix any validation errors before proceeding.
10. Register Resource Claims [coordinated only]
Skip unless TIER is "coordinated" and CAN_LOCK=true.
Pre-register resource claims with the coordinator:
For each package in work-packages.yaml:
For each lock key in package.locks.keys:
acquire_lock(file_path=key, reason="planned: <feature-id>/<package-id>", ttl_minutes=0)
Use ttl_minutes=0 for planning claims -- they signal intent without expiring.
11. Commit, Push, and Pin Worktree [all tiers]
git add openspec/changes/<change-id>/
git commit -m "plan: <change-id> -- proposal, design, specs, tasks, contracts, work-packages"
git push -u origin "$FEATURE_BRANCH"
python3 "<skill-base-dir>/../worktree/scripts/worktree.py" pin "<change-id>"
11.5. Append Session Log [all tiers]
Construct a PhaseRecord for the Plan phase and call write_both(). This persists the session-log markdown AND the coordinator handoff from a single in-memory record, so the structured fields (Decisions, Alternatives, Trade-offs, Open Questions) flow into both the human-readable log and the next phase's incoming handoff.
Capture from this planning session:
- Decisions — Architecture decisions, scope boundaries, tier selection rationale. Tag long-lived decisions with
capability="<kebab-case>" so the per-capability decision index picks them up.
- Alternatives Considered — Approaches considered and rejected during planning, with reasons.
- Trade-offs — Trade-offs accepted, with reasons.
- Open Questions — Unresolved questions for implementation.
- Summary — 2–3 sentences: planning goal and what was decided.
- Next Steps (optional) — What the implementation phase should pick up first.
- Relevant Files (optional) — Key artifacts produced (
proposal.md, design.md, etc.).
Persist via PhaseRecord.write_both():
This step MUST run BEFORE the git add in Step 11 so the session-log entry is included in that commit. If you already committed in Step 11, amend with git add openspec/changes/<change-id>/ && git commit --amend --no-edit and re-push.
python3 - <<'EOF'
import sys
from pathlib import Path
sys.path.insert(0, str(Path("<skill-base-dir>").parent / "session-log" / "scripts"))
from phase_record import PhaseRecord, Decision, Alternative, TradeOff, FileRef
record = PhaseRecord(
change_id="<change-id>",
phase_name="Plan",
agent_type="<agent-type>",
summary="<2-3 sentences on planning goal + decisions>",
decisions=[
Decision(title="<title>", rationale="<rationale>", capability="<kebab-case-capability>"),
],
alternatives=[Alternative(alternative="<approach>", reason="<rejection reason>")],
trade_offs=[TradeOff(accepted="<X>", over="<Y>", reason="<reason>")],
open_questions=["<question>"],
next_steps=["<what implement-feature should tackle first>"],
relevant_files=[FileRef(path="openspec/changes/<change-id>/proposal.md", description="approved proposal")],
)
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 the rendered markdown to
openspec/changes/<change-id>/session-log.md
- Sanitize the file in-place via
sanitize_session_log.py
- Coordinator handoff via
try_handoff_write — falls back to openspec/changes/<change-id>/handoffs/plan-<N>.json on failure
Each step logs a warning if it fails but does not raise, so the workflow continues even if the coordinator is unreachable. Verify the rendered session-log.md: all populated sections present, no incorrect [REDACTED:*] markers, markdown intact. If over-redacted, fix the offending content in your PhaseRecord construction and re-run — write_both() appends a new entry rather than rewriting in place.
12. Gate 2: Plan Approval [all tiers]
Present the complete plan to the user:
proposal.md -- What, why, and selected approach
design.md -- How (if applicable)
tasks.md -- Implementation plan
All tiers (including sequential):
contracts/ -- Machine-readable interfaces (or README stub if no interfaces apply)
work-packages.yaml -- Execution plan (single wp-main for sequential, DAG for parallel)
Highlight any assumptions made during artifact generation that were not explicitly confirmed by the user.
Use AskUserQuestion to request final approval with these options:
- "Approve -- proceed to implementation" (description: "Plan is ready. Next step: /implement-feature ")
- "Revise tasks" (description: "Keep the approach but adjust the implementation plan")
- "Revise approach" (description: "Go back to approach selection with different options")
- "Reject -- start over" (description: "Discard this proposal and start fresh")
On Approve -- seed coordinator issues (D3):
After the user selects "Approve -- proceed to implementation", invoke the
seeder to create coordinator issues for every hand-authored task in tasks.md:
python3 "<skill-base-dir>/../coordinator-task-status-renderer/scripts/seed_tasks_from_md.py" <change-id>
The seeder is idempotent on the (change:<id>, task:<key>) label pair
(per D3 / D7): re-runs after partial seeding only POST new tasks and skip any
that already carry the matching task:<key> label. If the coordinator is
unreachable, the seeder logs a warning and exits 0 — Gate 2 still completes,
and /implement-feature retries seeding on its first invocation per D11.
If CAN_HANDOFF=true, write completion handoff.
STOP HERE -- Wait for approval before proceeding to implementation.
- If "Revise tasks": gather feedback, loop back to Step 6
- If "Revise approach": loop back to Step 4
- If "Reject": teardown worktree and exit
Output
openspec/changes/<change-id>/proposal.md
openspec/changes/<change-id>/design.md
openspec/changes/<change-id>/tasks.md
openspec/changes/<change-id>/specs/**/spec.md
openspec/changes/<change-id>/contracts/
openspec/changes/<change-id>/work-packages.yaml
Context Slicing for Implementation
When /implement-feature dispatches work packages, each agent receives only the context it needs:
| Package Type | Context Slice |
|---|
wp-contracts | proposal.md + spec deltas + contract templates |
| Backend packages | design.md (backend section) + contracts/openapi/ + package scope |
| Frontend packages | design.md (frontend section) + contracts/generated/types.ts + package scope |
wp-integration | Full work-packages.yaml + all contract artifacts |
wp-main (sequential) | proposal.md + design.md (if exists) + contracts/ + full task list |
Next Step
After approval:
/implement-feature <change-id>
Common Rationalizations
| Rationalization | Why it's wrong |
|---|
| "I know what the user wants — I can skip discovery" | The fastest way to redo a plan is to skip the gates that prevent rework. Steps 3 and 5 are cheap insurance against generating tasks.md against the wrong target. |
| "An L-sized task is fine — splitting it is overhead" | L tasks routinely become XL once implementation starts; the split costs less now (in plan) than mid-implementation (where it forces work-package re-decomposition and lock re-acquisition). |
| "The title says 'and' but they're tightly coupled — keep as one task" | Tightly coupled work usually deserves a shared dependency between two tasks, not a single fused task. Fusing hides the second outcome from the checkpoint cadence. |
| "I'll skip the contracts step — this is a small feature" | Contracts are the coordination boundary between parallel agents AND the artifact that lets /validate-feature distinguish implementation from spec. Even a contracts/README.md stub documents that the question was asked. |
| "Approaches Considered is busywork — I already know the right approach" | The Approaches section forces you to articulate at least one alternative; if you can't, the "right" approach is probably untested. Reviewers (and Gate 1) need this to push back. |
Red Flags
- A task in
tasks.md whose title contains the word "and" (almost always a hidden two-task; see "And splitting heuristic").
- An L-sized task with no decomposition attempt recorded in
design.md.
- An XL task that survived to Gate 2 (Step 12) without being split.
work-packages.yaml declares parallel packages with overlapping write_allow scopes (violates the parallel-package rule in Step 8b).
- A spec file uses
## Requirements instead of ## ADDED Requirements / ## MODIFIED Requirements — openspec validate will reject it.
proposal.md lacks an "Approaches Considered" section, or has only one approach listed.
- No checkpoint markers in
tasks.md between groups of 2–3 implementation tasks.
Verification
- Cite the size class assigned to each task in
tasks.md and confirm none are XL and ≤ 1 are L (per Task Sizing Reference).
- Run
grep -i ' and ' openspec/changes/<change-id>/tasks.md | grep -E '^\s*- \[' and confirm any matches are intentional (and noted in design.md).
- Confirm
tasks.md contains at least one Checkpoint: marker per 3 implementation tasks.
- Confirm
openspec validate <change-id> --strict passes (Step 9).
- Confirm
proposal.md lists ≥ MIN_APPROACHES distinct approaches and marks one as Recommended (Step 4).
- Confirm Gate 1 and Gate 2 user-approval responses are recorded in the session log (Step 11.5).