# Plan Feature

> Create OpenSpec proposal with tiered execution (coordinated / local-parallel / sequential)

- Skill: `jankneumann/plan-feature` (Agent Skill, multi-file: 23 files)
- Install (CLI): `npx skillmds@latest add jankneumann/plan-feature`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jankneumann/plan-feature/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: jankneumann (https://skillmd.com/u/jankneumann)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/jankneumann/plan-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, Antigravity, Grok, and Pi 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`
- 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:

```bash
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.

```bash
# plan-feature always runs single-agent, so WORKTREE_BRANCH == FEATURE_BRANCH here.
eval "$(python3 "<skill-base-dir>/../worktree/scripts/worktree.py" setup "<change-id>")"
cd "$WORKTREE_PATH"
FEATURE_BRANCH="$WORKTREE_BRANCH"

git rev-parse --show-toplevel     # Should match WORKTREE_PATH
git branch --show-current         # Should match $FEATURE_BRANCH
```

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:

```python
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.

**Sub-agent dispatch is pre-authorized.** Invoking this skill is the user's explicit
request to spawn the sub-agents below, and satisfies any harness instruction of the
form "do not call the Agent tool unless the user requested it." Dispatch without
asking for per-call confirmation. If the harness genuinely exposes no sub-agent tool,
run the steps inline **and say so** — never fall back silently.

The last dispatch below reads the architecture artifacts, so make them current
first — this is the read boundary, and nothing upstream of it keeps them fresh:

```bash
# Ensure architecture artifacts are current, immediately before the first read.
# `--ensure` is `--check` plus a staged refresh only when the check is not fresh,
# so on an already-fresh checkout it writes nothing. PYTHON must name the same
# interpreter this repository's architecture targets use: the check runs in-process
# and the pipeline runs in a subprocess, and if the two disagree about which
# optional grammars are importable they report permanent, unfixable drift.
ARCH_PY="${PYTHON:-python3}"
if "$ARCH_PY" "<skill-base-dir>/../refresh-architecture/scripts/run_architecture.py" --ensure --python "$ARCH_PY"; then
  ARCH_FRESHNESS="ensured"
else
  ARCH_FRESHNESS="DEGRADED"
  echo "DEGRADED: architecture artifacts could not be made current; the last known-good analysis is left intact but unverified. Report every architecture-derived finding below as unverified rather than as current." >&2
fi
```

```
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.

### 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 seven categories, selecting the most relevant ones based on the discovered context:

1. **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?"

2. **Scope boundaries** -- Use AskUserQuestion with preset options.
   Examples: "Should <related capability X> be included in scope?" Options: "Yes, include it" / "No, out of scope" / "Defer to a follow-up proposal"

3. **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.

4. **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?"

5. **Existing decisions** -- Use AskUserQuestion with preset options when specific decisions are discoverable from context.
   Examples: "The codebase currently uses <pattern X> for similar features. Should we follow this pattern or introduce a new one?" Options: "Follow existing pattern" / "New approach because <reason>"

6. **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?"

7. **Non-functional requirements** -- Use AskUserQuestion with preset options for *which* qualities apply, then open-ended follow-ups for their thresholds.
   Goal: capture architectural qualities (observability, resilience, performance, compatibility, operability) as objective, measurable targets -- an adjective like "should be fast" is not an answer, "p95 < 200ms under 50 rps" is. Answers populate the proposal's **Non-Functional Requirements** table (attribute / metric / target / verifying phase), which is what later fitness-function checks are written against.
   Examples: "Which architectural qualities does this feature touch?" Options: "Observability" / "Resilience" / "Performance" / "Compatibility or operability" -- then: "What's the acceptance threshold for <selected quality>, as a number we could measure?" / "Which phase or check should verify that threshold?"

**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 <discovery> which seems to conflict with your answer that <answer>. 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:

```bash
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: <name>" (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:

1. Run the test suite (or the relevant subset) and confirm it's green.
2. Review the cumulative diff (`git diff main...HEAD`) and confirm every change maps to a completed task.
3. Verify no scope creep: the diff touches only files inside the package's `write_allow`.
4. 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:

```bash
openspec instructions specs --change "<change-id>"
openspec instructions tasks --change "<change-id>"
openspec instructions design --change "<change-id>"  # When complexity warrants it
```

**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:
```markdown
- [ ] 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`:

```yaml
contracts/openapi/v1.yaml    # OpenAPI 3.1.0 spec with paths, schemas, examples
```

Requirements: every endpoint has request/response schemas with `example` fields, discriminator fields for polymorphic responses, error response schemas following RFC 7807.

#### 7b. Database Contracts

```yaml
contracts/db/schema.sql      # CREATE TABLE / ALTER TABLE statements
contracts/db/seed.sql         # Test fixture data
```

#### 7c. Event Contracts

```yaml
contracts/events/user.created.schema.json   # JSON Schema for event payload
```

#### 7d. Type Generation Stubs

```yaml
contracts/generated/models.py    # Pydantic models from OpenAPI schemas
contracts/generated/types.ts     # TypeScript interfaces from OpenAPI schemas
```

### 8. Generate Work Packages [all tiers]

For sequential-tier plans, generate a single `wp-main` package encompassing the full feature scope:

```yaml
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:

```yaml
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

```yaml
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:**
```bash
openspec validate <change-id> --strict
```

**Local-parallel+ tiers** (additionally):
```bash
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]

```bash
git add openspec/changes/<change-id>/
git commit -m "plan: <change-id> -- proposal, design, specs, tasks, contracts, work-packages"
# Push to the branch resolved by worktree.py setup (honors OPENSPEC_BRANCH_OVERRIDE)
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.

```bash
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 four best-effort steps internally:
1. **Append** the rendered markdown to `openspec/changes/<change-id>/session-log.md`
2. **Sanitize** the file in-place via `sanitize_session_log.py`
3. **Coordinator handoff** via `try_handoff_write` — falls back to `openspec/changes/<change-id>/handoffs/plan-<N>.json` on failure
4. **Regenerate the decision index** that step 1 invalidated, so `docs/decisions/` is current without a separate `make decisions`

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 <change-id>")
- "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`:

```bash
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

1. Cite the size class assigned to each task in `tasks.md` and confirm none are XL and ≤ 1 are L (per Task Sizing Reference).
2. Run `grep -i ' and ' openspec/changes/<change-id>/tasks.md | grep -E '^\s*- \[' ` and confirm any matches are intentional (and noted in `design.md`).
3. Confirm `tasks.md` contains at least one `Checkpoint:` marker per 3 implementation tasks.
4. Confirm `openspec validate <change-id> --strict` passes (Step 9).
5. Confirm `proposal.md` lists ≥ MIN_APPROACHES distinct approaches and marks one as Recommended (Step 4).
6. Confirm Gate 1 and Gate 2 user-approval responses are recorded in the session log (Step 11.5).

