Decompose a spec's PRD/TechSpec into a dependency-ordered task graph — vertical-slice task files plus a machine-parseable _tasks.md DAG manifest under docs/specs/<slug>/, including the authored QA gate decision, decided autonomously from the Spec and escalated only when authority, architecture, or blast radius is at stake.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add marcioaltoe/roundfix --skill write-tasks --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Write Tasks?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/marcioaltoe-write-tasks-roundfix)More formats (shields.io, HTML) on the badges page.
---
name: write-tasks
description: Decompose a spec's PRD/TechSpec into a dependency-ordered task graph — vertical-slice task files plus a machine-parseable _tasks.md DAG manifest under docs/specs/<slug>/, including the authored QA gate decision, decided autonomously from the Spec and escalated only when authority, architecture, or blast radius is at stake.
argument-hint: "<spec slug or path under docs/specs/>"
metadata:
category: issue-decomposition
tags: [issues, workflow, prd, agents]
version: 0.0.2
author: Marcio Altoé
source: https://github.com/marcioaltoe/skills
version: 0.0.2
---
# Write Tasks
Turn `docs/specs/<slug>/_prd.md` (and `_techspec.md` when present) into the executable task graph: one `task_NN.md` per task plus `_tasks.md`, the manifest that `implement-task`, `implement-spec`, and any future daemon schedule from. The artifacts are the contract — a fresh agent session must be able to pick up one task file and build it with no other context than the spec folder.
## Preconditions
`$ARGUMENTS` names the spec. `_prd.md` must exist. If `_techspec.md` is missing on a feature with real architectural surface, run `write-techspec` first and then decompose — authoring the missing artifact is the resolution, never a reason to stop for permission. Under autonomous work this needs no confirmation: directing autonomous work authorizes the whole authoring chain for every Spec already in the approved queue, per `docs/agents/autonomous-work.md`. Ask for acceptance only when explicitly told to decompose *without* a tech spec, and then compensate with deeper codebase exploration before decomposing.
### Project Constraint preflight
Run this preflight before deriving or approving a breakdown:
1. Classify the Spec. A Spec under the resolved archive directory
(`docs/history/specs/` for the built-in `docs/specs` root, or
`<spec-root>/_archived/` for an
external or non-default root), or one proven to
have completed before this contract applied, is a completed or archived
legacy Spec. Leave every legacy artifact byte-identical: do not backfill,
reformat, regenerate, or otherwise rewrite it.
2. For every active, non-archived, and not already completed Spec, require the
PRD and every present TechSpec to contain complete `Project Constraints`
sections. Each section must record `Identifier strategy`,
`Authentication and HTTP`, `Active ADR obligations`, and `Tooling authority`
as applicable or not applicable with a reason, and each row must cite its
operative `docs/agents/` source path.
3. If either active artifact is absent or incomplete, refuse to create or
update `_tasks.md` or any Task file. Name every missing row, reason, or
source path and send the Spec back through the owning authorial skill.
4. Inspect the proposed slices for any creation, edit, rename, move, or
deletion of repository-tooling configuration, scripts, ignore files, plugin
declarations, or version pins. Such work is authorized only when the PRD
and present TechSpec record express maintainer authorization and the exact
bounded repository-relative files it covers. A generic implementation
request, setup approval, or Task assignment is not authorization. Without
both the authorization and bounded files, refuse decomposition.
5. Copy the bounded file list into each authorized tooling Task's scope and
acceptance criteria. The Task may change only those paths plus its own Task
file; split any work that needs a different boundary.
Dependencies remain owned only by `_tasks.md`; Task status remains owned only
by each Task file. The preflight never moves either responsibility.
## Ownership rules (what lives where, and why)
- **Dependencies live only in the `_tasks.md` graph.** Never in task files. Two copies of an edge is how graphs drift.
- **Status lives only in each task file's frontmatter.** `_tasks.md` owns topology, not progress — schedulers re-read task files for state.
- **Task Type is required in every task file.** Use exactly one of `backend`,
`frontend`, `data`, `infra`, `docs`, `test`, `chore`, or `qa`. Reserve `qa`
for the unique authored terminal gate named by the manifest's `qa:` field.
The Implement Command consumes this value for routing and rejects missing or
unknown values; never leave a placeholder or infer the type later.
- **Content references, never duplicates.** Task files point at PRD/TechSpec
sections by name; a task that restates the spec goes stale the first time the
spec is amended. Link an adopted source at its post-adoption path in the
owning Spec with a destination relative to that Task file, never at its
pre-adoption provenance path. A secondary Spec links the primary owner's
copy and adopts nothing.
- **Context entries are labeled paths.** Add `## Context` only when the Task
needs specific instruction or interface paths beyond the standard Spec
bundle. Use bullets shaped as `- instruction: <path>` or
`- interface: <path>`. Paths must be clean, repository-relative, and unique;
a Task may declare at most 50 unique entries. The Daemon reserves those paths
before filling the 200-path Spec Context Bundle with prior changed files.
## Decomposition rules
- **Vertical slices.** Each task delivers a narrow but complete path through every layer it touches, demoable or verifiable on its own — a tracer bullet, not a layer ("all the schemas" is a wrong task; "expired imports retry and surface their status" is a right one).
- **Prefactoring first.** When a slice needs the ground prepared, make that its own leading task: make the change easy, then make the easy change.
- **Characterize external surfaces during authoring.** When a Spec crosses an
external surface such as an adapter, contract, or database, author a
characterization Task that records what the real boundary does, not what a
fake does, so an unsupported premise fails while the Spec is still being
authored rather than at the QA gate.
**List it in `needs`, not merely earlier in the file.** Every Task whose
premise depends on the characterization names that Task in its `needs` entry
in `_tasks.md`. Execution waves are built from `needs` alone, so a Task that
merely appears later in the manifest can run in the same wave as the
characterization it depends on, and read a boundary nobody has characterized
yet. File order is not a dependency.
**A characterization Task touches a real boundary, so bound it explicitly.**
Its file states the target it will reach, by name. It reads rather than
writes, or runs against an isolated instance the Task itself creates and
removes; it never characterizes against shared or production state by
default. It uses credentials scoped to that target and no wider, honours
cancellation, and cleans up what it created even when it fails. A
characterization that must write requires that write to be authorized in the
Task the way any other irreversible action is — recording what a boundary
does is not authority to change it.
**Its Verification asserts the record, never the boundary.** The Daemon runs a
Task's declared `## Verification` commands verbatim through the shell, once
against the unchanged tree before the Agent turn exists and again at
settlement. Nothing in that path reads the bounds above: they constrain the
Agent, and the Daemon is not the Agent. A Verification that reaches the real
surface therefore reaches it unbounded, twice, including before any work has
been done. Verify the characterization the Task recorded — its shape, its
presence, what downstream Tasks will read — and leave the boundary call in the
Agent turn, where the Task's declared target, isolation, credentials, and
write authorization apply. This is the hermeticity rule these commands already
carry, stated where the temptation to break it is strongest: a live external
surface is ambient state, and a command that depends on one is not hermetic.
- **Sized for one fresh session.** A task an agent can complete in a single sitting with a fresh context. More than ~7 subtasks or files means split it.
- **Tests embedded, never separated.** Every task's acceptance criteria include its own tests; a trailing "write the tests" task means the earlier tasks were never done.
- **Independently implementable.** Once its `needs` are completed, a task must require no other unfinished work — that's what allows parallel execution across worktrees later.
- **Verification must be hermetic, portable, effect-proving, and Daemon-owned.** Every task's `## Verification` commands must be satisfiable in a fresh worktree using only repository state, declared config, and task-owned setup. Do not depend on untracked local files, prior Runs, interactive prompts, pushed branches, or ambient machine state unless the task explicitly creates that state. Use portable shell forms: prefer `grep` over `rg` in task gates, avoid `wc`-pipeline assertions that vary across platforms, and use repository build flags such as `go build -buildvcs=false ./...` when a build is required. A Task must prove its own effect with executable checks; the Run-level gate proves nothing else regressed. Do not add a whole-package suite command to each Task for regression coverage. Every Verification command must be able to fail when no work was done: a command that names a missing test or selects no cases is vacuous if it still exits zero. A Verification command passes only by exiting zero. For an assertion whose success is an empty result or an absent string, use a form that turns that condition into exit zero: `matches="$(find path -name '*.tmp' -print)" || exit 1; test -z "$matches"`, `test -d path && ! grep -rq 'forbidden' path`, or `find path -name '*.tmp' -print > /tmp/matches.txt 2>&1 || { cat /tmp/matches.txt; exit 1; }; test ! -s /tmp/matches.txt || { cat /tmp/matches.txt; exit 1; }` when failure must print the matches. Refuse the work-independent shape composed only of repository-wide gates plus working-tree cleanliness checks; those checks pass most easily when no Task work occurred. The Daemon runs these commands after the Agent turn and may send one failure-only Verification Feedback prompt; do not tell the Agent to run the authoritative gate itself.
- **Requirements must be mutually satisfiable.** Refuse a Task when its declared `MUST` and `MUST NOT` clauses require and forbid the same named state. Do not send work that cannot satisfy its own written contract to an Agent Session.
- **Gate rehearsals declare their evidence.** A Task whose title states that it rehearses or proves a gate must include `## Rehearsal Cases` with one `- Case: <case>; Observation: <observation>` entry for every case it must exercise. Refuse the Task when the section is absent or any entry lacks its case or observation.
- **One acceptance row rests on evidence the Spec did not author.** At least one
named acceptance row in the graph must rest on evidence originating outside
the Spec's own artifacts — a repository the Spec did not build, a measurement
it did not design, or published literature — and must record where that
evidence came from. A rehearsal shows that the code matches the requirement;
only an outside source can show that the requirement was right. When that
source cannot be obtained during task authoring, decomposition proceeds
without human interaction and records the row as blocked with its reason.
The blocked row is carried into the QA gate, where it triggers a
`rows_blocked_environment` entry on the report and blocks PR preparation.
- **Commit and push stay out of task criteria.** The Daemon owns Task commits, Run integration, and any configured push. Never put commit, push, PR creation, or branch-publishing requirements in task Requirements, Subtasks, Acceptance Criteria, or Verification commands.
### Author the QA gate decision
Follow one order per Spec: implement the graph including its authored gate,
archive, open the Pull Request, watch until Clean, and merge.
ADR-0091 keeps the authored QA gate before any Pull Request exists, while
ADR-0080 lets environment-blocked rows pass with equivalent evidence. Spec
0078 proved that path: eleven of eighteen rows were blocked, nine of those
eleven on no open Pull Request and each of those nine backed by recorded
payload, command-runner, and event-stream evidence.
Every active Spec authored under the QA Task contract must choose exactly one
of these shapes during decomposition:
- **Include the gate.** Add `qa: task_NN` to `_tasks.md` frontmatter, naming
one `task_NN.md` whose `type` is `qa`. Make that node terminal and make its
`needs` list cover every non-QA leaf so the gate cannot run before any branch
of the graph settles.
- **Decline the gate.** Add `qa: declined` and a non-empty `qa_reason` to
`_tasks.md` frontmatter. Do not emit a `qa` Task when the gate is declined.
A post-contract graph with neither declaration, both shapes, an unnamed `qa`
Task, or a gate that is not terminal is a defect. Refuse to produce it. An
absent declaration remains valid only for a legacy graph proven to predate the
contract; leave that graph byte-identical.
Whichever shape the graph takes, its closing node also carries the glossary
check — the authored `qa` Task when the gate is included, otherwise the last
Task in topological order. Give the check that home so a term the Spec coined,
changed, or dropped is noticed while the work is still open rather than after it
closes. The domain guide (`docs/agents/domain.md`) owns what the check looks for
and when the domain context is updated in response; the graph owns only where it
happens, and nothing in it waits for a human.
### Corrective-Task ceiling
The corrective-work ceiling remains two Tasks. When QA findings would require
more than two corrective Tasks, do not author a third patch or stop for a policy
decision. Choose one sanctioned exit:
- Amend the TechSpec and recut the Task Graph from it.
- Promote the excess corrective work to its own Spec and leave the gate failing
the discovered story explicitly.
Reaching the ceiling is a decision inside the loop's authority. The loop chooses
an exit and continues without stopping for a human.
### Task Type selection
Choose the type from the Task's dominant delivered outcome, not from who will
implement it, the tool used, or configuration choices:
- `backend` — CLI, daemon, service, API, domain, or core application behavior;
- `frontend` — browser UI, TUI, visual, interaction, accessibility, or UX behavior;
- `data` — persistence schema, query, migration, import, or data-pipeline behavior;
- `infra` — build, CI/CD, packaging, deployment, environment, or operational infrastructure;
- `docs` — documentation-only behavior or durable knowledge artifacts;
- `test` — test harness, fixture, evaluation, or coverage work without a product behavior change;
- `chore` — bounded maintenance that changes none of the preceding product surfaces;
- `qa` — the unique authored terminal gate that executes `qa-gate` after every
non-QA leaf settles; never use it for implementation tests or an intermediate
checkpoint.
When a vertical slice crosses types, use the type of its primary user-visible or
operational outcome. If two outcomes are independently valuable or the dominant
outcome remains ambiguous, split the Task so each slice has one dominant outcome;
do not encode multiple values and do not defer the classification.
## Process
### 1. Derive the breakdown
Start from the TechSpec's Build Order (its dependency statements become graph edges). Map every PRD user story and core feature onto at least one task; an uncovered story is a hole to fix now. Decide the QA gate once from the authored Spec and add either its terminal node or the reasoned decline to the breakdown.
### 2. Escalation check — decide autonomously, escalate by exception
Derive the breakdown and write it. The Spec is the authorization: a PRD and TechSpec that passed their own gates already carry the product and architecture decisions, so granularity, dependency order, and Task Type classification are derivations from those artifacts — not decisions to confirm. Report the finished breakdown so a change to any slice can be requested afterwards; do not block on approval.
**Stop and ask only when the next decision would change authority, architecture, or blast radius:**
- **Missing authority.** A slice would create, edit, rename, move, or delete protected tooling that the PRD and present TechSpec do not authorize with exact bounded files. Name the paths and why they are needed; never widen the boundary yourself.
- **Unverifiable without a human.** A Task's acceptance genuinely needs a person or a credential the repository cannot hold — a live external account, a physical device, a subjective judgement. Verification must stay hermetic, so a Task that cannot be is a Spec problem, not a graph problem.
- **The Spec is wrong, not just thin.** The decomposition only works by contradicting or extending the TechSpec's Build Order, architecture, or contracts. Route back to `write-techspec` rather than silently redesigning.
- **Irreversible blast radius inside one slice.** A Task would run a data migration, a destructive deletion, a release or publication, or a credential rotation. Confirm the boundary before that Task exists.
- **Irreducible complexity.** A slice cannot be sized to one fresh session and cannot be split without breaking the vertical-slice rule. That signals an under-specified Spec; say which slice and why.
Escalate with the specific blocking fact and a proposed resolution, never with an open "is this ok?". Anything outside those five is a derivation: granularity preference, naming, ordering the Build Order already implies, and dominant-outcome classification are decided here. Every `type` cell must carry exactly one allowed Task Type before writing — a blank, placeholder, or combined value is a defect to resolve, not a question to ask.
### 3. Write the files
From the templates in [references/task-template.md](references/task-template.md), write `_tasks.md` and every `task_NN.md` (numbered from `01` in topological order). Copy the derived Task Type into each task's `type` frontmatter and the `_tasks.md` projection table. For an included gate, name the `qa` Task with `qa:` in manifest frontmatter, place it last in topological order, and give it every non-QA leaf in `needs`; for a decline, write `qa: declined` with `qa_reason` and emit no `qa` Task. Acceptance criteria must be independently verifiable — a criterion nobody can check is a wish, not a criterion. Include a `## Verification` section with exact, hermetic, portable commands that prove the task's effect in a fresh worktree; the Daemon runs them verbatim after Agent work. Add `## Context` only for task-specific instruction or interface paths that the standard Spec Context Bundle would not make obvious.
Durability applies here too: describe behavior and interfaces, not repo file paths (relative references within the spec folder are fine — the folder moves as a unit).
### 4. Validate the graph
Before reporting, verify mechanically — parse, don't eyeball.
Start by running the repository's own checker on the Spec you just decomposed:
```bash
roundfix spec check <slug> --run-verification
```
This form reads the PRD, the TechSpec, the ADRs they cite, and the graph
together — which is the first moment all four exist — and executes authored
Verification commands in a disposable checkout at `HEAD`. A clean verdict from
`roundfix spec check <slug>` without `--run-verification` does not cover
Vacuous Verification: it does not execute those commands against the unchanged
tree, so a command that already exits zero can remain undiscovered. Do not
prefer the non-probing form for its speed; that speed is a consequence of
omitting command execution.
Treat every reported error as blocking: fix the artifact and re-run until it is
clean, and do not report the breakdown while a finding stands. A `[gap]` is
blocking too; account for the named ADR or record why it does not apply. Skipped
checks are informational.
This is not a substitute for the list below, which covers what the checker does
not: it catches unlisted and unaccounted ADRs, incomplete Project Constraints,
unmapped coverage, contradictory requirements, undeclared rehearsals,
work-independent Verification, and an undocumented Vocabulary Contract. It does
not yet verify that a cited ADR says what the artifact claims it says.
Then confirm by reading:
- Every `graph.nodes[].file` exists and every task file has parseable frontmatter with `status: pending`.
- Every task file has exactly one `type` value from `backend`, `frontend`,
`data`, `infra`, `docs`, `test`, `chore`, or `qa`; the `_tasks.md` projection
row for that task contains the identical value.
- Every `needs` entry names an existing node id; no cycles (a topological order can be printed).
- Every post-contract graph declares exactly one QA decision. An included gate
is the unique `qa` Task, has no dependents, and depends directly or
transitively on every non-QA leaf; a declined gate has a non-empty
`qa_reason` and no `qa` Task.
- Every PRD user story appears in some task's References.
Print the wave plan (wave 1 = no needs; wave N = needs met by earlier waves) as the execution preview.
### 5. Report
Reply with the breakdown table (`id | title | type | complexity | needs`), the authored QA decision and its reason when declined, a one-line rationale for the slicing, the file list, and the wave plan — and say that a change to any slice can be requested now. Name any escalation trigger you hit and how it was resolved.
## Anti-patterns
- Horizontal slicing (a schema task, an API task, a UI task) — every task should cut through the stack.
- Mega-tasks that "do the feature" — if it can't be verified alone, it isn't a task yet.
- Vague criteria ("works correctly", "handles errors") — name the observable behavior.
- Non-hermetic Verification that relies on local-only files, prior Run state, manual prompts, or remote branch state.
- Non-portable Verification such as `wc` pipeline shape checks, `rg` dependency checks where `grep` works, or Go build commands missing the repository's `-buildvcs=false` flag.
- Verification that only exercises broad suites without proving the Task's specific effect.
- Commit, push, PR, or branch-publishing acceptance criteria — those are Daemon and delivery responsibilities, not task success criteria.
- Editing the graph and the task files out of sync — regenerate both from the same breakdown.
- Blocking on approval for a breakdown the Spec already authorizes — invocation is the authorization, and granularity is a derivation, not a decision.
- Proceeding past a real escalation trigger because the loop is running — a missing tooling authorization or a Task that cannot be verified hermetically is a stop, not a judgement call.
- Missing, placeholder, combined, or invented Task Types — classification is a
required routing contract, not descriptive free text.
- Leaving a post-contract graph without either an authored terminal `qa` Task
or `qa: declined` plus `qa_reason` — omission is a defect, not a per-run QA
choice.
## References
- [references/task-template.md](references/task-template.md) — templates for `_tasks.md` and `task_NN.md`. Read it before writing any file.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!