Skip to content
Skills Library
Management & product · Planning

implementation-plan

/implementation-plan

Use when a user story or simple epic needs to be broken into atomic, TDD-driven implementation tasks with architectural review, or when planning the TDD task breakdown for a specific story or task ticket. Produces an ordered task list with explicit RED-test specs, identifies file impact and consumer/contract dependencies, runs an architectural review pass with a pass_threshold of 5, and writes the task tickets to the tracker with TDD task structure. Trigger phrases include 'plan this story', 'break this into tasks', 'implementation plan', 'plan the work', 'generate tasks', 'TDD task breakdown'

Licence
Apache-2.0
Last repository change
17.06.2026

Assessment and security

91/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content26 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
12
Scripts
0
HTML comments
19
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 17.06.2026, without references to retired models or APIs.

Licence

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

What to check before installing
  • SKILL.md zawiera komentarze HTML (19). Nie widać ich w podglądzie Markdown, a model je czyta.

What it does and when to use it

Use when a user story or simple epic needs to be broken into atomic, TDD-driven implementation tasks with architectural review, or when planning the TDD task breakdown for a specific story or task ticket. Produces an ordered task list with explicit RED-test specs, identifies file impact and consumer/contract dependencies, runs an architectural review pass with a pass_threshold of 5, and writes the task tickets to the tracker with TDD task structure. Trigger phrases include 'plan this story', 'break this into tasks', 'implementation plan', 'plan the work', 'generate tasks', 'TDD task breakdown'

Triggers from the description

plan this storybreak this into tasksimplementation planplan the workgenerate tasksTDD task breakdown
Original author description

Use when a user story or simple epic needs to be broken into atomic, TDD-driven implementation tasks with architectural review, or when planning the TDD task breakdown for a specific story or task ticket. Produces an ordered task list with explicit RED-test specs, identifies file impact and consumer/contract dependencies, runs an architectural review pass with a pass_threshold of 5, and writes the task tickets to the tracker with TDD task structure. Trigger phrases include 'plan this story', 'break this into tasks', 'implementation plan', 'plan the work', 'generate tasks', 'TDD task breakdown'

How it works

  1. Config Resolution
  2. Stage-Boundary Entry Check
  3. Usage
  4. Progress Checklist
  5. Pre-flight Tag Guards
  6. Step 1: Contextual Discovery
  7. Step 2: Consistency & Architectural Review
  8. Step 3: Atomic Task Drafting (via dso:task-decomposer opus sub-agent)
  9. Step 4: Implementation Plan Review
  10. Step 5: Task Creation
Original SKILL.md headings · 1,612 lines, 14,729 words
SKILL.md preview
---
name: implementation-plan
description: Use when a user story or simple epic needs to be broken into atomic, TDD-driven implementation tasks with architectural review, or when planning the TDD task breakdown for a specific story or task ticket. Produces an ordered task list with explicit RED-test specs, identifies file impact and consumer/contract dependencies, runs an architectural review pass with a pass_threshold of 5, and writes the task tickets to the tracker with TDD task structure. Trigger phrases include 'plan this story', 'break this into tasks', 'implementation plan', 'plan the work', 'generate tasks', 'TDD task breakdown', 'how should I implement this story'.
user-invocable: true
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
---

<HARD-GATE>
<SUB-AGENT-GUARD>
**/dso:implementation-plan is orchestrator-level only. It MUST NOT be invoked as a sub-agent.**

The skill dispatches its own sub-agents (`dso:approach-proposer`, `dso:approach-decision-maker`, `dso:task-decomposer`, opus gap-analysis on COMPLEX). Running implementation-plan ITSELF as a sub-agent means those dispatches become nested Task calls — which CLAUDE.md `rule:no-nested-task` forbids, and which the platform surfaces as `[Tool result missing due to internal error]`, silently losing the plan output.

**If you detect that this skill is running as a sub-agent**, STOP immediately and return verbatim:

```
ERROR: /dso:implementation-plan must be invoked at the orchestrator level, not as a sub-agent.
REASON: This skill dispatches sub-agents internally; nesting would violate rule:no-nested-task.
CALLER ACTION: Return control to your orchestrator and have IT invoke /dso:implementation-plan
              directly (via the Skill tool, not the Agent/Task tool).
```

Do NOT proceed with any of the Phase A/B/C steps. Do NOT dispatch any further sub-agents. Do NOT produce a partial plan.

**Proxy detection (best-effort)**: if the Agent tool is unavailable in your context (`<available_tools>` does not list `Agent` / `Task`), you are running as a sub-agent — refuse per above. If the Agent tool IS available, that is a necessary but not sufficient signal that you are at the orchestrator level; if the orchestrator's most recent user-facing turn does not match the trigger phrases for this skill (per the `description:` frontmatter), and you were invoked via Skill tool by an upstream skill rather than by the user directly, treat the invocation as suspect and prefer to STOP-and-return rather than proceed.

**Caller-side enforcement**: orchestrators that detect the ERROR return from this skill MUST halt their own progression rather than swallow the ERROR and continue — silently continuing produces a broken plan with missing sub-agent outputs.
</SUB-AGENT-GUARD>
</HARD-GATE>

<!-- Schema reference: docs/designs/stage-boundary-preconditions/ -->

# Implementation Plan: Atomic Task Generation

Generate a production-safe implementation plan for a User Story by decomposing it into atomic, TDD-driven tasks with correct dependencies. Prioritize understanding over assumptions — resolve ambiguity before planning.

## Config Resolution

At activation, load project commands and the approach-resolution mode:

```bash
PLUGIN_SCRIPTS="${CLAUDE_PLUGIN_ROOT}/scripts"
TEST_CMD=$(bash "$PLUGIN_SCRIPTS/read-config.sh" commands.test)             # shim-exempt: internal orchestration script
LINT_CMD=$(bash "$PLUGIN_SCRIPTS/read-config.sh" commands.lint)             # shim-exempt: internal orchestration script
FORMAT_CHECK_CMD=$(bash "$PLUGIN_SCRIPTS/read-config.sh" commands.format_check)   # shim-exempt: internal orchestration script
APPROACH_RESOLUTION=$(bash "$PLUGIN_SCRIPTS/read-config.sh" implementation_plan.approach_resolution)  # shim-exempt: internal orchestration script
APPROACH_RESOLUTION="${APPROACH_RESOLUTION:-autonomous}"   # autonomous (default) | interactive
```

Resolution order: see `${CLAUDE_PLUGIN_ROOT}/docs/CONFIG-RESOLUTION.md`. **Supports dryrun mode** (`/dso:dryrun /dso:implementation-plan`).

## Stage-Boundary Entry Check

```bash
source "${CLAUDE_PLUGIN_ROOT}/hooks/lib/preconditions-validator-lib.sh" 2>/dev/null || true
_dso_pv_entry_check "implementation-plan" "preplanning" "${STORY_ID:-${primary_ticket_id:-}}" || true
```

## Usage

```
/dso:implementation-plan                  # Interactive story selection
/dso:implementation-plan <story-id>       # Plan a specific user story
/dso:implementation-plan <epic-id>        # Plan simple epic directly (when routed by /dso:sprint)
```

If `<id>` is omitted, present an interactive list of open stories. Stories accept tasks under the story; epics (when routed by sprint) accept tasks directly under the epic.

## Progress Checklist

> **Task tracking rule**: Only call `TaskCreate` when `/dso:implementation-plan` is invoked **standalone** (directly by the user). When invoked from `/dso:sprint`, do NOT call `TaskCreate` — sprint owns the task list. Track progress through inline notes.

```
Progress:
- [ ] Step 1: Contextual Discovery (story loaded, context gathered, ambiguities resolved, cross-cutting detection done — layers: _, interfaces: _)
- [ ] Step 2: Architectural Review via REVIEW-PROTOCOL-WORKFLOW.md inline (passed / skipped — no new pattern)
- [ ] Step 3: Task Drafting (tasks drafted with E2E + docs coverage)
- [ ] Step 4: Plan Review via REVIEW-PROTOCOL-WORKFLOW.md inline (all dimensions: 5, iteration: _/3)
- [ ] Step 5: Task Creation (tasks created, deps added, health validated)
- [ ] Step 6: Gap Analysis (COMPLEX: opus sub-agent dispatched, findings processed; TRIVIAL: skipped)
```

