Task — single-task TDD execution
Boundaries
/task always executes TDD on the current branch. Want the full pipeline (evals + validate +
plan check, changes left for you to commit)? That's /sdlc. Want just the row written and
nothing run? Edit TASKS.md directly.
Flow
1. Prepare the task record
- TASKS.md: if missing, create it from
templates/TASKS.md.template (or from scratch with sections Active / Pending, Blocked, Done).
- Determine the next task number by reading existing rows (
(P?) <title> — plans/tasks/task-<N>-...) and taking max(N) + 1. Start at 1 if empty.
- Slugify the description (lowercase, hyphen-separated, ≤40 chars).
- Append a row to the
Active / Pending section: - [ ] (P2) <one-line title> — plans/tasks/task-<N>-<slug>.md.
- Initialize a minimal state record (best-effort): write
.claude/pipeline/task-<N>-<slug>/run.json with {schema_version: 1, feature_slug: "task-<N>-<slug>", pipeline: "task", stage: "implement", status: "in_progress", started_at} (shape per
skills/sdlc/templates/state-schema.md). This puts /task runs in the
same state journal as /sdlc. If the write fails
(permissions, read-only volume), log a one-line stderr warning and
continue — state writes never fail a task.
2. Write the task file
Create plans/tasks/task-<N>-<slug>.md:
---
id: task-<N>
status: pending
priority: P2
files: []
---
# <Title>
## Description
<1–3 sentences restating the ask.>
## Steps
- [ ] <first concrete step>
- [ ] <next step>
## Acceptance criteria
- <observable condition for done>
## Files
<expected paths to create/modify; fill in as you go>
## Notes
<open questions, links, context>
3. (Claude only; optional) Mirror to native Tasks
If running under Claude Code, call TaskCreate with the same title. This gives the user a live progress indicator in the UI. Skip silently on other agents.
4. Execute with TDD
Pure-docs / no-testable-surface asks: if the change has nothing to assert
against (docs, comments, config, copy edits), skip steps 1, 2, and 5 — make
the edit directly at step 4 and note "no testable surface" in the report.
Don't manufacture a hollow test, and don't punt the task to another skill.
- Write a failing test that encodes the acceptance criterion. Use the project's configured test runner (
.claude/project.json → test.unit or test.frontend). If the project has no tests, write one in the conventional location (tests/, __tests__/, etc.).
- Run the test and confirm it fails for the expected reason. If it passes, the test is wrong — fix it before continuing.
- Mark the TASKS.md row as in-progress (
[ ] → [~]) and, on Claude, TaskUpdate status: in_progress.
- Implement the change, following existing patterns. Keep the diff minimal.
- Re-run the test until it passes. Do not weaken the test to make it pass.
- Run the wider test suite if one is configured (
/test-check or the project's root test command).
5. Close out
- Update the task file: set
status: completed, mark all step checkboxes [x], fill in Files with the actual paths touched.
- Mark the TASKS.md row done (
[~] → [x]) and move it to the Done section.
- On Claude:
TaskUpdate status: completed.
- Update the state record (best-effort): set
run.json.status = "complete",
stage = "done", and record the files touched (and commit_sha if you
committed). Same never-fail rule as Section 1.
- Report a concise summary: files touched, tests that now pass (or "no testable surface"), anything left open.
Commit only if the user asked for it, or if they have a durable "always commit finished tasks"
instruction. When you do commit, append a blank line and
Co-Authored-By: Claude <noreply@anthropic.com> only if .claude/project.json
coauthor_trailer is true; absent or false means no trailer.
Gotchas
- Don't inflate small tasks. If the ask is one line of code, the task file can be terse. Don't pad acceptance criteria to look thorough.
- Respect
GOTCHAS.md. Before writing code, check the configured gotchas_file (if it exists) for entries that apply to the area you're touching — scoped to the touched area, not the whole file.
- Capture at exit (flywheel). If the run hit a real trap (a test flipped red→green, or something surprised you), run the loop-exit capture protocol in
skills/gotcha/SKILL.md — auto-draft + one-tap confirm; clean runs stay silent. If capture is declined/deferred, append a /gotcha <drafted text> sentinel line — structured + deduped by cmd (multi-slot seam, see docs/SEAM.md): line='{"cmd":"/gotcha <drafted text>","source":"task","confirm":false}'; grep -qF "$line" .claude/.next-action 2>/dev/null || echo "$line" >> .claude/.next-action (never a bare /gotcha). On Codex (as a fallback until its .codex/hooks.json Stop hook is wired+trusted) also print Next: /gotcha … inline so the seam degrades gracefully. If no Stop hook is wired at all, apply the no-hook nudge (docs/SEAM.md SEAM2).
- Don't skip the failing-test step when there IS a testable surface. A passing test that was never red verifies nothing. Only skip the red-test cycle for genuinely untestable asks (pure docs/config/copy) — handle those directly here per the note in Section 4; don't punt them to another skill.
- One task at a time.
/task handles a single bounded item. If the ask implies a batch of tasks, don't fire /task repeatedly — run them through /sdlc <range> (e.g. /sdlc 1-5), which keeps full-pipeline discipline across the set and commits nothing until you review. For an open-ended feature, start with /brainstorm.
1---2name: task3description: Create a bounded, single-purpose task and execute it with TDD. Appends a row to TASKS.md, writes a task file at plans/tasks/task-N-<slug>.md, and runs a write-test → implement → verify loop. Use for small to medium items that are too concrete for /brainstorm and too small to justify the full /sdlc pipeline. Invoke via /task or when the user asks to "just do X" with a clear, bounded ask.4---56# Task — single-task TDD execution78## Boundaries910`/task` always executes TDD on the current branch. Want the full pipeline (evals + validate +11plan check, changes left for you to commit)? That's `/sdlc`. Want just the row written and12nothing run? Edit `TASKS.md` directly.1314## Flow1516### 1. Prepare the task record17181. **TASKS.md**: if missing, create it from `templates/TASKS.md.template` (or from scratch with sections `Active / Pending`, `Blocked`, `Done`).192. **Determine the next task number** by reading existing rows (`(P?) <title> — plans/tasks/task-<N>-...`) and taking `max(N) + 1`. Start at 1 if empty.203. **Slugify the description** (lowercase, hyphen-separated, ≤40 chars).214. **Append a row** to the `Active / Pending` section: `- [ ] (P2) <one-line title> — plans/tasks/task-<N>-<slug>.md`.225. **Initialize a minimal state record** (best-effort): write23 `.claude/pipeline/task-<N>-<slug>/run.json` with `{schema_version: 1,24 feature_slug: "task-<N>-<slug>", pipeline: "task", stage: "implement",25 status: "in_progress", started_at}` (shape per26 `skills/sdlc/templates/state-schema.md`). This puts `/task` runs in the27 same state journal as `/sdlc`. If the write fails28 (permissions, read-only volume), log a one-line stderr warning and29 continue — **state writes never fail a task.**3031### 2. Write the task file3233Create `plans/tasks/task-<N>-<slug>.md`:3435```markdown36---37id: task-<N>38status: pending39priority: P240files: []41---4243# <Title>4445## Description46<1–3 sentences restating the ask.>4748## Steps49- [ ] <first concrete step>50- [ ] <next step>5152## Acceptance criteria53- <observable condition for done>5455## Files56<expected paths to create/modify; fill in as you go>5758## Notes59<open questions, links, context>60```6162### 3. (Claude only; optional) Mirror to native Tasks6364If running under Claude Code, call `TaskCreate` with the same title. This gives the user a live progress indicator in the UI. Skip silently on other agents.6566### 4. Execute with TDD6768**Pure-docs / no-testable-surface asks**: if the change has nothing to assert69against (docs, comments, config, copy edits), skip steps 1, 2, and 5 — make70the edit directly at step 4 and note "no testable surface" in the report.71Don't manufacture a hollow test, and don't punt the task to another skill.72731. **Write a failing test** that encodes the acceptance criterion. Use the project's configured test runner (`.claude/project.json` → `test.unit` or `test.frontend`). If the project has no tests, write one in the conventional location (`tests/`, `__tests__/`, etc.).742. **Run the test** and confirm it fails for the expected reason. If it passes, the test is wrong — fix it before continuing.753. **Mark the TASKS.md row as in-progress** (`[ ]` → `[~]`) and, on Claude, `TaskUpdate status: in_progress`.764. **Implement the change**, following existing patterns. Keep the diff minimal.775. **Re-run the test** until it passes. Do not weaken the test to make it pass.786. **Run the wider test suite** if one is configured (`/test-check` or the project's root test command).7980### 5. Close out81821. **Update the task file**: set `status: completed`, mark all step checkboxes `[x]`, fill in `Files` with the actual paths touched.832. **Mark the TASKS.md row done** (`[~]` → `[x]`) and move it to the `Done` section.843. On Claude: `TaskUpdate status: completed`.854. **Update the state record** (best-effort): set `run.json.status = "complete"`,86 `stage = "done"`, and record the `files` touched (and `commit_sha` if you87 committed). Same never-fail rule as Section 1.885. Report a concise summary: files touched, tests that now pass (or "no testable surface"), anything left open.8990Commit only if the user asked for it, or if they have a durable "always commit finished tasks"91instruction. When you do commit, append a blank line and92`Co-Authored-By: Claude <noreply@anthropic.com>` **only** if `.claude/project.json`93`coauthor_trailer` is `true`; absent or `false` means no trailer.9495## Gotchas9697- **Don't inflate small tasks.** If the ask is one line of code, the task file can be terse. Don't pad acceptance criteria to look thorough.98- **Respect `GOTCHAS.md`.** Before writing code, check the configured `gotchas_file` (if it exists) for entries that apply to the area you're touching — scoped to the touched area, not the whole file.99- **Capture at exit (flywheel).** If the run hit a real trap (a test flipped red→green, or something surprised you), run the loop-exit capture protocol in `skills/gotcha/SKILL.md` — auto-draft + one-tap confirm; clean runs stay silent. If capture is declined/deferred, append a `/gotcha <drafted text>` sentinel line — structured + deduped by `cmd` (multi-slot seam, see `docs/SEAM.md`): `line='{"cmd":"/gotcha <drafted text>","source":"task","confirm":false}'; grep -qF "$line" .claude/.next-action 2>/dev/null || echo "$line" >> .claude/.next-action` (never a bare `/gotcha`). On Codex (as a fallback until its `.codex/hooks.json` Stop hook is wired+trusted) also print `Next: /gotcha …` inline so the seam degrades gracefully. If no Stop hook is wired at all, apply the **no-hook nudge** (`docs/SEAM.md` SEAM2).100- **Don't skip the failing-test step when there IS a testable surface.** A passing test that was never red verifies nothing. Only skip the red-test cycle for genuinely untestable asks (pure docs/config/copy) — handle those directly here per the note in Section 4; don't punt them to another skill.101- **One task at a time.** `/task` handles a single bounded item. If the ask implies a *batch* of tasks, don't fire `/task` repeatedly — run them through `/sdlc <range>` (e.g. `/sdlc 1-5`), which keeps full-pipeline discipline across the set and commits nothing until you review. For an open-ended feature, start with `/brainstorm`.