Break an implementation plan markdown file into a sequenced, dependency-annotated, checkbox-tracked task list saved as a sibling doc (e.g. `docs/<feature>-tasks.md`). Each task is annotated with the lower-numbered tasks it depends on (or marked dependency-free) and ordered topologically, so independent tasks can be implemented in parallel. The task list is the execution contract — each task is one logical commit that bundles the code change **and** the ticked checkbox together (never a separate "tick-only" commit). Use this whenever the user has an implementation plan (file like `docs/<thing>-plan.md`, an `## Implementation plan` section, or a freshly written design doc) and wants a step-by-step todo list to drive the build. Trigger on phrases like "break this plan into tasks", "make a task list", "generate the tasks doc", "turn this plan into checkboxes", "split this into steps I can implement one by one", or whenever a plan exists and the next move is execution — even if the user doesn't explicitly say "task list".
Convert an implementation plan into an ordered, phase-grouped, dependency-annotated, checkbox-tracked task list that an implementer (Claude or human) can work through one task at a time, committing after each one. Each task records which other tasks it depends on — and dependency-free tasks are flagged — so a single implementer can go top-to-bottom while multiple implementers can fan out on independent tasks in parallel.
The output isn't documentation — it's an execution contract. Done right, the user (or a future agent) can open the tasks doc, pick the first unchecked box, implement only that, tick the box, and commit the code + tick together as one commit before moving on.
Where the output lives
Default path: alongside the source plan, with
-plan.md
swapped for
-tasks.md
.
Plan at
docs/budgets-page-plan.md
→ tasks at
docs/budgets-page-tasks.md
Plan at
docs/auth-rewrite-plan.md
→ tasks at
docs/auth-rewrite-tasks.md
If the plan has no
-plan.md
suffix, drop the file next to it with a
-tasks.md
name derived from the feature (ask only if the slug isn't obvious — otherwise pick the reasonable name and proceed).
If a tasks file already exists at the target path, do not overwrite without checking. Either ask the user, or — if they clearly want a regeneration — read the existing file first to preserve any checkboxes already ticked. Re-ticking finished work is annoying.
Document structure
Use this template exactly. The intro paragraph, the auto-commit callout, and the test-coverage callout are non-negotiable — they teach the next agent how to use the doc.
markdown
# <Feature name> — task listStep-by-step execution order for `<relative-path-to-plan>.md`. Tick each task as it lands. Order is intentional: <oneshortsentencenamingthesliceorder,e.g."viewfirst,thenedit,thendelete—eachsliceshipsitstypes,action,UI,copy,andteststogethersotheappbootsandthesliceworksaftereverycommit">.
**Workflow:** Each task is one logical commit. When you finish a task: (1) tick its checkbox in this file, (2) stage the implementation changes **plus** the ticked checkbox together, and (3) create a single commit covering both. Never create a "tick-only" commit separate from the implementation — the box moves in lockstep with the code. Do not batch multiple tasks into one commit either; the per-task granularity is what makes this list resumable and reviewable.
**Working app (non-negotiable):** every task must leave the app runnable and the slice it touches end-to-end functional. If a task adds UI, it adds the i18n keys, types, server actions, styles, and routes that UI needs **in the same task**. Never split a slice across tasks such that an intermediate commit leaves the app with missing translations (raw `t("…")` keys rendered), undefined imports, unimplemented action calls, dead components, or a half-wired flow. Layer-first ordering ("all types → all actions → all UI → all i18n") is wrong: prefer vertical slices that each ship one working increment.
**Test coverage (non-negotiable):** every task must leave the suite green AND meaningfully exercised. Before ticking a task, (a) re-run the relevant existing tests and confirm they still cover the touched behavior, and (b) add new unit/integration tests for any new function, action, branch, or UI state the task introduces. A task that adds code without adding or extending tests is not done — extend the assertions, add a new `*.test.ts(x)`, or document in the commit message why no new test is warranted (e.g. pure type-only change). Reviewer will reject task ticks that ship untested logic.
**Dependencies & parallelism:** Every task line ends with a `_(deps: …)_` marker listing the lower-numbered tasks that must land before it, or `_(deps: none)_` if it has no prerequisites. Tasks marked `none` are independent and can be picked up in parallel by separate implementers (people or agents) from the start; any other task becomes available the moment all the tasks it names are done. Ordering is topological — a task never depends on a higher number — so working strictly top-to-bottom is always valid for a single implementer, while the markers let multiple implementers fan out safely. See the **Parallelizable now** line under each phase for the independent entry points.
## Phase 1 — <slice name, e.g. "View budgets"> (<optional: why this slice is first>)_Parallelizable now: 1, 2 (no prerequisites)._- [ ] **1.**<Actionverb><filepathinbackticks> — <whatchanges,includingi18nkeys,types,tests,andanyothercross-cuttingbitsthesliceneedstoberunnable>. _(deps: none)_- [ ] **2.**<…>_(deps: none)_## Phase 2 — <next slice, e.g. "Edit budget">_Parallelizable now: 3 (depends only on Phase 1, which is independent of this slice's siblings)._- [ ] **3.**<…>_(deps: 1)_...
## Phase N — Verification- [ ] **<n>.**`pnpm lint` clean. (or the project's lint command)
- [ ] **<n+1>.**`pnpm typecheck` clean.
- [ ] **<n+2>.**`pnpm test` green.
- [ ] **<n+3>.** Manual smoke: <onesentenceperuser-visibleflowtoclickthrough>.
## Phase N+1 — Wrap-up- [ ] **<n>.**<anystatus-trackingtheprojectuses,e.g.closinganissueorflippingastatustoDone>.
How to break a plan into tasks
1. Read the plan carefully
Read the whole plan before writing anything. Note the sections, the "Critical files" list if present, the verification section, and any "Out of scope" callouts. The tasks list must cover everything the plan promises to ship — nothing more, nothing less.
2. Slice by user-visible increment, not by code layer
Each task is a vertical slice — a small, working increment of user-visible behavior. A task that adds a button also adds the action it calls, the i18n keys for its label, the types its props need, and the tests for its behavior. After the commit, the app boots and the slice works end-to-end. No "we'll add the i18n later" task — the strings ship with the markup that consumes them.
Wrong (layer-first): Phase 1 all types → Phase 2 all actions → Phase 3 all UI → Phase 4 all i18n. This leaves the app in a non-working intermediate state for many commits — buttons render with
t("budgets.edit")
returning the raw key, components import actions that don't exist yet, pages 404, fixtures reference fields the type doesn't expose.
Each phase ships a fully working slice. After Phase 1's commit, you can boot the app and view budgets. After Phase 2's commit, you can view and edit. No intermediate broken state.
Within a slice, dependencies still matter — write the type before the consumer, the action before the component that calls it. But the slice is the shipping unit, and everything the slice needs to be runnable lands together in its task (or in adjacent tasks of the same phase, if the slice is genuinely large enough to split).
Cross-cutting phases stay separate at the end: Verification (lint, typecheck, tests, manual smoke) and Wrap-up (any status-tracking the project uses, e.g. issue closure).
If the plan inverts something (e.g., a refactor that must land before any new slice is added), follow the plan. The governing principle: after every committed task,
pnpm dev
boots, the touched flow works, and nothing renders raw i18n keys or throws on missing imports.
3. Choose the right task granularity
One task = one logical commit that leaves the app working. That's the test. The commit bundles the implementation diff and the ticked checkbox; never split the tick into its own follow-up commit.
A right-sized task can — and often should — span layers: types + action + component + i18n keys + tests, all in one task, as long as it stays a single coherent slice. The slice is what makes it commitable; the layers are just the parts the slice needs.
A task is too big if:
It bundles multiple user-visible slices ("add edit and delete and duplicate").
The user would want to review it as multiple PRs.
It touches many unrelated areas of the codebase.
A task is too small if:
It leaves the app non-functional after the commit (e.g. adds an i18n key with no markup using it, adds a component that's never rendered, adds an action with no caller).
It's "add an import statement" or "rename a single variable" with no behavior change.
Splitting it from the next task produces a commit where the app between commits is broken or the change makes no sense on its own.
Wrong-shape tasks (avoid entirely):
❌ "Add all i18n keys for the feature" — layer-task; UI added in earlier tasks renders raw keys until this lands.
❌ "Add all server actions" — layer-task; UI calling them is broken in the meantime.
❌ "Add types" as a standalone task with no consumer — dead code at commit time.
❌ "Wire up i18n" as a final step — every UI task should already have wired its own keys.
The layer-shaped task is the one to watch out for: anything phrased as "all X" or "wire up X across the feature" is a smell. Convert it into per-slice work and fold it into the slice tasks.
Good granularity examples:
Right-sized: "Build inline budget editing — add
src/components/budget-edit-row.tsx
(client; display + edit modes, inline name/amount inputs), wire to
updateBudget
, add i18n keys
budgets.edit.{label,save,cancel,error}
to
messages/en.json
+
messages/de.json
, add
budget-edit-row.test.tsx
covering both modes." — one slice, one commit, app is runnable and editing works after it lands.
Right-sized (foundational): "Add
updateBudget({ id, name, amount, categories })
to
src/actions/budgets.ts
(zod schema =
createBudgetSchema
+
id
, throws
"NOT_FOUND"
, revalidates
/[locale]/budgets
+
/[locale]/budgets/manage
) paired with the component task in the same slice so no commit ships an action with no caller."
Too big (avoid): "Build the manage page" — bundles three slices (view + edit + delete) into one commit; should be three tasks.
Layer-shaped (avoid): "Add i18n keys for budgets manage page" as its own task at the end — split the keys across the slice tasks that actually use them.
4. Write each task as a concrete instruction
Each bullet should answer: what file, what change, what constraints.
Pattern:
- [ ] **<N>.** <Verb> <file in backticks> — <one sentence of what changes, including any tricky constraint in parens>.
Verbs that work well:
Add
,
Build
,
Update
,
Rewrite
,
Extract
,
Wire up
,
Extend
,
Flip
.
Include in parens any constraint that matters for correctness: zod schemas, revalidation paths, error codes, props, edge cases. The implementer should not have to re-read the plan to get the task right — but the plan should remain the source of truth for design rationale.
Example transformations (plan prose → task line):
Plan: "Add
updateBudget({ id, name, amount, categories })
— zod schema mirrors
createBudgetSchema
plus
id
. Throw
"NOT_FOUND"
if missing. Revalidate
/[locale]/budgets
and
/[locale]/budgets/manage
."
Task:
- [ ] **7.** Add
updateBudget({ id, name, amount, categories })
to
src/actions/budgets.ts
(zod schema =
createBudgetSchema
+
id
, throws
"NOT_FOUND"
, revalidates
/[locale]/budgets
+
/[locale]/budgets/manage
).
Plan: "
src/components/budget-period-picker.tsx
: Add optional prop
basePath?: string
(default
"/budgets"
). Replace the hard-coded path with
${basePath}?year=&month=
."
Task:
- [ ] **18.** Add optional
basePath?: string
prop (default
"/budgets"
) to
src/components/budget-period-picker.tsx
; replace the hard-coded path with
${basePath}?year=&month=
.
5. Number tasks globally, group by phase
Numbers run 1..N across all phases (don't restart per phase). The number is what people quote when discussing a task ("I'm on task 14"). Phase headers exist for grouping and ordering rationale, not for numbering.
6. Annotate dependencies and mark parallelizable tasks
Every task carries a
_(deps: …)_
marker so a reader can tell, at a glance, what must land first and what can be built independently. This is what lets the work fan out across multiple implementers instead of forcing a single serial chain.
What a dependency is. Task B depends on task A when B's code cannot be written, compiled, or tested without A already in place — B imports a type A defines, calls an action A adds, renders a component A builds, or asserts against a fixture A creates. If B can be implemented and its tests pass with A absent, B does not depend on A, even if they live in the same feature.
What is not a dependency. Don't manufacture edges from mere thematic grouping ("both touch budgets") or from preferred review order. Two independent slices in the same phase should each read
_(deps: none)_
or point only at a shared foundation — not at each other. Over-declaring dependencies silently kills parallelism, so be strict: list an edge only when removing it would break the build or the tests.
How to derive the markers:
List the concrete artifacts each task produces (a type, an action, a component, a route, a set of i18n keys, a migration).
For each task, find which of those artifacts it consumes. The producing tasks are its dependencies.
Record them as the lower task numbers:
_(deps: 4, 7)_
. A task that consumes nothing produced by an earlier task is
_(deps: none)_
.
Ordering. Sort tasks so the graph is topological — every dependency has a lower number than the task that needs it (no forward references). Within that constraint, keep vertical slices contiguous (don't interleave Phase 2's tasks into Phase 1) and put independent, prerequisite-free tasks early so implementers can start them immediately.
Mark the parallel entry points. Under each phase header, add one italic line —
_Parallelizable now: <task numbers> (<why>)._
— naming the tasks in that phase whose dependencies are all satisfied by the time the phase opens (i.e. everything they need is either
none
or in an already-completed earlier phase). These are the tasks a second implementer can pick up without waiting. If a phase has a single strictly-serial chain, say so:
_Sequential: 5 → 6 → 7 (each builds on the last)._
Worked example. A "Manage budgets" feature:
1. Add
Budget
type +
budgetSchema
to
src/types/budget.ts
. (deps: none)
2. Add
messages/{en,de}.json
keys
budgets.manage.*
. (deps: none) — pure copy, needs nothing.
3. Add
listBudgets()
to
src/actions/budgets.ts
+ test. (deps: 1) — returns
Budget[]
.
4. Add
updateBudget()
to
src/actions/budgets.ts
+ test. (deps: 1) — independent of 3; both only need the type.
5. Build
budget-list.tsx
(renders
listBudgets
, uses
budgets.manage.*
) + test. (deps: 2, 3)
6. Build
budget-edit-row.tsx
(calls
updateBudget
, uses
budgets.manage.*
) + test. (deps: 2, 4)
Tasks 1 and 2 are parallel from the start. Once 1 lands, 3 and 4 run in parallel. 5 and 6 are independent of each other and each unblock as soon as their own deps finish. A single implementer still just goes 1→6 top to bottom.
Keep the dependency annotation aligned with the same correctness rule the rest of this skill enforces: a task and its dependencies must still, when committed in order, leave the app runnable after each commit. Parallelism is about who can work simultaneously — it never licenses an intermediate commit that breaks the build.
7. Always include verification and wrap-up phases
Verification phase: Whatever the project uses for static checks (lint, typecheck) + tests + a one-line manual smoke covering the user-visible flow. If the plan has a "Verification" section, base this on it. If it doesn't, derive checks from the project's conventions (look at
package.json
scripts, CLAUDE.md, etc.).
Wrap-up phase: Anything that marks the feature done in the project's tracking system, if it uses one — e.g. closing a GitHub issue, updating a CHANGELOG, flipping a status to Done.
8. Don't invent scope
The task list must not introduce work the plan didn't agree to. If the plan says something is "Out of scope," don't add tasks for it. If the plan is vague on a detail, leave the task line aligned with the plan's level of detail — don't fabricate constraints.
If the plan has a gap that blocks task generation (e.g., no acceptance criteria for a non-trivial flow), flag it back to the user instead of guessing.
Self-check before saving
Before writing the file, run this mental checklist:
Every "Critical files" entry in the plan appears in at least one task.
Each task fits in one logical commit (not too big, not too small).
Every task line ends with a
_(deps: …)_
marker listing its lower-numbered prerequisites, or
_(deps: none)_
if it has none.
All declared dependencies point to lower task numbers — the order is topological, no forward references.
Dependency edges are real (an artifact a task consumes is produced by the task it names); no edges invented from mere theme or review order.
Every phase has a
_Parallelizable now: …_
(or
_Sequential: …_
) line naming the tasks that can start once the phase opens.
At least the genuinely independent, prerequisite-free tasks are marked
_(deps: none)_
and ordered early.
After every task,
pnpm dev
boots and the slice that task touches is functional end-to-end — no dangling i18n keys rendered as raw
t("…")
, no unimplemented action calls, no orphan components, no broken imports.
No layer-shaped tasks (no standalone "add all i18n keys", "add all actions", "wire up types"); cross-cutting concerns are folded into the slice tasks that need them.
Phases are named by slice/user-visible increment (e.g. "View budgets"), not by code layer (e.g. "Types", "Actions").
Verification phase covers the project's static checks + tests + a manual smoke.
Wrap-up phase reflects the project's tracking conventions, if any.
The intro names the plan file and states the slice order in one sentence.
The auto-commit workflow callout is present and unmodified — and it tells the implementer to tick the box and commit the tick together with the code in a single commit (never a separate tick-only commit).
The working-app callout is present and unmodified.
The test-coverage callout is present and unmodified.
Task numbers run 1..N globally; no gaps, no restarts per phase.
After saving
State briefly: "Saved task list to
<path>
. <N> tasks across <K> phases. Start with task 1." Don't dump the whole list back into chat — the user will open the file.