## Pre-flight Tag Guards

Before any planning work, run a single tag-guard check on the ticket. Capture the exit code explicitly so a lookup failure (exit 2) is treated as fail-open rather than aborting under `set -e`:

```bash
set +e
_guard=$(bash "$PLUGIN_SCRIPTS/implementation-plan/check-tag-guards.sh" "${STORY_ID:-${primary_ticket_id}}")  # shim-exempt: internal orchestration script
_guard_rc=$?
set -e
# rc=0 → verdict in $_guard (OK or BLOCKED:*); rc=1 → BLOCKED in $_guard;
<!-- EMIT-PRECONDITIONS: gate_name=implementation_plan_tag_guard degradation_type=inferred_decision -->
# rc=2 → lookup failure, treat as OK (fail-open).
if (( _guard_rc == 2 )); then _guard="OK"; fi
```

The script returns one of: `OK`, `BLOCKED:scrutiny_pending`, `BLOCKED:interaction_deferred`, `BLOCKED:manual_awaiting_user`. The first two BLOCKED verdicts halt planning; `BLOCKED:manual_awaiting_user` is non-halting — it enters the branching logic below.

| Verdict | Action |
|---------|--------|
| `BLOCKED:scrutiny_pending` | **HALT.** Emit: "This epic has not been through scrutiny review. Run `/dso:brainstorm <epic-id>` first to complete the scrutiny pipeline, then retry `/dso:implementation-plan`." Do NOT produce any planning output. |
| `BLOCKED:interaction_deferred` | **HALT.** Emit: "This epic has unresolved cross-epic interaction conflicts. Resolve or override them in `/dso:brainstorm <epic-id>` before proceeding to `/dso:implementation-plan`." Do NOT produce any planning output. |
| `BLOCKED:manual_awaiting_user` | **Do NOT halt** — enter the Manual Story Branching section below. |
| `OK` | Proceed to Step 1 (Contextual Discovery). |

The `manual:awaiting_user` check is gated by `planning.external_dependency_block_enabled` — when the flag is absent or `false`, the script returns `OK` regardless of tags.

### Copy Story Bypass

Before continuing past the tag guards, detect whether this story is a **copy story** auto-created by `dso:story-decomposer` (Step C3 of the Copy-Needed Auto-Create Protocol). A story is a copy story when EITHER:

- it carries the `copy-story` tag (canonical signal), OR
- its title begins with the verbatim prefix `"Apply gov-copy to "` (case-insensitive fallback)

These match the producer-side values written by `${CLAUDE_PLUGIN_ROOT}/agents/story-decomposer.md` Step C3. If either signal fires:

1. **Do NOT decompose** — copy stories are dispatched to `dso:gov-copy-writer` by `/dso:sprint` Phase E (see sprint SKILL.md "Copy Story Dispatch" section), not by implementation-plan.
2. Emit exactly: `STATUS:bypass REASON:copy_story STORY:<story-id>`
3. Stop. Do not run Step 1 (Contextual Discovery) or any subsequent step.

The bypass is the canonical handoff from `/dso:implementation-plan` to `/dso:sprint`'s gov-copy-writer dispatcher. Decomposing a copy story into ordinary tasks would silently bypass the federal-style canon, the deterministic post-processor, and the coordination-pass — defeating the gov-copy epic's entire pipeline.

### Manual Story Branching (only when `BLOCKED:manual_awaiting_user`)

**Prep-work detection heuristic**: scan the story's done definitions for references to artifacts not yet in the codebase — a verification script path, a user-facing instructions document path, or a CLI wrapper that would need to be authored. Use Glob and `test -f` to confirm.

**Branch A — No prep work needed** (done definitions reference no new code artifacts):
- Do NOT decompose into tasks. Emit a refusal diagnostic and `STATUS:blocked REASON:manual_story_no_prep STORY:<story-id>`. The manual verification step is never decomposed.

**Branch B — Prep work required** (done definitions reference at least one missing artifact):
- Decompose ONLY the prep tasks using standard RED/GREEN/UPDATE classification. The manual verification step itself is NEVER a decomposed task.
- Read the parent epic's External Dependencies block (per `${CLAUDE_PLUGIN_ROOT}/docs/contracts/external-dependencies-block.md`) to seed prep-task context: use `name`, `verification_command`, and `justification` to populate prep-task descriptions.
- Continue to Step 1 with only the prep tasks in scope.

---

## Step 1: Contextual Discovery

### Select Story

If `<story-id>` was not provided:
1. `.claude/scripts/dso ticket list --type=story --status=open` to show open stories
2. Fall back to `.claude/scripts/dso ticket list --type=story` if none open
3. If no stories at all, report and exit
4. Present and get selection

Load: `.claude/scripts/dso ticket show <story-id>`. If not found, report the error and exit.

### Re-invocation Guard

Detect existing children before drafting:

```bash
# Capture exit code separately — rc=2 is the fail-open lookup-failure path
# and would abort the surrounding `set -e` context if not handled here.
set +e
_reinv=$(bash "$PLUGIN_SCRIPTS/implementation-plan/check-reinvocation.sh" "$STORY_ID")  # shim-exempt: internal orchestration script
_reinv_rc=$?
set -e
# rc=2 → lookup failed; the script already emitted verdict=fresh, treat as fresh.
# Any other non-zero rc is unexpected; surface and continue with verdict=fresh.
if (( _reinv_rc != 0 && _reinv_rc != 2 )); then
    echo "WARN: check-reinvocation.sh exited with unexpected rc=${_reinv_rc} — continuing with verdict=fresh"
    _reinv="verdict=fresh"
fi
# Parse KEY=VALUE output line-by-line (do NOT use `eval` — script output is
# trusted but eval on external command output is a fragile pattern).
verdict=$(echo "$_reinv" | grep '^verdict=' | cut -d= -f2-)
closed_count=$(echo "$_reinv" | grep '^closed_count=' | cut -d= -f2-)
in_progress_count=$(echo "$_reinv" | grep '^in_progress_count=' | cut -d= -f2-)
open_count=$(echo "$_reinv" | grep '^open_count=' | cut -d= -f2-)
closed_ids=$(echo "$_reinv" | grep '^closed_ids=' | cut -d= -f2-)
in_progress_ids=$(echo "$_reinv" | grep '^in_progress_ids=' | cut -d= -f2-)
open_ids=$(echo "$_reinv" | grep '^open_ids=' | cut -d= -f2-)
```

Branch on `verdict`:

- **`fresh`**: no children — proceed to Epic Type Detection.
- **`in_progress_hold`**: at least one in-progress child. **Hard hold** — emit `STATUS:blocked REASON:in_progress_children_detected TASKS:<in_progress_ids>` and stop. The sprint orchestrator should retry after those tasks complete.
- **`all_closed`**: all children closed/archived AND no unresolved REPLAN_TRIGGER:validation on the parent. Emit the early-exit STATUS line and stop:
  ```
  STATUS:complete TASKS:<closed_ids> STORY:<story-id>
  ```
  Do not proceed further.
- **`replan_required`**: all children closed/archived BUT the parent epic has an unresolved `REPLAN_TRIGGER:validation` comment mentioning this story — i.e., the completion-verifier reported a story-level FAIL after the original tasks closed, and no `REPLAN_RESOLVED:implementation-plan` has acknowledged it yet. Do NOT emit `STATUS:complete` (that would re-trigger the verifier ↔ impl-plan loop). Enter **diff-plan mode**: read the verifier's `remediation_tasks_created` payload from the parent epic's `REPLAN_TRIGGER:validation` comment, treat its uncovered DDs as the AC source, and produce NEW TDD task pairs (RED + GREEN) addressing each uncovered DD. Continue to Epic Type Detection with the new tasks in scope; on completion, emit a `REPLAN_RESOLVED: implementation-plan — Remediation tasks created for story <story-id>.` comment on the parent epic so the next re-invocation collapses back to `all_closed`.
- **`diff_plan`**: mixed open + closed children. Produce a diff plan covering only new tasks + revisions to open children — never touch closed children. Distinguish "new or reopened" tasks from unchanged ones in the output. Continue to Epic Type Detection with only the open/new tasks in scope.

