Writing tasks an agent can build
How to choose the right task type, fill required fields, write acceptance criteria, set priority, and attach tasks to parent work so the engine routes them correctly.
The quality of your tasks directly determines the quality of an agent’s output. A task with a vague title and no acceptance criteria will be attempted — and often not quite hit the mark. A task with a clear type, a concise description, and a concrete checklist of success conditions gives the engine everything it needs to route the task correctly and the agent everything it needs to build confidently.
Choose the right type
Every task has a type (set with --type) that controls its workflow pipeline. CodeHerder ships with six built-in types:
| Type | Use when | Pipeline |
|---|---|---|
story |
A concrete, buildable unit of work that fits in one MR | todo → plan → code → review → merge → verify → done |
task |
A lightweight to-do for yourself or a specific member | todo → code → merge → done |
bug |
A defect to triage and fix | todo → plan → code → review → merge → verify → done |
feature |
Same full pipeline as story — plan-first with review, merge, and verify | todo → plan → code → review → merge → verify → done |
epic |
A scoped deliverable that groups stories | todo → planning → in_progress → done |
initiative |
A cross-team objective that groups epics | todo → planning → in_progress → done |
For most concrete work, story is the right starting point. A story gives an agent a brief planning stage to read the description and confirm the acceptance criteria before it writes any code — which keeps build sessions focused and traceable. Stories are auto-staffed: once you create one and fill its required fields, the engine assigns an agent and begins work automatically.
feature is identical to story — same pipeline, same gates, same auto-staffing. Use it when your team’s conventions distinguish “features” from “stories”, or when the parent epic expects feature-typed children. Use task for lightweight to-dos where you want to assign work directly to a person — task is the one type the engine does not auto-staff.
epic and initiative are containers — they group child work items and never run a build stage themselves. A container’s in_progress status means “child tasks are being worked”, not “an agent is coding”. See Core concepts for the full hierarchy rules and How work flows for the stage machine.
Create a task
ch task create --title "Add password-reset flow" --type story
--title is required. --type defaults to task if omitted. The command prints the new task’s ID — copy it, as you will need it to set fields in the next step.
Title and description — three input forms
Both --title and --description accept three ways to supply text:
- Inline: pass the text as a quoted string —
--title "My task". - File:
--from-file <path>reads from a file;--from-file -reads from stdin. - Bare stdin:
-as the value reads from stdin without--from-file—--title -.
At most one of --title or --description may read from stdin in a single invocation.
--description is optional. When omitted, no description is stored; you can add or change it later — see Editing and cancelling tasks.
# Description from a file:
ch task create --title "Deploy OAuth callback" --type story --description --from-file brief.md
# Title from stdin:
echo "Add rate limiting" | ch task create --title - --type story
Priority
--priority controls the order in which the engine picks up tasks. Valid values: high, normal (default), and low. High-priority tasks are staffed before normal, and normal before low.
ch task create --title "Fix login crash" --type bug --priority high
Parent task
Pass --parent <taskId> to nest this task under an existing one at creation time. The parent must be a type that accepts children of this task’s type — stories attach to epics, features attach to epics or stories, bugs attach to stories (not directly to epics), and plain tasks have no restriction. For how to re-parent or detach an existing task, see Attach to parent work below. For the complete nesting rules, see Task hierarchy.
Dependencies
--depends-on declares an upstream task that must reach done before this task can advance. Repeat the flag to declare multiple upstreams at once:
ch task create --title "Deploy new API" --type story \
--depends-on <migrationTaskId> \
--depends-on <schemaTaskId>
CodeHerder blocks every forward stage advance — planning stages included — until each upstream finishes. An upstream satisfies the gate however it finishes; if it’s cancelled or archived rather than completed, CodeHerder leaves a note on this task saying the upstream’s work never landed, so a human can judge whether the task should still go ahead — see Task dependencies for the full model, including what happens after creation (adding edges, removing them, and viewing the dependency graph).
Due date
--due-at sets a deadline. Pass an RFC 3339 timestamp:
ch task create --title "Security audit" --type story --due-at 2026-07-01T00:00:00Z
The due date is visible in the task list and on the task detail page. To update or clear it after creation, see Editing and cancelling tasks.
Required capabilities
Pass --cap to signal that this task needs a specific capability — such as a language skill or model tier. Repeat for multiple requirements:
ch task create --title "Write Go migration" --type story --cap go --cap model:opus
The engine ranks candidate agents by how many of these capabilities their skill set covers — agents with more overlap are prioritised for assignment. For a full explanation of how capability routing works and what to do if a task is not being picked up, see Agents and the CLI and Capabilities.
Fill required fields
Each type has fields the engine checks (via stage gates) before advancing to the next stage. Use ch task field to set them after creation:
ch task field <taskId> <fieldKey> --from-file <path>
| Type | Gate-required fields |
|---|---|
story |
acceptance (at plan); artifact (at code); verification (at verify) |
task |
artifact (at code) |
bug |
repro (at plan); artifact (at code); verification (at verify) |
feature |
acceptance (at plan); artifact (at code); verification (at verify) |
epic |
proposal, design, spec, plus at least one accepted child — a story, feature, or task (all at planning) |
initiative |
proposal, design, spec, plus at least one child epic (all at planning) |
The artifact field holds the merge request or commit link that the code agent opens. It is filled automatically by the agent and enforced by the code stage gate — the task cannot advance to review until it is set.
The verification field records proof that the merged change works on the default branch. Like artifact, it is filled automatically — here by the verify-stage agent — and enforced by a stage gate: a story, feature, or bug cannot advance from verify to done until it is set.
For a bug, supply concrete reproduction steps:
ch task field <taskId> repro --from-file repro.md
You can read any field’s current value by running the same command without --from-file:
ch task field <taskId> acceptance
To see every stored field value at once:
ch task fields <taskId>
To check readiness instead, see ch task validate below.
Write acceptance criteria
For story and feature tasks, the acceptance field is what the planning agent reads and what the engine checks before the task can advance to the code stage. A task without acceptance criteria cannot leave the plan stage.
Set acceptance criteria from a file:
ch task field <taskId> acceptance --from-file criteria.md
Acceptance criteria are a checklist, not a plain list. Write each item as a GFM task-list line (- [ ]), and the task page renders it as a tickable checkbox with a progress badge showing how many items are done:
- [ ] Login form accepts email and password
- [ ] Invalid credentials show a clear error message
- [ ] Session persists across page reloads
- [ ] Successful login redirects to the dashboard
Ticking an item is how a criterion gets signed off. Every item must be ticked before the task can advance to merge or done — an unticked item blocks the close. This is separate from the presence check above: the stage gate lets the task leave plan once the field is written; the checklist gate additionally requires every item ticked before the task can reach merge or done.
Tick items by clicking the checkbox on the task page, or by editing the marker ([ ] → [x]) and re-saving the field:
ch task field <taskId> acceptance --from-file criteria.md
A plain bullet list without [ ] still renders as text, but none of its items count as checklist items — no checkboxes, no progress badge, and nothing to tick before closing. Use - [ ] items for every criterion you want tracked and enforced.
Avoid vague items like “the feature works” or “the code is clean”. The agent reads the acceptance criteria literally when deciding what to build, and so should you when reviewing and ticking the result. If an item could be satisfied without the agent actually solving the underlying problem, tighten it.
To check whether a task’s required fields are filled before the engine tries to advance it:
ch task validate <taskId>
validate runs the stage gate (the CLI calls this the rigor gate) as a dry run and reports any missing fields. It exits 0 if the task is ready to advance, 2 if there are violations.
Attach to parent work
To nest a task under a parent at creation time, pass --parent:
ch task create --title "Implement OAuth callback" --type story --parent <epicId>
The parent must be a type that accepts this task’s type as a child. Stories attach to epics, features attach to epics or stories, and bugs attach to stories (not directly to epics). Plain tasks have no parent-type restriction. See Task hierarchy for the full set of rules.
To attach an existing task to a parent (or move it):
ch task set-parent <taskId> <parentId>
To detach a task from its parent and make it a root-level item:
ch task set-parent <taskId> --none
What the engine does next
Five of the six built-in types — initiative, epic, feature, bug, and story — are auto-staffed: once an unassigned task of one of these types is created and its first-stage fields are ready, the engine assigns a capable idle agent and begins work automatically. You do not need to assign anything manually; the engine handles routing.
The exception is task. Tasks are the lightweight, human-started to-do type; the engine leaves them for a person to direct. See Assigning and claiming work to route a task to yourself or another member via the web app.
For the full list of pipeline stages and how the stage gate works, see How work flows. For the object model behind types and their hierarchies, see Core concepts. For monitoring a task after it starts, see Finding and tracking your work.
Related guides
- How work flows — the stages every task moves through
- Assigning and claiming work — who ends up working a task
- Approvals & staying in control — gate advances behind a designated approver
- Reviewing an agent’s work — the human side of the review stage
- Why isn’t my task moving? — diagnose a stuck task
