# Do

> Execute tasks from an epic doc. Picks the next unblocked task, reads context and acceptance criteria, does the work, verifies criteria, updates the epic doc, and loops until done — updating coverage matrices and generating a retro on completion. Owns the Change Type Decision procedure, so a criterion that contradicts reality is resolved in-loop rather than silently edited — and under an autonomous run, without pausing for input. Triggers on "/cpm:do".

- Skill: `ninthspace/do` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ninthspace/do`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ninthspace/do/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: ninthspace (https://skillmd.com/u/ninthspace)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/ninthspace/do

---


# Task Execution

Work through stories and tasks defined in epic documents produced by `cpm:epics`. Hydrates one story at a time into Claude Code's task system, then for each task: read context, do the work, verify acceptance criteria, update the epic doc, and move on to the next.

## Input

Resolve the epic doc first, then select a task.

### Epic Doc

1. If `$ARGUMENTS` is a file path (e.g. `docs/epics/01-epic-setup.md`), use that as the epic doc.
2. If no path given, run smart discovery:
   a. **Glob** `docs/epics/*-epic-*.md` to find all epic files.
   b. If no epic files found, proceed without one (tasks still work via their descriptions).
   c. If only one epic file exists, use it — no need to ask.
   d. If multiple epic files exist, use Grep to search for `**Status**:` across the matched files, then filter to epics that are not `Complete`/`Done` (`Done` reads as a synonym for `Complete`) and not retired (`Superseded` / `Withdrawn` — terminal, user-set statuses for work no longer needed; there is nothing to do on a retired epic). Use Grep and Read tools directly (Bash loops with shell variables lose context). If only one has remaining work, auto-select it. If multiple have remaining work, present the choices to the user with AskUserQuestion — show each epic's name and status. The choice is the start of the run, not the end of a turn: startup follows it immediately.
   e. If all epics are `Complete` or retired (`Superseded` / `Withdrawn`), tell the user there's nothing to do.
3. If no epic docs exist, proceed without one (tasks still work via their descriptions).

The epic doc, once resolved, applies to the entire work loop. Parse it once and reuse the result across all tasks.

### Task Selection

1. If `$ARGUMENTS` includes a task ID (e.g. `3` or `#3`), start with that task.
2. Otherwise, run the **Story Hydration** gating check (see below). This ensures Claude Code tasks exist for the current story before selection.
3. Call `TaskList` and pick the lowest-ID task that is `pending` and has no unresolved `blockedBy`.
4. If no pending unblocked tasks exist after hydration, the work is done.

## Stale-Progress Check

Follow the shared **Stale-Progress Check** procedure (from the CPM Shared Skill Conventions loaded at session start).

## Retro Check

Before the work loop begins, run the **retro consumption gate** — a hard gate (defined here, local to `cpm:do`) that forces each relevant prior-epic lesson to be consciously applied, deferred, or judged not relevant before any work starts. This is `cpm:do`'s stronger form of retro consumption; the shared **Retro Awareness** procedure remains advisory for the other skills, and this skill overrides its Yes/No prompt with the disposition gate below.

1. **Find the lessons.** Glob `docs/retros/[0-9]*-retro-*.md`. If none exist or the directory is absent, skip the gate silently and start the loop. Otherwise select relevant observations **across all retros** per the shared **Retro Awareness** procedure (step 2): judge each observation by source/domain match against the epic being worked and by category, with recency only as a tiebreaker, capped to a handful — do **not** consume only the highest-prefix file. A relevant lesson from an older retro (e.g. a testing gap in the same feature area) is not shadowed by a more recent, unrelated one. **Make the selection visible**: when you present the gate (step 3), name the source retro for each observation — its filename, `**Source**:`, and `**Date**:` — so *which* lessons are consumed (and from where) is explicit and a mis-selection stays visible.
2. **Select relevant observations.** Filter the selected cross-retro observations (from step 1) to the categories this skill acts on (see **Retro incorporation** below); observations outside those categories are not gated.
3. **Gate on disposition, not acknowledgement.** Present each relevant observation **verbatim, with its category** (rendered in the message body per the **Gate Presentation** convention), and require a disposition for *each one* via `AskUserQuestion`, rather than a single blanket confirmation. The three dispositions are all **per-run**: each records a breadcrumb on this epic (step 4) and **none** mutate the source retro, so a lesson set aside here is re-judged on the next run. The dispositions answered, the run opens the work loop in the same turn rather than reporting them back.
   - **Applied** — state *how* it changes this run (which task exploration or criterion it informs).
   - **Deferred** — give a one-line *reason* it isn't being applied now.
   - **Not relevant here** — the lesson is still valid but doesn't bear on *this* work; say why. This is a **local** judgement about fit, not a verdict on the lesson's truth — it is per-run and reversible, leaving the lesson in place at its source.
   A lone "acknowledge" or "continue" response does **not** satisfy the gate — each observation gets its own disposition.

   **Retiring a spent lesson in-cycle (deliberate, rare).** If, while disposing of an observation, you can *guarantee* its usefulness has permanently passed — e.g. you just deleted the module it warned about — you may retire it durably without leaving the flow. This is neither a fourth peer option nor a default: it is a deliberate, separately-confirmed action, offered only on explicit request for the observation in hand. Confirm via a dedicated `AskUserQuestion` ("Retire this lesson at its source? This removes it from **all** future runs.") and require a typed **reason**. Only on that confirmation does it write the durable retirement marker (step 5). Durable retirement's normal home is the deliberate `/cpm:retro retire` review pass — reserve this in-cycle path for the rare can't-miss case. Record it as the **Obsolete** disposition in steps 4–5.
4. **Record the trace.** For each disposition, append a breadcrumb to the epic doc being worked (the epic resolved in Input), in its top-level metadata block (below the epic's `**Blocked by**` field):
   `**Retro applied**: {nn} · {category} · {disposition} — {note}`
   where `{nn}` is the source retro's number, `{category}` the observation category, `{disposition}` one of Applied / Deferred / Not relevant here / Obsolete (Obsolete only via the deliberately-confirmed in-cycle retire above), and `{note}` the how/reason/why from step 3. This makes how each prior lesson was handled auditable by the next reader, `cpm:status`, and the next retro.
5. **Retire obsolete lessons at the source (durable).** For each observation given the **Obsolete** disposition via the deliberately-confirmed in-cycle retire (step 3), append a retirement marker to that observation's bullet in its **source retro file** per the shared **Retro Retirement** convention — `**Retired {YYYY-MM-DD}**: {reason}`, using the typed *reason* from the confirmation. The bullet stays in place; the marker makes the shared **Retro Awareness** selection skip it in all future runs. The step-4 breadcrumb records the decision for *this* run; this source marker makes it durable across runs. Only the gated **Obsolete** retire writes to the source retro.

**Autonomous mode (e.g. `cpm:ralph`)**: When the run is autonomous — no human is present to choose dispositions — the gate **does not block**. Rather than deferring everything, it **branches by observation category** — and this split is the single source of truth, so `cpm:ralph` references it rather than maintaining its own list:

- **Safe categories — auto-apply.** **Codebase discoveries** and **Patterns worth reusing** are low-ambiguity and additive, so apply them autonomously and carry each into the run as active context (see *What "apply" means autonomously* below for the exact breadcrumb and semantics).
- **Judgement-heavy categories — defer.** **Scope surprises**, **Criteria gaps**, **Complexity underestimates**, and **Testing gaps** each imply a re-planning or scope call that belongs to a human, so they are deferred rather than auto-applied — defer each with `**Retro applied**: {nn} · {category} · deferred (autonomous run, unreviewed)` (the step 4 format).
- **Smooth deliveries** is informational — nothing to apply or defer.

Surface the full list of **both** the auto-applied and the deferred-unreviewed observations in the batch/run summary (Step 8 item 4) so a human can review them after the loop. Degrade to these logged dispositions rather than waiting for input.

**What "apply" means autonomously**: applying a safe-category lesson means carrying its observation into the work loop as an explicit constraint or piece of context for the tasks ahead — the same lens an *Applied* disposition gets in an interactive run. It does **not** trigger autonomous re-planning, story re-scoping, or any edit to the source spec or to any other artefact upstream of the open epic; a lesson that seems to demand that belongs to a judgement-heavy category and is deferred, not applied. Amending the open epic doc is reachable only through the autonomous branch of **Surface change moments explicitly** (in Guidelines), which requires a citable contradiction and leaves a `**Pivot deferred**` breadcrumb for every artefact it could not reach — an applied retro lesson is not a licence to take that path, and never reaches the spec by either route. Record each auto-application with `**Retro applied**: {nn} · {category} · applied (autonomous, safe-category) — {what it did}` (the step-4 format, with an `applied (autonomous, safe-category)` disposition in place of the interactive Applied/Deferred/Not relevant here/Obsolete set). Retirement stays a deliberate human action, so the **Obsolete** disposition and its source-retro marker are unavailable to an autonomous run.

Once every relevant observation has a disposition, **start the loop**, carrying each *Applied* disposition into the run as active context — a lens on every task per the incorporation guidance below, not a one-off.

**Retro incorporation** (this skill — which categories matter and what changes):
- **Complexity underestimates**: Inform per-task exploration (Step 1, Load Context) — when exploring a task, look for the complexity drivers past tasks underestimated (concurrency, edge cases, data shapes).
- **Codebase discoveries**: Inform per-task exploration — surfaced patterns and limitations are checked first before re-discovering them.
- **Testing gaps**: Inform Step 4 (Verify Acceptance Criteria) — past untestable criteria patterns are flagged early so the user can refine criteria before implementation, not after.
- **Patterns worth reusing**: Inform Step 4 implementation — apply the surfaced pattern when its conditions match the current task.

## Library Check

Follow the shared **Library Check** procedure with scope keyword `do`. Deep-read selectively during task execution when a library document's content directly affects the current task — e.g. coding standards before writing code, or architecture docs before structural decisions.

### Template Hint (Startup)

After the Library Check and before task selection, display:

> Output format is fixed (used by downstream skills). Run `/cpm:templates preview do` to see the format.

### Test Runner Discovery (Startup)

After the Template Hint and before story hydration, discover the project's test runner command. This command is used by verification gates to execute tests when acceptance criteria carry automated test tags (`[unit]`, `[integration]`, `[feature]`).

**Discovery priority**:

1. **Library documents**: Check any library documents scoped to `do` (found during Library Check) for testing instructions. Look for explicit test commands, framework references, or testing conventions. If found, use the specified command.
2. **Project config files**: If no library document provides a test command, inspect project configuration files:
   - `composer.json` — check `scripts.test` (e.g. `composer test`, `./vendor/bin/pest`, `./vendor/bin/phpunit`)
   - `package.json` — check `scripts.test` (e.g. `npm test`, `npx jest`)
   - `Makefile` — check for a `test` target (e.g. `make test`)
   - `pyproject.toml` or `pytest.ini` — check for pytest configuration (e.g. `pytest`)
   - `Cargo.toml` — check for Rust project (e.g. `cargo test`)
3. **Ask the user**: If no test command is discoverable from steps 1-2, use AskUserQuestion to ask: "No test runner found automatically. What command runs your tests?" with options for common runners and a freeform option.

**Cache the result**: Store the discovered test command in the progress file (see State Management) as `**Test command**: {command}` or `**Test command**: none` if the user declines. Reuse the cached command for all verification gates in the session.

**Graceful degradation**: If no test runner is discoverable and the user chooses not to provide one, set `**Test command**: none`. Verification gates will fall back to the existing self-assessment approach and note that no test runner was available.

**Skip conditions**: If the epic doc has no acceptance criteria with `[unit]`, `[integration]`, or `[feature]` tags, skip test runner discovery entirely — it won't be needed.

### Framework Detection (Startup)

After Test Runner Discovery and before story hydration, detect the project's framework to enable framework-specific tooling (e.g. refactoring agents).

**Detection**:

1. **Laravel**: Check for an `artisan` file in the project root **and** `composer.json` containing `laravel/framework` in its `require` or `require-dev` dependencies. If both are present, the project is Laravel.
2. **Other frameworks**: No special detection needed at this stage. Additional frameworks can be added here as framework-specific tooling becomes available.

**Cache the result**: Store the detected framework in the progress file as `**Framework**: laravel` or `**Framework**: none`. Reuse the cached value for the entire session.

**Permission check**: If the framework is `laravel`, check whether the `laravel-simplifier:laravel-simplifier` agent is available by reviewing the session's tool permissions. The story refactoring pass (Step 5b) invokes this agent via the Task tool, which requires `Task(laravel-simplifier:laravel-simplifier)` in the user's permission allow list. If unsure whether it's pre-authorised, warn the user early: "Laravel detected — the story refactoring pass uses the `laravel-simplifier` agent. If you haven't already, add `Task(laravel-simplifier:laravel-simplifier)` to your permission allow list (in `.claude/settings.json` under `permissions.allow`) to avoid permission prompts that may not surface during the work loop." This is advisory — proceed with startup regardless.

## Story Hydration

When `cpm:do` needs work and no pending unblocked Claude Code tasks exist, it hydrates the next story from the epic doc into Claude Code's task system. This is the bridge between planning artifacts (epic docs) and execution state (Claude Code tasks).

### When to Hydrate

Hydration fires as a **gating check before task selection**:

1. Call `TaskList`. If there are pending unblocked tasks, skip hydration — proceed directly to task selection.
2. If no pending unblocked tasks exist, hydrate the next story (see below).
3. If hydration finds no unblocked stories remaining, the work loop is done.

This single mechanism covers both the initial work loop entry and story-to-story transitions.

### How to Hydrate

When hydration is triggered:

1. **Read the epic doc** using the Read tool. Parse all `##` story headings and their metadata fields (`**Story**:`, `**Status**:`, `**Blocked by**:`).

2. **Identify the next unblocked story**:
   - A story is unblocked when its `**Blocked by**` field is either `—` (no dependencies) or all referenced stories have `**Status**: Complete` (or `Done`, which reads the same). A referenced `Superseded` / `Withdrawn` epic does not satisfy the dependency — that work will not be done — so the story stays blocked.
   - Among unblocked stories, pick the lowest-numbered one with `**Status**: Pending`.
   - If no unblocked pending stories remain, the epic is done — proceed to batch summary.

3. **Check for existing tasks** (idempotency): Call `TaskList` and scan task descriptions for entries that reference the same epic doc path and story number (e.g. `Epic doc: {path}` and `Story: {N}`) **and carry a `Task:` or `Type: verification` line**. If matching tasks already exist — from a previous partial run or interrupted session — skip creation and use the existing tasks. Proceed directly to step 6 (task selection). A `Type: remaining` placeholder (step 5b) names a story without holding its work, so it never satisfies this check; hydrating a story whose placeholder is present is the ordinary case, not the resumed one.

4. **Create Claude Code tasks** for the selected story:
   - For each `###` task heading within the story, call TaskCreate:
     ```
     TaskCreate:
       subject: "{Task title from ### heading}"
       description: "{**Description** field if present, otherwise task title}\n\nEpic doc: {epic doc path}\nStory: {N}\nTask: {N.M}"
       activeForm: "{Present continuous form of the task title}"
     ```
   - After all tasks, create the story's verification gate:
     ```
     TaskCreate:
       subject: "Verify: {Story title}"
       description: "Verify acceptance criteria for Story {N}: {Story title}\n\n{List the acceptance criteria}\n\nEpic doc: {epic doc path}\nStory: {N}\nType: verification"
       activeForm: "Verifying: {Story title}"
     ```

5. **Set intra-story dependencies**: Call TaskUpdate on the verification gate task with `addBlockedBy` set to all the task IDs just created. This ensures the gate only fires after all implementation work is complete.

5b. **Show the stories still to come**: for every pending story after this one in the epic doc — blocked or not — create one placeholder task, unless it already has one:
   ```
   TaskCreate:
     subject: "Story {N}: {Story title}"
     description: "Not yet hydrated.\n\nEpic doc: {epic doc path}\nStory: {N}\nType: remaining"
     activeForm: "Working Story {N}: {Story title}"
   ```
   Then TaskUpdate each with `addBlockedBy` set to this story's verification gate. **Blocked is what they are for**, not presentation: it keeps them out of the pending-unblocked set that step 1's gating check and step 6's task selection both read, so neither can pick one up as work. Delete a story's placeholder when hydrating it, before step 4 creates the real tasks.

   The task list is the only signal of remaining work a reader — or a run resuming after a compaction — sees without opening the epic doc. Without the placeholders it empties as each story finishes, which is the same picture it shows when the epic is genuinely done.

6. **Proceed to task selection** — the newly created tasks are now available for the work loop to pick up.

### Format Tolerance

The hydration parser tolerates both:
- **Old-format** epic docs (with `**Task ID**: —` fields present) — ignore these fields
- **New-format** epic docs (without `**Task ID**` fields) — the default going forward

Parse stories and tasks by their heading structure (`##` for stories, `###` for tasks within stories) and metadata fields (`**Story**:`, `**Task**:`, `**Status**:`, `**Blocked by**:`, `**Description**:`). Ignore any unrecognised fields.

## Per-Task Workflow

**State tracking**: Create the progress file before the first task and update it after each task completes. See State Management below for the format and rationale. Delete the file once the work loop finishes.

### Termination

- **Success**: No pending unblocked tasks remain after the Story Hydration check in Step 7 — proceed to Batch Summary (Step 8).
- **Blocker**: A task requires an external dependency that cannot be resolved in this session (missing API access, awaiting another team's output, infrastructure not available). Use AskUserQuestion to confirm the blocker with the user, then skip the task and continue to the next unblocked task. If no unblocked tasks remain, proceed to Batch Summary with the blocker noted.
- **Ambiguity**: Acceptance criteria cannot be evaluated — the requirement is unclear, contradictory, or untestable with available tools. Use AskUserQuestion to surface the specific criterion text and ask the user to clarify. If resolved, continue. If unresolvable, treat as a blocker — skip the task and continue to the next.

For each task, follow these steps in order.

### 1. Load Context

- Call `TaskGet` to read the full task description. The description includes the `Epic doc:`, `Story:`, and `Task:` fields set during hydration.
- If an epic doc was resolved during Input, read it with the Read tool. Use the `Story:` and `Task:` fields from the task description to locate the matching entry — search for the `**Story**: {N}` or `**Task**: {N.M}` field that matches. For verification gate tasks, match the `##` story heading. For implementation tasks, match the `###` task heading. Note the parent story's acceptance criteria — for `###` tasks, look up to the nearest `##` story heading above the matched task. If the matched `###` task has a `**Description**:` field, read it — this scopes the task within its parent story and clarifies which acceptance criteria it addresses.
- **Coverage matrix**: Check for a companion coverage matrix alongside the epic doc. Derive the coverage path from the epic path by replacing `-epic-` with `-coverage-` in the filename. This works for both legacy flat epics (`docs/epics/15-epic-foo.md` → `docs/epics/15-coverage-foo.md`) and new two-part epics (`docs/epics/28-01-epic-foo.md` → `docs/epics/28-01-coverage-foo.md`) via the same rule, with no shape detection or branching. If the coverage matrix exists, read it — it provides side-by-side verbatim text from the source spec and the story's acceptance criteria. This gives you requirement-level traceability: the spec's exact wording for each requirement this epic covers, so you can verify implementation against the spec's intent, not just the story's paraphrase.
- **Drift detection**: If a coverage matrix was loaded and any of its rows have `✓` in the Verified column, compare the "Story Criterion (verbatim)" text in those verified rows against the corresponding acceptance criteria in the epic doc. If the text differs — indicating the epic doc was modified after verification — flag the mismatch to the user: "Coverage matrix drift detected: Story {N} criterion text has changed since verification. The `✓` marker may be stale." This catches out-of-band edits that bypassed `/cpm:pivot`'s invalidation logic. If no verified rows exist or no coverage matrix is present, skip this check.
- **Determine task type**: Check the task description for `Type: verification`. If present, this is a story verification gate — the work in step 4 will be acceptance criteria checking, not implementation. If absent, this is a normal implementation task.
- **Determine workflow mode**: Scan the parent story's acceptance criteria for the `[tdd]` tag. If any criterion carries `[tdd]`, this story uses TDD workflow mode — record this for use in Step 4. If no `[tdd]` tag is found, the story uses the standard post-implementation workflow.
- **Determine planning mode**: Check whether the parent story's `##` heading contains a `[plan]` tag (e.g. `## Set up OAuth provider integration [plan]`). If `[plan]` is present, this story uses formal plan mode in Step 3 — but the plan covers the whole story and fires **once per story, not once per task**. Also check the progress file's `**Planned stories**:` line: if this story's number already appears there, its plan is approved and Step 3 will skip straight to implementation. If `[plan]` is absent, Step 3 uses inline planning (the default). Record both the tag and the already-planned state for use in Step 3.
- If no epic doc is available, proceed without epic doc integration — the task still gets done.

### 2. Update Status to In Progress

- Call `TaskUpdate` to set the task status to `in_progress`.
- If epic doc integration is active, use the Edit tool to update the matched entry's status. The entry may be a `##` story or a `###` task — locate the correct `**Status**: Pending` field near the matched heading:
  - `old_string`: `**Status**: Pending` (scoped near the matched heading)
  - `new_string`: `**Status**: In Progress`
- **Preserve any note tail.** A status may carry a human note after a delimiter — `**Status**: Pending — waiting on API keys`. Match only the **status token** (`**Status**: Pending`) rather than the whole line, so the ` — waiting on API keys` tail is left untouched (the result becomes `**Status**: In Progress — waiting on API keys`). A note you did not author survives the edit unchanged.

### 3. Plan (when warranted)

Before jumping into implementation, assess whether this task warrants a planning step. The planning approach depends on whether the parent story carries a `[plan]` tag (detected in Step 1).

**Default: Inline planning (no `[plan]` tag)**

For most tasks, plan inline — explore the codebase, output a brief plan as text, and proceed directly to Step 4. No mode switch, no user approval gate, no loop disruption.

For complex, critical, or sensitive tasks: explore the codebase using Read, Glob, and Grep, then output a brief plan covering order of operations, implementation decisions, and risk flags. Then proceed to Step 4.

Skip planning entirely for straightforward tasks — config changes, documentation updates, simple additions to existing patterns.

**Formal plan mode (`[plan]` tag present) — once per story**

The `[plan]` tag lives on the story, so the plan covers the whole story and formal plan mode fires **once per story, not once per task**. Which case applies depends on whether the story has already been planned in this run (the `**Planned stories**:` check from Step 1):

- **Story not yet planned** (its number is absent from `**Planned stories**:` — typically the first task of the story): enter `EnterPlanMode`, explore the codebase, and design the approach **for the entire story** — sequence all of its tasks and call out implementation decisions and risks across them, not just the current task. Get user approval before writing any code, then exit plan mode. Record the story as planned by adding its number to the progress file's `**Planned stories**:` line (written at the next Step 6 Part C). Then proceed to Step 4 for the current task.
- **Story already planned** (its number appears in `**Planned stories**:`): do **not** re-enter plan mode — the approved story plan already governs this task. Proceed directly to Step 4. Add a brief one- or two-line inline note only if this specific task needs a wrinkle the story-level plan did not cover.

Use formal plan mode for stories where the enforcement benefit (physically prevented from writing code while planning) and the approval gate (user reviews before implementation begins) justify the loop interruption. The `[plan]` tag is applied by `cpm:epics` to stories that touch architecture, security, or multi-system integration.

**Note**: Formal plan mode creates an interaction boundary that pauses the task loop. After exiting plan mode and completing the task, continue the task loop — proceed to Step 5, then Step 6, then Step 7 (next task). The plan mode interaction is not a stopping point, and it does not repeat for the remaining tasks in the same story.

**Keep execution plans concise** (both modes). The epic doc already defines *what* to build — stories, tasks, acceptance criteria, and description fields provide the specification. Your plan should only add what the epic doc doesn't say:

1. **Order of operations** — which files to create/modify and in what sequence
2. **Implementation decisions** — choices not already captured in the epic (e.g. which design pattern, which library API to use)
3. **Risk flags** — edge cases or complications you've spotted during exploration

Include only what the epic doc omits: order of operations, implementation decisions, and risk flags. Acceptance criteria, test cases implied by tags, and file content details belong to the implementation phase. A bulleted list of 5-15 lines is the target. Context is finite; every token spent on the plan is a token unavailable for implementation.

### 4. Do the Work

**If this is a verification gate** (`Type: verification` in the task description): Read the parent story's acceptance criteria from the epic doc and verify each criterion against the current state of the codebase. The gate's purpose is assessment only — all implementation happens in prior tasks.

**Test execution in verification gates**: For criteria tagged `[unit]`, `[integration]`, or `[feature]`, run the cached test command and use pass/fail as evidence — a failing test means the criterion is not met. For `[manual]` or untagged criteria, self-assess by inspecting the codebase. For `[target]` criteria, record `target-only — unverified in this environment` and assess nothing; they are never self-assessed and never counted as met. An unrecognised tag is reported rather than self-assessed. If `**Test command**` is `none`, all verification uses self-assessment — except `[target]` and unrecognised tags, which are reported either way, since the missing thing there is the environment rather than the runner.

**Stalled verification**: If a fix-and-recheck cycle fails to reduce the count of unmet criteria after a fix attempt, stop cycling and use AskUserQuestion: "Verification not converging — {N} criteria still unmet." Options: "Continue trying", "Mark unmet criteria as known issues and proceed", "Stop the work loop".

Proceed to step 5 with your assessment.

**If this is an implementation task in TDD mode** (no `Type: verification`, and the story carries `[tdd]` as determined in Step 1): Replace the standard implementation approach with the **red-green-refactor sub-loop**. This is the core TDD discipline — execute all three phases in sequence, each producing a distinct outcome.

> **Intentionally preserved**: The three-phase structure below is a behavioural lock that enforces test-before-implementation ordering — it is not instructional verbosity. Compressing it risks losing the ordering guarantee that is the whole point of TDD mode.

**Phase 1 — Red (write a failing test)**:
1. Derive a test from the parent story's acceptance criteria and the current task's description. Write a test file (or add test cases to an existing test file) that describes the expected behaviour.
2. Construct a **targeted test command** — run only the specific test file just written, not the full test suite. Derive the command from the cached test runner and the test file path (e.g. `pest tests/Feature/MyTest.php`, `jest path/to/test.spec.ts`, `pytest tests/test_my_feature.py`).
3. Run the targeted test. It **must fail** — this confirms the test is actually testing something that doesn't exist yet.
4. If the test **passes unexpectedly**: stop. Something is wrong — either the test isn't testing what you think, or the behaviour already exists. Use AskUserQuestion to present the situation: "Red phase: test passed unexpectedly. This means the expected behaviour may already exist, or the test isn't verifying the right thing." Options: "Investigate and fix the test", "Skip TDD for this task (fall back to standard workflow)", "Stop and discuss".

**Phase 2 — Green (minimum implementation)**:
1. Write the **minimum code** needed to make the failing test pass — only what the failing test requires. Extra features, uncovered edge cases, and refactoring belong to later phases.
2. Run the targeted test command again. It **must pass**.
3. If the test **still fails**: the implementation isn't sufficient. Continue working on the implementation until the test passes. If stuck after a reasonable attempt, use AskUserQuestion: "Green phase: test still failing after implementation." Options: "Continue working on it", "Skip TDD for this task (fall back to standard workflow)", "Stop and investigate".

**Phase 3 — Refactor (clean up within task scope)**:
1. Review the code just written in Phases 1 and 2. Clean up: improve naming, extract methods, remove duplication, improve readability.
2. **Scope constraint**: Limit refactoring to code touched by the current task. Restructuring, file reorganisation, and broader changes belong to the story-level refactoring pass (Step 5b).
3. Run the targeted test command again. It **must still pass** — refactoring must not change behaviour.
4. If the test **fails after refactoring**: you changed behaviour, not just structure. Undo the refactoring change that broke the test and try again.

Proceed to step 5 after the sub-loop completes.

**If this is an implementation task in standard mode** (no `Type: verification`, and the story does **not** carry `[tdd]`): Execute the task as described. This is the existing approach — writing code, creating files, running commands, whatever the task requires. Read the full task description and the parent story's acceptance criteria to understand the broader context. Work until the task is complete.

**ADR awareness** (both modes): Before starting implementation, check if this task touches architectural boundaries. **Glob** `docs/architecture/[0-9]*-adr-*.md` — if ADRs exist and the task involves structural decisions, data models, integration points, or deployment concerns, read the relevant ADRs for context. Let the architectural decisions guide implementation choices. If no ADRs exist, proceed normally.

**Companion-asset awareness** (both modes): A task's acceptance criterion may reference an HTML **companion asset** — a relative path to `docs/{type}/assets/{nn}-{slug}-{label}.html` (a UI mockup or diagram a `spec`/`architect` artifact generated; see the shared **HTML Output** convention). When it does, open the asset and treat it as a **visual design target**: build the implementation to *match what it shows*. Do **not** parse the companion HTML to extract requirements, structure, or values — the Markdown acceptance criteria are the only machine-readable source of truth. The asset informs *appearance*, not *requirements*: read it the way a developer reads a mockup, not the way a parser reads data.

### 5. Verify Acceptance Criteria

Before marking the task complete:

- Re-read the acceptance criteria from the epic doc (or from the task description if no epic doc).
- For each criterion, assess whether it's been met. The assessment method depends on the criterion's tag:
  - **`[unit]`, `[integration]`, `[feature]`**: If a test command is cached (`**Test command**` is not `none`), run it and use the pass/fail result as evidence. A passing test suite satisfies these criteria. A failing test suite means the criterion is not met — report the specific failures.
  - **`[manual]` or no tag**: Self-assess by inspecting the codebase, checking files, or reviewing outputs. This is the existing approach.
  - **`[target]`**: Do **not** self-assess and do **not** count it as met. The check is mechanical but can only run against the real deployment target, so a verdict from this machine is worth nothing — confirming "the production host provides PHP 8.2 or later" from a sandbox that already does is the false pass the tag exists to prevent. Record it as `target-only — unverified in this environment`, name it in the summary, and let the remaining criteria decide whether the task is complete. It does not block completion: a criterion that can never be satisfied where the run happens would stall the loop rather than protect anything.
  - **An unrecognised tag**: report it and stop treating it as a verification instruction — do not fall back to self-assessment. A tag nobody routes is a tag nobody has thought about, and self-assessing it reads in the epic doc as a deliberate verification choice while being the opposite of one. Name the tag and the criterion, and assess nothing.
- If all criteria are met (by test results or self-assessment), proceed to step 6.
- If any criteria are **not** met, flag them to the user. List what's unmet and ask whether to continue working on them or mark the task as Complete anyway. Use AskUserQuestion for this gate. Either answer is acted on and the run carries straight on into 5b, 6 and 7, in the same turn the answer arrived in.

**Coverage matrix proof recording** (verification gates only): When a verification gate passes (all criteria met), update the companion coverage matrix to record proof. Check for the companion coverage matrix alongside the epic doc — derive its path via the `-epic- → -coverage-` substitution rule described in Step 1 (which works for both legacy flat and new two-part epic shapes). If it exists:

1. Read the coverage matrix and identify rows where the "Covered by" column matches the current story (e.g. `Story {N}`).
2. For each matching row, use the Edit tool to replace the empty Verified cell with `✓`. The edit targets the specific row's trailing `| |` (empty Verified cell) and replaces it with `| ✓ |`.
3. Only update rows matching the current story — rows for other stories are left untouched.

If the coverage matrix file doesn't exist, log a note ("No coverage matrix found — skipping proof recording") and continue. Proof recording is additive: a failure here does not stop task execution. If an Edit call fails (e.g. the row text doesn't match the expected pattern), flag the failure to the user via AskUserQuestion with options: "Continue without recording proof for this row" or "Stop and investigate" — every failed write is surfaced. "Continue" resumes the gate's remaining rows and then step 5b, in the same turn; only "Stop and investigate" ends the run.

### 5b. Story Refactoring Pass (every completed story)

When a story's verification gate task runs — the story-completion point — perform a focused refactoring pass on the code the story produced. This pass fires **once per story, in both interactive and autonomous modes** (it lives in `cpm:do`, so `cpm:ralph` inherits it). It is invoked at the verification-gate task because that is where a story finishes, but it is **no longer gated on the verification *result***: a story whose criteria were unmet-but-continued still earns its pass. Running it at the verification-gate task, rather than at individual implementation tasks, is what holds it to exactly one pass per completed story (subject to the preconditions below).

**Preconditions** — run the pass only when **all** of these hold; if any fails, skip the pass and record the skip with its reason in the per-story simplifier-outcome line (Step 6 Part C) rather than silently:

- **The story completed.** It was not marked Blocked, left stuck, or skipped past the autonomous stuck threshold. The partial, possibly-broken code of an unfinished story is left alone.
- **The story touched code.** At least one implementation task created or modified a code file. A story with no implementation tasks (pure documentation or configuration) has nothing to refactor — skip it and log `skipped — no code touched`.
- **A cached test command exists** (`**Test command**` is not `none`). The retest below is the only thing that catches a refactor that changed behaviour; with no test command there is no safety net, so do **not** refactor untested code blind — skip the pass and log `skipped — no test command to verify against`.

**Identify scope**: Review the tasks completed in this story (listed in the progress file's Completed Tasks section). Identify the files that were created or modified during the story's implementation tasks. These files — and only these files — are the refactoring target.

**Run the refactoring pass**:

- **Laravel project with `laravel-simplifier`**: If `**Framework**` in the progress file is `laravel`, use the Task tool with `subagent_type: "laravel-simplifier:laravel-simplifier"` to refactor the touched files. Pass the agent a prompt listing the files modified by this story and instruct it to 

…(truncated)