Log a one-liner: `Re-invocation guard: <closed_count> closed (read-only), <in_progress_count> in-progress (flagged), <open_count> open (candidates)`.

### Epic Type Detection

Check the `type` field from `.claude/scripts/dso ticket show`:

- **`type=epic`**: enter **epic-direct mode** — the epic's done definitions are the AC source; tasks become direct children of the epic; skip Context File Check (no parent); use `--parent=<epic-id>` in Step 5; proceed directly to **Architectural Alignment**.
- **otherwise** (task, story, etc.): continue with Context File Check below.

### Context File Check

After loading the story, look for a recent preplanning context comment on the parent epic:

1. Extract `parent` from the story.
2. Run `.claude/scripts/dso ticket show <parent-epic-id>`. Scan `comments` for the LAST comment whose `body` starts with `PREPLANNING_CONTEXT:`.
3. If found AND embedded `generatedAt` is within 7 days:
   - Parse JSON payload (strip the `PREPLANNING_CONTEXT: ` prefix). On invalid JSON, treat as not found.
   - Load epic data + sibling stories from the payload (skip redundant fetches).
   - Carry forward: review findings, walking skeleton flags, classifications, traceability lines, story dashboard.
   - Log: `"Context loaded from preplanning comment on epic <parent-epic-id>"`.
   - **Skip Input Analysis** and proceed directly to Architectural Alignment.
4. Else: log `"No recent preplanning context — running full Input Analysis"` and proceed below.

**schema_version-aware parsing** (load-bearing wire format):
- Check `schema_version`. If absent or `< 2`: v1 mode — `researchFindings` not expected, treat as empty array.
- If `>= 2`: `researchFindings` expected; if absent, treat as empty array (fail-open).
<!-- EMIT-PRECONDITIONS: gate_name=implementation_plan_research_findings degradation_type=inferred_decision -->
- **Fail-open contract**: any parsing failure on `researchFindings` MUST NOT block context loading — treat as empty, log `"researchFindings parse failed on epic <parent-epic-id> — treating as empty"`, continue.

### Input Analysis

```bash
.claude/scripts/dso ticket show <story-id>
# Extract parent ID from the 'parent' field, then:
.claude/scripts/dso ticket show <parent-epic-id>
.claude/scripts/dso ticket deps <parent-epic-id>     # sibling stories
```

If no parent, proceed with story context alone and note the limited scope.

### Architectural Alignment

- Glob for `docs/**/*.md` and `.claude/docs/**/*.md`
- Grep for "system context", "architecture", "standardization", "ADR"
- Glob for `docs/adr/**/*.md`

### Recipe Consultation

Query the recipe registry to surface available mechanical transforms for the story's language context:

```bash
bash "$PLUGIN_SCRIPTS/consult-recipe-registry.sh" --language <primary-language>
```

Record the output as **available recipe capabilities**. Run without `--language` if the story spans multiple languages or the language is unknown. Empty output (unknown language or missing registry) is not an error — proceed without recipe context.

Recipe-eligible transforms are mechanical and deterministic: function-signature changes, import normalization. Business logic additions, feature decisions, and architectural changes are NOT recipe-eligible regardless of the registry contents.

### Migration-Class Detection

Run the migration-class detector against the story's primary target symbol to classify the migration scope before task drafting begins. This step is **unconditional and non-interactive** — it runs on every Step 1 execution, writes the marker regardless of outcome, and never prompts before persisting.

Determine `TARGET_SYMBOL` from the story description or ticket title (the function, method, or class being changed). If no symbol is determinable, pass an empty string — the script emits `migration-class=inconclusive` cleanly and the marker is still written.

```bash
MIGRATION_MARKER=$(bash "$PLUGIN_SCRIPTS/implementation-plan/migration-class-detect.sh" "<target_symbol>" 2>/dev/null)
.claude/scripts/dso ticket comment <story-id> "MIGRATION_CLASS: $MIGRATION_MARKER"
```

**Even when `migration-class=inconclusive`** (ast-grep unavailable or no symbol provided), write the marker. Never silently omit it — inconclusive is a first-class result, not an error state.

**Last-wins idiom**: re-running Step 1 appends a new `MIGRATION_CLASS:` comment. Consumers MUST read the **last** matching `MIGRATION_CLASS:` comment on the story ticket, ignoring any earlier ones. This mirrors the `COMPLEXITY_CLASSIFICATION:` scan-for-last-comment idiom.

The full marker contract (JSON shape, field definitions, threshold configuration, `sg`-unavailable degradation, consumer protocol) is at `${CLAUDE_PLUGIN_ROOT}/docs/contracts/migration-class-marker.md`.

Log a one-liner: `"Migration-class detection: <migration-class value> (symbol: <target_symbol>)"`.

### Feature-Flag Approval Detection

Run the feature-flag approval lookup for this story immediately after migration-class detection. This step is **unconditional and non-interactive** — it runs on every Step 1 execution, writes the marker regardless of outcome, and never prompts before persisting.

```bash
FEATURE_FLAGS_MARKER=$(bash "$PLUGIN_SCRIPTS/implementation-plan/resolve-feature-flag-approval.sh" "<story-id>" 2>/dev/null)
.claude/scripts/dso ticket comment <story-id> "FEATURE_FLAGS: $FEATURE_FLAGS_MARKER"
```

The helper performs the two-hop story→parent epic tag lookup and always exits 0 (see safe-default contract). When the `rollout:feature-flags-approved` tag is absent on both the story and its parent epic, the marker carries `"feature-flags":"prohibited"` with a non-empty `reason`.

**Last-wins idiom**: re-running Step 1 appends a new `FEATURE_FLAGS:` comment. Consumers MUST read the **last** matching `FEATURE_FLAGS:` comment on the story ticket, ignoring any earlier ones. This mirrors the `MIGRATION_CLASS:` scan-for-last-comment idiom.

The full marker contract (JSON shape, fields, source-of-truth helper, safe-default, consumer protocol) is at `${CLAUDE_PLUGIN_ROOT}/docs/contracts/feature-flags-marker.md`.

Log a one-liner: `"Feature-flag approval detection: <feature-flags value> (source: <source>, reason: <reason>)"`.

### Ambiguity Scan

**Curiosity before planning.** A plan built on assumptions is worse than no plan.

**Exploration decomposition**: when context-gathering involves compound or multi-source questions (multi-layer, web research, ambiguous scope), apply `skills/shared/prompts/exploration-decomposition.md`. Classify each question as SINGLE_SOURCE or MULTI_SOURCE. Emit DECOMPOSE_RECOMMENDED when a factor is unspecified or two findings contradict.

| Signal | Example | Action |
|--------|---------|--------|
| Undefined scope boundaries | "improve performance" — of what? by how much? | Ask for measurable criteria |
| Implicit acceptance criteria | "user can upload files" — types? size limits? | Ask for constraints |
| Conflicting signals | Epic says X, story says Y, codebase does Z | Surface the conflict |
| Missing persona | "as a user" — admin or end-user? | Ask which role |
| Unstated constraints | API story with no auth/rate-limiting mention | Ask if intentionally omitted |
| Ambiguous priority | Multiple criteria, unclear essential vs. nice-to-have | Ask for priority ranking |

Batch all questions in one message. Separate **blocking** ("cannot plan without this") from **defaultable** ("I'll assume X unless you say otherwise"). Never ask about things clearly inferrable from the codebase or parent epic.

### Unsatisfiable Criteria Detection

After resolving ambiguities, check whether the SC can be satisfied at all:

- SC contradicted by codebase state (e.g., closed ticket permanently removed the feature SC asks for)
- SC items mutually exclusive (A and B cannot both be true simultaneously)
- Architecture makes SC impossible without redesign beyond this story's scope

When any apply, emit and STOP — do NOT proceed:

```
REPLAN_ESCALATE: brainstorm EXPLANATION:<what SC cannot be satisfied, why (the codebase state contradicting it), and what the orchestrator should investigate>
```

This signal is terminal — do not emit STATUS:complete or STATUS:blocked after it.

**Distinction**: STATUS:blocked = user can answer questions to unblock (ambiguous requirements, missing info). REPLAN_ESCALATE = the story intent itself needs brainstorm-level re-examination; no clarifying question can unblock it.

### Cross-Cutting Change Detection

#### Evaluator Shortcut

If complexity-evaluator output was provided (when invoked from `/dso:sprint`):

1. Reuse `layers_touched` and `interfaces_affected` directly.
2. Sanity-check against the codebase context from Architectural Alignment. If counts seem wrong, note the discrepancy and proceed with corrected counts.
3. Apply the escalation rule below.
4. Skip the full analysis.

If no evaluator output (standalone invocation): perform full analysis.

#### Detection

1. **Trace data/control flow** from entry point to deepest dependency. Count distinct architectural layers touched. Example layer chain: route → service → agent → LLM provider → formatter → DB model → migration. **≥ 3 layers = cross-cutting threshold met.**
2. **Count interfaces/classes needing updates** via Grep/Glob for classes, abstract base types, Protocol definitions, and public method signatures. **≥ 5 interfaces/classes = cross-cutting threshold met.**

#### Escalation Rule

| Condition | Action |
|-----------|--------|
| ≥ 3 architectural layers touched | Force Step 2 — mark **CROSS-CUTTING** |
| ≥ 5 interfaces/classes need updates | Force Step 2 — mark **CROSS-CUTTING** |
| Both thresholds met | Force Step 2, note both signals |
| Neither threshold met | Proceed to new-pattern check; otherwise skip to Step 3 |

When escalating, annotate the Step 2 subject:
```
"Architectural Pattern: {pattern name} [CROSS-CUTTING — {N} layers / {M} interfaces]"
```

### Doc-Only Skip Gate

Before generating proposals, check whether the story exclusively touches documentation or instruction files (`.md` files in `skills/`, `agents/`, `prompts/`, `docs/`, `CLAUDE.md`, or any workflow/config file with no executable behavior). Apply behavioral-testing-standard.md Rule 5: instruction-file changes have only one meaningful implementation and carry no architectural decision. In that case:

1. Log: `"Doc-only story — skipping Proposal Generation and approach-decision-maker dispatch"`
2. Skip directly to **Step 3: Atomic Task Drafting** with `testing_mode: GREEN` for all tasks.
3. Do NOT generate proposals or dispatch the decision-maker.

Signals that indicate a doc-only story:
- Story title/description mentions updating a SKILL.md, CLAUDE.md, workflow doc, agent file, or other `.md` guidance file
- All affected files in the file impact table are `.md` or `.conf` with no associated executable code change
- Story is testing_mode: GREEN with only instruction file updates in scope

### Proposal Generation (via `dso:approach-proposer` opus sub-agent)

**Sync discipline** — the contract summary, dispatch wiring, validation steps, and `agents/approach-proposer.md` are dual-maintained. When updating complexity-gate criteria, the four distinctness axes, the minimum-proposal count, or any other rule below, you MUST update `agents/approach-proposer.md` in the same change to keep the two in sync. The structural test pins wiring + summary presence but does not detect semantic drift between this SKILL.md block and the agent's embedded protocol.

**Contract summary** (the agent enforces these; this block is the human-readable contract surface for SKILL.md readers and is pinned by `tests/scripts/test-implementation-plan-proposals.sh`):

- **Minimum count**: the agent must produce **at least 3 distinct proposals**. Fewer is permitted only when the solution space is genuinely constrained (≥ 2 required); the constraint must be documented in `generation_notes`.
- **Proposal schema** (six required fields per `prompts/proposal-schema.md`): `title`, `description`, `files`, `pros`, `cons`, `risk` (one of `low` / `medium` / `high`).
- **Distinctness validation gate**: every pair of proposals must differ on at least one of the four structural axes:
  - **data layer** — where state is stored or retrieved
  - **control flow** — execution path or orchestration strategy
  - **dependency graph** — modules, packages, or services introduced or removed
  - **interface boundary** — where the public contract is drawn
  A set with any structurally equivalent pair MUST NOT be emitted by the agent. The agent records pairwise comparisons in `distinctness_summary` so the audit trail proves the gate ran.
- **Complexity gates** (per `${CLAUDE_PLUGIN_ROOT}/skills/shared/prompts/complexity-gate.md`): every proposal is gated through Gate 1 (YAGNI), Gate 2 (Rule of Three), and Gate 3 (new-dependency) with outcomes recorded in `complexity_gate_summary`.

Read `shared/prompts/complexity-gate.md` to confirm it is present. If unreadable, STOP and emit:
"ERROR: complexity-gate.md not found at skills/shared/prompts/complexity-gate.md — create this file before running implementation-plan."

Dispatch via `subagent_type: "dso:approach-proposer"` with `model: "opus"` passed explicitly (do not rely on the agent frontmatter default — distinct-approach reasoning, complexity-gate evaluation, and structural-axis analysis depend on opus-level sustained reasoning, and the explicit param defends against future routing changes that might silently downgrade).

**Two distinct fallback concepts — do not conflate:**

- **Dispatch-target fallback** (this paragraph): if the named `subagent_type: "dso:approach-proposer"` is unregistered in this session ("Unknown agent type" or similar dispatch error), re-dispatch with `subagent_type: "general-purpose"` + `model: "opus"` and `agents/approach-proposer.md` content read inline as the prompt. **The agent still does the work** — only the dispatch transport changes. This is NOT inline drafting.
- **Inline-drafting fallback** (forbidden, see Validation below): the orchestrator itself drafts proposals without the agent. This is the bug class the dispatch was introduced to eliminate and is explicitly prohibited regardless of what dispatch path was taken.

Pass the following as task arguments:

- `{story-id}`, `{story-title}`, `{story-description}` from the story ticket
- `{story-done-definitions}` — the story's DDs, one per line, with stable identifiers (`dd-1`, `dd-2`, ...). Parse these from the story description's `## Done Definitions` section; do NOT rely on session memory.
- `{codebase-context}` — discovery output from Step 1 (relevant files, existing patterns, architectural reviewer findings, walking-skeleton flags)
- `{counter-proposal-feedback}` — if NEW_COUNT ≥ 1, the prior counter-proposal's `approach` and `done_definitions` from the cycle state file; otherwise pass `(none — first cycle)`

The agent returns a JSON object with `proposals`, `distinctness_summary`, `complexity_gate_summary`, and `generation_notes`. Validate the output in this order — do NOT reorder, the model-requirement check MUST run before schema validation:

<!-- VALIDATION-ORDER: model_requirement_unmet → domain-error escalation → schema validation. -->
<!-- VALIDATION-STEP-1: model_requirement_unmet -->
<!-- EMIT-PRECONDITIONS: gate_name=implementation_plan_approach_proposer_model_retry degradation_type=inferred_decision -->
1. **Model-requirement check (first)**: if the response carries `"error": "model_requirement_unmet"`, log `"Approach proposer model requirement unmet — re-dispatching with explicit model: opus"` and retry once via the fallback path. If the retry also returns `model_requirement_unmet`, HALT with diagnostic: `"Approach proposal generation requires opus; configure the environment to allow opus dispatch and re-run /dso:implementation-plan."` Do NOT fall back to inline proposal drafting.
<!-- VALIDATION-STEP-1.5: domain-error-escalation -->
2. **Domain-error escalation (second)**: if the response carries `"error": "insufficient_solution_space"` (documented in `agents/approach-proposer.md`), this is a legitimate signal that the story is genuinely constrained to fewer than 2 viable approaches — NOT a malformed response. Surface the agent's `generation_notes` to the user and HALT with diagnostic: `"Approach proposer: insufficient_solution_space — <generation_notes joined>. Refine the story constraints (or accept the single-approach path manually) and re-run /dso:implementation-plan."` Do NOT proceed to schema validation; an empty `proposals` array under this error code is the intended shape.
<!-- VALIDATION-STEP-2: schema-validation -->
3. **Schema validation (third)**: with both error-envelope branches ruled out, validate that `proposals` conforms to `prompts/proposal-schema.md` (every proposal has all six fields), `distinctness_summary` is a non-empty array of pairwise comparisons (every pair has `differs_on` non-empty), and `complexity_gate_summary` has one entry per proposal with all three gate outcomes recorded. If schema validation fails, HALT with diagnostic: `"Approach proposer returned malformed output; cannot proceed without verified proposals."` Do NOT fall back to inline drafting — inline drafting is the failure mode this phase exists to prevent.  <!-- precondition-emit-ok: negation — HALT, no degradation event -->
<!-- EMIT-PRECONDITIONS landmark above (line 329) also covers this section per the within-10-lines rule. -->


**Why halt (not skip-and-continue) here?** Proposal generation is a **correctness gate**: the selected approach drives every subsequent task in Step 3, and inline orchestrator drafting (the only available fallback) is the bug class this dispatch was introduced to eliminate.

**Approach resolution routing** (config-driven via `APPROACH_RESOLUTION`):

- **`autonomous`** (default): pass the agent's full `proposals` set to the decision-maker; use the selected proposal as the basis for Step 3. Do NOT show proposals to the user or wait for selection.
- **`interactive`**: display the agent's `proposals` to the user (title, description, pros, cons, risk); wait for user selection before Step 3. Do NOT dispatch the decision-maker.

### Resolution Loop

After generating a valid distinct proposal set, dispatch `dso:approach-decision-maker` to evaluate and select.

#### Cycle State

```bash
CYCLE_COUNT=$(bash "$PLUGIN_SCRIPTS/implementation-plan/approach-cycle-state.sh" read "$STORY_ID")  # shim-exempt: internal orchestration script
```

State file `/tmp/approach-resolution-${STORY_ID}.json` (4h TTL); stale files reset to 0 automatically.

#### Dispatch

Dispatch `dso:approach-decision-maker` (subagent_type, model: opus, timeout: 600000) with:
- All proposals (full set with title, description, files, pros, cons, risk)
- Story success criteria and done definitions
- Current codebase context

**Inline fallback**: if the Agent tool rejects the subagent type ("Unknown agent type", "not supported", or any pre-run dispatch failure), read `agents/approach-decision-maker.md` inline and execute its evaluation directly with the same inputs. The inline path must still produce a valid `APPROACH_DECISION:` output conforming to `docs/contracts/approach-decision-output.md`.

#### Parse Response

Scan output for the `APPROACH_DECISION:` prefix per `docs/contracts/approach-decision-output.md`. Extract the JSON block between ` ```json ` and ` ``` ` fences. Validate `mode`.

If output is absent, malformed, missing the prefix, or has an unrecognized `mode`: log a warning and surface to the user for manual selection. Do NOT autonomously fall back to any proposal.

#### Accept Path (`mode: "selection"`)

1. Read `selected_proposal_index`; extract the corresponding proposal.
2. Log the ADR rationale (`context`, `decision`, `consequences`, `rationale_summary`).
3. Autonomous mode: proceed directly to Step 3 with the selected proposal. Interactive mode: present selection and rationale; confirm before Step 3.
4. Clear cycle state: `bash "$PLUGIN_SCRIPTS/implementation-plan/approach-cycle-state.sh" clear "$STORY_ID"`.  # shim-exempt: documentation reference in skill prompt

#### Revise Path (`mode: "counter_proposal"`)

```bash
NEW_COUNT=$(bash "$PLUGIN_SCRIPTS/implementation-plan/approach-cycle-state.sh" increment "$STORY_ID")  # shim-exempt: internal orchestration script
```

If `NEW_COUNT <= 2`: incorporate the counter-proposal's `approach` and `done_definitions` as additional constraints; return to Proposal Generation with explicit guidance (new proposals must satisfy the original SC AND the counter-proposal's requirements); re-enter the Resolution Loop. If `NEW_COUNT > 2`: Escalate Path.

In interactive mode, briefly note that the decision-maker requested revisions and the loop is retrying.

#### Escalate Path (after 2 cycles)

Present:
- All proposals from the most recent generation
- All counter-proposal feedback across cycles (from state file + current agent output)
- Summary: "The decision-maker could not reach a satisfactory selection after 2 cycles. Please review the proposals and counter-proposal feedback, then select an approach manually."

In autonomous mode, emit `STATUS:blocked REASON:approach_escalated_to_user STORY:<story-id>` and pause. Do NOT proceed autonomously. After user selects, clear cycle state.

---

## Step 2: Consistency & Architectural Review

If the implementation does not require a new pattern (or modification to an existing one), skip to Step 3.

If a pattern change is proposed, read and execute `${CLAUDE_PLUGIN_ROOT}/docs/workflows/REVIEW-PROTOCOL-WORKFLOW.md` inline with:

- **subject**: `"Architectural Pattern: {pattern name}"`
- **artifact**: proposed pattern + relevant architecture docs and existing patterns from Step 1
- **pass_threshold**: `4`
- **start_stage**: `1`
- **perspectives**: from reviewer files in `docs/reviewers/architectural/`:
  - [docs/reviewers/architectural/best-practices.md](docs/reviewers/architectural/best-practices.md) — `"Best Practices"`
  - [docs/reviewers/architectural/project-alignment.md](docs/reviewers/architectural/project-alignment.md) — `"Project Alignment"`
  - [docs/reviewers/architectural/justification.md](docs/reviewers/architectural/justification.md) — `"Justification"`

### Remediation Re-dispatch

On Step 2 architectural-reviewer FAIL verdict (i.e., the architectural-review protocol returns a non-passing score after all autonomous resolution cycles), re-dispatch `dso:approach-proposer` with `model: "opus"` on the dispatch payload to generate a revised approach.

Build a `remediation_context` block before dispatching. Pass all artifact file paths by reference (absolute file paths — do not inline file contents). The shape MUST match the schema declared in `${CLAUDE_PLUGIN_ROOT}/agents/approach-proposer.md` under `### Remediation Context (optional)`:

```json
{
  "remediation_context": {
    "reviewer_artifact_paths": [
      "<absolute path to reviewer-findings.json from the failing review cycle>",
      "<absolute path to the original approach proposal artifact>"
    ],
    "findings": [
      {
        "target": "<proposal-N id that triggered the FAIL verdict, or 'n/a' for set-wide concerns>",
        "description": "<verbatim evidence quote from reviewer findings — copy the exact reviewer text that triggered the FAIL verdict>"
      }
    ],
    "target_story_id": "<story id this delta cycle is scoped to>"
  }
}
```

The orchestrator-level dispatch operates in `mode: remediation` — i.e., the presence of `remediation_context` activates `dso:approach-proposer`'s DELTA OUTPUT MODE and produces only the changed proposals rather than a full regeneration. Do NOT add a `mode` field inside the `remediation_context` object itself (the schema does not declare it).

Also pass the following scratch namespacing args so the sub-agent can write its output via the scratch store:

- `SCRATCH_TICKET_ID`: The story ticket id for this implementation-plan run — the sub-agent uses this to namespace the scratch write.
- `SCRATCH_KEY`: The literal string `implementation-plan:step2:arch-review-draft` — the sub-agent uses this as the scratch key when writing its output.

**Sub-agent output contract** (include verbatim in the dispatched prompt):

```xml
<output_contract>
You MUST write your full JSON output (proposals, distinctness_summary, complexity_gate_summary,
generation_notes) to the ticket scratch store before returning, using the SCRATCH_TICKET_ID and
SCRATCH_KEY values provided:

  bash "$PLUGIN_SCRIPTS/ticket-scratch.sh" set "$SCRATCH_TICKET_ID" "$SCRATCH_KEY" "<your-json-output>"

After writing, return ONLY a 3-field receipt JSON — do NOT embed the draft body in your return block:

  {"ticket_id": "<SCRATCH_TICKET_ID>", "key": "<SCRATCH_KEY>", "byte_count": <N>}

where byte_count is the byte length of the JSON payload you wrote.

NEGATIVE EXAMPLE (contract violation — do NOT do this):
  {"ticket_id": "...", "key": "...", "byte_count": 4096, "proposals": [...]}
  ^^^ Extra fields beyond the 3-field envelope are a contract violation and will
      cause RECEIPT_PARSE_ERROR on the orchestrator side.

NEGATIVE EXAMPLE (contract violation — do NOT do this):
  Here are the revised proposals: proposals: [...], distinctness_summary: [...]
  ^^^ Returning the draft body inline (not via scratch) is a contract violation.
      A SCRATCH_MISS on the orchestrator side is NOT a fallback signal to send
      the payload inline — it is a hard error requiring the agent to be re-dispatched.
</output_contract>
```

After receiving the revised proposals, re-enter the Resolution Loop from Dispatch with the new proposal set. If the re-dispatched approach-proposer returns `model_requirement_unmet`, apply the same retry-once rule as in the Proposal Generation section.

Record exactly one `.claude/scripts/dso ticket comment <id> "REMEDIATION_CYCLE:<N> approach-proposer re-dispatched after Step 2 FAIL"` per cycle per SC5 single-comment policy.

**Fallback**: if the review fails after autonomous resolution (`review.max_cycles`, default: 4) and user escalation, revert to existing patterns and note the unresolved concern. If no existing pattern solves the story, halt and consult the user.

**Remediation loop protocol (Step 2 — architectural review re-dispatch)**

When `dso:approach-proposer` returns revised proposals after a Step 2 FAIL verdict, the orchestrator enters a remediation loop governed by the shared protocol. Source `MAX_CYCLES` from `get_max_remediation_cycles()` in `${CLAUDE_PLUGIN_ROOT}/hooks/lib/planning-config.sh` before entering the loop.

**Per-cycle declaration**: At the start of every remediation cycle, emit exactly:
`Current cycle: N of MAX_CYCLES`
where `N` is the 1-based cycle counter and `MAX_CYCLES` is the resolved maximum. This line is required before any findings, deltas, or token emissions within that cycle.

**Oscillation-check hard gate (cycle >= 2)**: On any cycle where `N >= 2`, the orchestrator MUST invoke `/dso:oscillation-check` (Skill) before proceeding with findings analysis. Skipping this gate requires an explicit `OSCILLATION_CHECK_SKIPPED: <reason>` ticket comment capturing the rationale; no other skip reason is permitted.

**Mid-loop success exit**: If the revised proposals pass architectural review on any cycle (before reaching `MAX_CYCLES`), the loop exits immediately and Step 2 proceeds to Step 3. No terminal token is emitted on success.

**HALT-vs-REPLAN exclusivity check**: `REPLAN_ESCALATE` MUST be emitted IFF `cycle_count == MAX_CYCLES AND findings non-empty`. No other condition may emit `REPLAN_ESCALATE`. All other terminal states use a different token — human-input required → `HALT_FOR_USER`; oscillation detected → `OSCILLATION_HALT`; illegal state transition → `PROTOCOL_ERROR`. Emitting `REPLAN_ESCALATE` under any other condition, or emitting it together with `PROTOCOL_ERROR`, is itself a `PROTOCOL_ERROR`.

**Terminal REPLAN_ESCALATE**: On cycle `MAX_CYCLES` with findings still non-empty, emit (per the canonical `${CLAUDE_PLUGIN_ROOT}/docs/contracts/replan-escalate-signal.md` colon-space prefix):
`REPLAN_ESCALATE: planner_supplied EXPLANATION:<explanation text>`
The upstream for implementation-plan Step 2 is `planner_supplied` (per the upstream enum in Section 6 of the protocol doc). Emit this as a standalone ticket comment so the orchestrator's parent session can detect and route the escalation. The literal prefix `REPLAN_ESCALATE: ` (colon followed by a single space) and the `EXPLANATION:` field label are fixed by contract — do not vary them.

**PROTOCOL_ERROR on invariant violation**: Any illegal state transition — emitting `REPLAN_ESCALATE` when `cycle_count < MAX_CYCLES`, emitting multiple terminal tokens, or violating the HALT-vs-REPLAN exclusivity invariant — MUST emit `PROTOCOL_ERROR` and halt remediation immediately.

See: `${CLAUDE_PLUGIN_ROOT}/skills/shared/workflows/remediation-loop-protocol.md`

**Parse Output (Receipt-Only Contract — Step 2 cycle recorder)**

After the sub-agent returns, validate its receipt via `receipt-parse.sh` before reading any payload:

```bash
# Prompt Alignment Finding 1: receipt validation co-located with the read site
PARSE_RESULT=$(printf '%s' "<sub-agent-return-block>" | \
    bash "$PLUGIN_SCRIPTS/receipt-parse.sh" implementation-plan:step2 dso:approach-proposer)
PARSE_EXIT=$?
if [ "$PARSE_EXIT" -ne 0 ]; then
    # RECEIPT_PARSE_ERROR — halt workflow; structured error already logged to stderr by receipt-parse.sh
    # Inline cleanup: remove any partial scratch written before the failure
    bash "$PLUGIN_SCRIPTS/ticket-scratch.sh" clear "$SCRATCH_TICKET_ID" "implementation-plan:step2:arch-review-draft" 2>/dev/null || true
    echo "HALT: approach-proposer returned a malformed receipt — see RECEIPT_PARSE_ERROR above. Re-dispatch the sub-agent to fix the return contract before continuing." >&2
    exit 1
fi
# Extract ticket_id and key from the validated receipt
SCRATCH_TICKET_ID_OUT=$(echo "$PARSE_RESULT" | awk '{print $1}')
SCRATCH_KEY_OUT=$(echo "$PARSE_RESULT" | awk '{print $2}')

# Retrieve the payload from scratch
SCRATCH_RESULT=$(bash "$PLUGIN_SCRIPTS/ticket-scratch.sh" get "$SCRATCH_TICKET_ID_OUT" "$SCRATCH_KEY_OUT")
SCRATCH_STATUS=$(echo "$SCRATCH_RESULT" | python3 -c "import json,sys; print(json.load(sys.stdin)['status'])")

# Prompt Alignment Finding 1: SCRATCH_MISS guard co-located with the get call
if [ "$SCRATCH_STATUS" != "hit" ]; then
    # SCRATCH_MISS — hard error; do NOT fall back to accepting inline payload  # precondition-emit-ok: negation, no degradation event
    # Inline cleanup: no payload to remove, but clear any stale key
    bash "$PLUGIN_SCRIPTS/ticket-scratch.sh" clear "$SCRATCH_TICKET_ID_OUT" "$SCRATCH_KEY_OUT" 2>/dev/null || true
    echo "HALT: scratch key '$SCRATCH_KEY_OUT' not found for ticket '$SCRATCH_TICKET_ID_OUT' (status=$SCRATCH_STATUS). A SCRATCH_MISS is not a signal to accept an inline payload — re-dispatch the sub-agent." >&2
    exit 1
fi
ARCH_REVIEW_JSON=$(echo "$SCRATCH_RESULT" | python3 -c "import json,sys; print(json.load(sys.stdin)['value'])")
# Clear scratch after successful read
bash "$PLUGIN_SCRIPTS/ticket-scratch.sh" clear "$SCRATCH_TICKET_ID_OUT" "$SCRATCH_KEY_OUT" 2>/dev/null || true
```

After retrieving the arch-review payload, record the cycle artifact via the recorder:

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/append_review_cycle.py \
  --artifact <path-to-architectural-review-artifact-for-step2> \
  --n <cycle-number> \
  --draft-hash <sha256 of DELTA OUTPUT block> \
  --findings-count <int> \
  --verdict <pass|fail|escalate>
```

After the append, emit exactly ONE ticket comment per cycle:

```bash
.claude/scripts/dso ticket comment <ticket-id> "Remediation cycle <n> recorded; arch-review draft stored under implementation-plan:step2:arch-review-draft"
```

**Single-comment policy (SC5)**: The ticket comment references the scratch key and does NOT duplicate the cycle body — cycle entries live in the scratch payload, not in ticket comments. One comment per cycle; no further detail in the comment body.

---

## Step 3: Atomic Task Drafting (via `dso:task-decomposer` opus sub-agent)

Draft tasks that **collectively fulfill all done definitions** of the User Story. If a new pattern was approved in Step 2, include consistency tasks. Task drafting is delegated to the `dso:task-decomposer` sub-agent; the orchestrator collects inputs, dispatches, validates output, then proceeds to Step 4 with the resulting draft set.

### Dispatch

Dispatch via `subagent_type: "dso:task-decomposer"` with `model: "opus"` passed explicitly.

**Two distinct fallback concepts — do not conflate:**

- **Dispatch-target fallback** (this paragraph): if the named `subagent_type: "dso:task-decomposer"` is unregistered ("Unknown agent type" or similar dispatch error), re-dispatch with `subagent_type: "general-purpose"` + `model: "opus"` and `agents/task-decomposer.md` content read inline as the prompt. **The agent still does the work** — only the dispatch transport changes. This is NOT inline drafting.
- **Inline-drafting fallback** (forbidden, see Validation below): the orchestrator itself drafts tasks without the agent. This is the bug class the dispatch was introduced to eliminate and is explicitly prohibited regardless of what dispatch path was taken.

Pass the following as task arguments:

- `{story-id}`, `{story-title}`, `{story-description}` from the story ticket
- `{story-done-definitions}` — the story's DDs with stable identifiers (`dd-1`, `dd-2`, ...). Parse from the story description's `## Done Definitions` section.
- `{selected-approach}` — the proposal selected by `dso:approach-decision-maker` (full proposal: title, description, files, pros, cons, risk)
- `{file-impact-table}` — the orchestrator's enumeration from "File Impact Enumeration" below (every file × action × test-classification)
- `{project-commands}` — `commands.test_unit`, `commands.lint`, `commands.format_check` from `dso-config.conf` (verbatim)
- `{testing-mode}` — the story's testing-mode classification (RED / GREEN / UPDATE; default RED)
- `{migration-marker}` — the migration-class marker emitted by Step 1 (Migration-Class Detection). Source it by scanning the story ticket for the **LAST** `MIGRATION_CLASS:` comment (last-wins idiom) and passing the verbatim single-line JSON payload that follows the `MIGRATION_CLASS:` prefix. If no `MIGRATION_CLASS:` comment is present (older stories planned before this step existed), pass an empty block — the task-decomposer treats an absent/unparseable marker as an inert no-op (the absent-marker path emits no migration pair, preserving backward compatibility). The agent parses migration-class from THIS passed-in arg; it never fetches it from the ticket (the agent has no ticket access).
- `{feature-flags-marker}` — the feature-flag approval marker emitted by Step 1 (Feature-Flag Approval Detection). Source it by scanning the story ticket for the **LAST** `FEATURE_FLAGS:` comment (last-wins idiom) and passing the verbatim single-line JSON payload that follows the `FEATURE_FLAGS:` prefix. If no `FEATURE_FLAGS:` comment is present (older stories planned before this step existed), pass an empty block — the task-decomposer treats an absent/unparseable marker as an inert no-op (backward-compatible default). The agent reads `feature-flags` from THIS passed-in arg; it never performs the two-hop story→parent lookup itself (the agent has no tracker access; the lookup is done by the orchestrator in Step 1 via `resolve-feature-flag-approval.sh`).

The agent returns a JSON object with `dd_partition_map`, `task_drafts`, and `decomposition_notes`. Validate in this order — do NOT reorder:

<!-- VALIDATION-ORDER: model_requirement_unmet → domain-error escalation → schema validation. -->
<!-- VALIDATION-STEP-1: model_requirement_unmet -->
<!-- EMIT-PRECONDITIONS: gate_name=implementation_plan_task_decomposer_model_retry degradation_type=inferred_decision -->
1. **Model-requirement check (first)**: if the response carries `"error": "model_requirement_unmet"`, log `"Task decomposer model requirement unmet — re-dispatching with explicit model: opus"` and retry once via the fallback path. If the retry also returns `model_requirement_unmet`, HALT with diagnostic: `"Task decomposition requires opus; configure the environment to allow opus dispatch and re-run /dso:implementation-plan."` Do NOT fall back to inline task drafting.
<!-- VALIDATION-STEP-1.5: domain-error-escalation -->
2. **Domain-error escalation (second)**: if the response carries `"error": "decomposition_blocked"` (documented in `agents/task-decomposer.md`), this is a legitimate signal that the inputs are insufficient or contradictory — NOT a malformed response. Surface the agent's `decomposition_notes` to the user and HALT with diagnostic: `"Task decomposer: decomposition_blocked — <decomposition_notes joined>. Re-supply the missing input (selected approach, file impact table, or story DDs) and re-run /dso:implementation-plan."` Do NOT proceed to schema validation; empty `task_drafts` / `dd_partition_map` arrays under this error code are the intended shape.
<!-- VALIDATION-STEP-2: schema-validation -->
3. **Schema validation (third)**: with both error-envelope branches ruled out, validate that `dd_partition_map` covers every story DD (union of all `owning_task_ids` is non-empty for every DD, and there are no duplicate ownerships unless explicitly sub-partitioned in `decomposition_notes`); `task_drafts` is a non-empty array with the required fields per the agent's output spec; **every task's `description` field begins with a literal `## Story DD Coverage` section header (verbatim — the completeness reviewer's `dd_collective_ac_coverage` audit greps for this exact heading; a missing header silently invalidates the audit)**; every task's first three `acceptance_criteria` lines are the Universal Criteria using the project commands verbatim; every task whose `testing_mode` is `RED` has a `tdd_test_spec` Given/When/Then sentence. If schema validation fails, HALT with diagnostic: `"Task decomposer returned malformed output; cannot proceed without verified drafts."` Do NOT fall back to inline drafting — inline drafting is the failure mode this dispatch exists to prevent.  <!-- precondition-emit-ok: negation — HALT, no degradation event -->
<!-- EMIT-PRECONDITIONS landmark above (line 445) also covers this section per the within-10-lines rule. -->


**Why halt (not skip-and-continue) here, while Step 6 Gap Analysis falls through on failure?** Task decomposition is a **correctness gate**: every subsequent step (Plan Review, Task Creation, Gap Analysis) consumes the draft set, and inline orchestrator drafting (the only available fallback) is the bug class this dispatch eliminates. Step 6 Gap Analysis is a **quality gate** with a defined skip path — losing it degrades coverage but does not break downstream steps.

The sub-sections below **mirror the rules embedded in `agents/task-decomposer.md`**. They are kept here for human reference and for the orchestrator's input-construction step (file impact enumeration in particular). The agent does NOT load them at dispatch time — it carries its own embedded protocol. When updating gate criteria, AC-library instructions, or sequencing rules below, you MUST update `agents/task-decomposer.md` in the same change to keep the two in sync. The orchestrator itself does not draft tasks inline; it only enumerates the file impact table (which the agent consumes), validates the agent's output, and proceeds to Step 4.

### Directives

* **TDD First** — every task must specify a concrete failing test to write first.
* **3-Gate Granularity** — every task must pass all three gates conjunctively. Gate 3 only mandates splitting when the split would not violate Gate 1 or Gate 2.
    * **Gate 1 — Testable Behavior**: the task must produce testable behavior. Grepping a source file to verify code exists is not a valid test. A valid test executes the code and asserts on output, exit code, or side effects.
    * **Gate 2 — Codebase Green**: after committing only this task, all tests pass and the system is deployable. Tasks must never require being committed together. A task that deploys an inert feature (a guard reading files no one writes yet) is acceptable — inert is not broken.
    * **Gate 3 — Maximum Granularity**: it must not be possible to split into smaller tasks each meeting Gate 1 and Gate 2. If two changes within a task each produce independently verifiable behavior and each leaves the codebase green on its own, they must be separate tasks. Bundling is acceptable only when splitting would violate Gate 1 (neither half produces testable behavior alone) or Gate 2 (intermediate broken state — e.g., a rename across import sites).
* **Acceptance Criteria** — every task must include AC passed via `-d/--description` at creation time, composed from `${CLAUDE_PLUGIN_ROOT}/docs/ACCEPTANCE-CRITERIA-LIBRARY.md`. Read the library once at the start of Step 3. For each task:
  1. Start with Universal Criteria (always included)
  2. Select applicable category blocks based on task type
  3. Fill in parameterized slots ({path}, {ClassName}, {N}, etc.)
  4. Add task-specific criteria not covered by templates
  5. Every criterion must include a `Verify:` command that returns exit 0 on pass.
* **Sequential Order**:
    1. Data Model Updates — backward compatible (nullable fields, defaults).
    2. API/Service Updates — backward compatible (versioning or optional parameters).
    3. UI/Frontend Updates — consume the new API/version.
    4. Cleanup — remove legacy fields, deprecated API versions, or bridge code.

### File Impact Enumeration

Before drafting tasks, enumerate every file the story affects. Produces an auditable **file impact table** mapping each source file to its change action, associated tests, and test classification. The table drives task type selection in TDD Task Structure.

1. **List affected source files** via Glob/Grep, tracing from entry points through all layers.

   **Prefer `sg` (ast-grep) for cross-file dependency discovery** — syntax-aware. Guard:

   ```bash
   if command -v sg >/dev/null 2>&1; then
       sg --pattern 'import $MODULE' --lang python .
       sg --pattern 'from $MODULE import $_' --lang python .
   else
       grep -r 'import <module>' .
   fi
   ```

2. **Find associated tests** for each source file:
   - **Fuzzy match** (preferred):
     ```bash
     source ${CLAUDE_PLUGIN_ROOT}/hooks/lib/fuzzy-match.sh
     fuzzy_find_associated_tests <src_file> <repo_root>
     ```
   - **`.test-index` lookup**: format `source/path.ext: test/path1.ext, test/path2.ext`.
   - Union both result sets.

3. **Classify each test** by what the story does to the source:

   **Non-executable instruction files are always `still-valid`** — before applying the table, identify any file that is an LLM instruction document: SKILL.md, agent definitions (files in `agents/`), prompt files (files in `prompts/`), workflow docs (`.md` prose files in `docs/` — excludes schemas, specs, configs, or any executable artifact stored under `docs/`), hook guidance, CLAUDE.md, or other `.md` prose files with no executable code. Per behavioral-testing-standard Rule 5, these files cannot be deterministically tested for behavioral correctness. Always classify them as `still-valid` regardless of the change action — `create`, `modify`, or `remove`. Do NOT produce a RED or UPDATE test task for instruction file changes. If the Doc-Only Skip Gate (above) should have caught a purely-instruction story, apply it retroactively here.

   | Source change action | Test classification |
   |---------------------|---------------------|
   | `create` (new source file) | `needs-creation` — write a new test file |
   | `modify` (behavior change) | `needs-modification` — update existing test(s) |
   | `remove` (source deleted) | `needs-removal` — remove or prune tests |
   | `modify` (no behavior change, e.g., refactor) | `still-valid` — existing tests remain correct |
   | any action on non-executable instruction file | `still-valid` — no test task ever needed |

4. **Build the file impact table**:

   | Source file | Action | Associated tests | Test classification |
   |-------------|--------|-----------------|---------------------|
   | `src/foo.py` | modify | `tests/test_foo.py` | needs-modification |
   | `src/bar.py` | create | *(none yet)* | needs-creation |
   | `src/legacy.py` | remove | `tests/test_legacy.py` | needs-removal |
   | `src/util.py` | modify | `tests/test_util.py` | still-valid |

`still-valid` requires no test task. `needs-modification` → modify-existing-test RED task. `needs-removal` → remove-test task. `needs-creation` → create-test RED task.

### Consumer Detection Pass

For every file in the impact table whose action is `modify` or `remove`, run a downstream consumer detection pass to identify callers/callsites outside the immediate task scope.

**Prefer `sg` over text grep** — syntax-aware. Guard:

```bash
if command -v sg >/dev/null 2>&1; then
    sg --pattern '$FUNC($$$)' --lang python . | grep -F '<symbol_name>'
    sg --pattern '<symbol_name>($$$)' --lang python .
else
    grep -rn '<symbol_name>(' .
fi
```

When external consumers are found, document them in the task's File Impact section with one disposition:

- **Update** — change the external callsite in this task; add the consumer file with action `modify` and pull its tests in.
- **Accept the breaking change** — record the rationale and link the consumer's owner story or follow-on ticket.

A modify/remove task with un-triaged external consumers is incomplete and must be revised before it leaves Step 3.

### Testing Mode Classification

Each task carries an explicit `testing_mode` field — RED, GREEN, or UPDATE — derived from the file impact table. The classification describes what the code *does* to observable behavior, not what text it adds or removes.

The mode applies to the **source file task** (not the test task). A test task for a RED source file is a "RED test task" but the implementation task for that same file also carries `testing_mode: RED`.

| Source condition | testing_mode | Meaning |
|----------------------|-------------|---------|
| Source action = `create`, classification = `needs-creation` | **RED** | New behavioral content; must have a preceding RED test task |
| Source action = `modify`, behavior changes, `needs-modification` | **UPDATE** | Existing file with observable behavior change; existing tests must be updated to assert new behavior before implementation runs |
| Source action = `modif

[…] pełny plik w paczce .zip
Files in this package (12)
  • SKILL.md 111 KB
  • docs/review-criteria.md 6 KB
  • docs/reviewers/architectural/best-practices.md 5 KB
  • docs/reviewers/architectural/justification.md 4 KB
  • docs/reviewers/architectural/project-alignment.md 5 KB
  • docs/reviewers/plan/completeness.md 8 KB
  • docs/reviewers/plan/dependencies.md 3 KB
  • docs/reviewers/plan/safety.md 4 KB
  • docs/reviewers/plan/task-design.md 7 KB
  • docs/reviewers/plan/tdd.md 12 KB
  • prompts/gap-analysis.md 8 KB
  • prompts/proposal-schema.md 6 KB

Versions

RepositoryScoreLicence
This page · navapbc/digital-service-orchestra91Apache-2.0
langri-sha/skills100MIT
Mozurok/fhorja.dev100MIT
hugrverk/riggverk90MIT
Gattic/GatPi-Harness90MIT
itsofficialadi/boundiff90MIT
nasac0x/code-orchestrator90MIT
Neeeophytee/finding-unknowns-skills90MIT
EmbedBuild/specbox-engine86Apache-2.0

Database snapshot: 13.11.2026 · package version 823176bb1fec (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100planning-and-task-breakdown
    Decompose specs into small, ordered tasks with acceptance criteria and verification steps. Use when you have requirements and need an implementable plan or task list. Triggers: "break down tasks", "implementation plan", "task breakdown", "plan mode".
  • 95Tasks
    Generate granular task breakdowns from implementation plans with TDD ordering, parallel task marking, and sequential numbering. USE WHEN tasks, task breakdown, todo list, implementation tasks, work items, actionable steps, generate tasks.
  • 100breakdown-ticket
    Break one epic — or a story or task too big for one reviewable PR — into PR-sized child tickets. Reads the ticket's requirements, its feature analysis, its tech design and a plan's oversize split seams, proposes every child (title, type story/task/bug, acceptance criteria,…
  • 100milestone-breakdown
    Break down the next open milestone from ROADMAP.md into small, actionable, independently testable tasks in PLAN.md. Trigger whenever the user says "break down", "plan tasks", "create tasks", "task breakdown", "decompose milestone", "phase 2", "let's break this down", references…
  • 90adev:plan
    Decompose reviewed Live Specs into ordered implementation tasks with TDD expectations and context routing. Use to break specs into actionable tasks. In Codex, invoke with $adev:plan
  • 100plan-epic
    Breaks an epic or large feature into tickets, each with its own branch name, from PRDs, TDDs, designs, and other reference material (@ paths), codebase research, or a prompt. The breakdown is checked automatically for gaps and breaking changes before the user reviews it, then…

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.