Editing and cancelling tasks
How to edit a task's fields, complete it early, or cancel, archive, and reopen it.
Once a task is created you can update its details at any time — no need to cancel and re-create. This page covers every edit surface: the web app, and ch task edit — the one command that writes any field on a task, whether it’s a column every type shares (title, priority, description, due date, base branch) or a per-task field like acceptance criteria. ch task field reads a field’s current value back. This page also covers the status transitions that close, cancel, or reopen a task.
Editing a task with ch task edit
ch task edit takes one flag per field you want to change, native or per-task. Only the flags you pass are updated:
ch task edit <taskId> --title "Renamed title"
Each flag accepts its value three ways: inline as shown above, from a file with --<flag>-file <path>, or from stdin with --<flag>-file - (or, on some flags, a bare -). There’s no standalone --from-file flag on ch task edit itself — since a single call can touch several freeform fields at once, the file and stdin forms are always tied to the specific flag they belong to:
ch task edit <taskId> --description-file description.md
Get a flag name wrong and ch task edit tells you what it actually accepts — an unrecognised flag lists every flag it accepts, plus every field key this task’s type declares.
Of the native columns below, title, priority, description, and base branch also have their own controls in the web app, described in the sections that follow — use whichever is convenient; both write to the same task. The due date is the exception: it has no web-app control, and is set, cleared, and viewed from the CLI only (see below).
ch task edit also takes --cap/--clear-caps and --device-cap/--clear-device-caps to replace a task’s required capabilities wholesale — see Capabilities for what a capability label means and when to reach for a device capability instead of a task one.
Editing the title
From the web app, open the task. The title appears at the top of the task detail page as an editable field — click it to begin typing. Press Enter or click Save to save your change, or press Escape or click Cancel to discard it. Clicking elsewhere on the page doesn’t save or discard anything — the field stays open until you use one of those four.
From the CLI:
ch task edit <taskId> --title "Renamed title"
Editing priority
From the web app, priority is shown in the detail panel on the task page. Click the priority badge to open a dropdown and select high, normal, or low. CodeHerder saves the change immediately and re-sorts the task in the queue.
From the CLI, --priority accepts exactly high, normal, or low:
ch task edit <taskId> --priority high
Editing the description
From the web app, the Description section appears below the task metadata on the detail page. Click Edit (or Add, if there’s no description yet), write your markdown in the editor that appears, then click Save changes. To discard your changes, click Cancel.
From the CLI, pass the body with --description-file rather than inline (this avoids shell-escaping problems with long or multi-line text):
ch task edit <taskId> --description-file description.md
Setting or clearing the due date
From the CLI only — there’s no web-app control for a task’s due date, and it isn’t shown anywhere in the web app either.
--due-at takes an RFC 3339 timestamp:
ch task edit <taskId> --due-at 2026-08-01T00:00:00Z
To clear a due date, pass an empty string:
ch task edit <taskId> --due-at ""
Changing the base branch
From the web app, the task page has an editable Base branch row alongside the task’s other details — click it to set or clear the value.
From the CLI:
ch task edit <taskId> --base-branch release/2.1
Pass an empty string to clear it and fall back to the repo’s default branch:
ch task edit <taskId> --base-branch ""
While the task has a running sandbox, its base branch is fixed — the worktree is already checked out from wherever it started, and CodeHerder refuses the change. See Choosing a task’s base branch for what a base branch is, how CodeHerder resolves one when a task doesn’t set it, and what happens once work has started.
Editing per-task fields (acceptance criteria, repro steps, and others)
The fields above are the same for every type. Per-task fields are different: which ones exist depends on the task’s type — acceptance criteria for stories and features, reproduction steps for bugs, proposal/design/spec for epics, and so on. Run ch task fields <taskId> to see this task’s own list.
Write one the same way as any native field, by passing it as a flag named after its key:
ch task edit <taskId> --acceptance-file updated-criteria.md
Read the current value back with ch task field:
ch task field <taskId> acceptance
ch task field is a frozen alias for the show op of the nested ch task fields child collection — see Managing a record’s child collections. ch task edit --<key>-file is the canonical writer; ch task field <taskId> <key> --value <text> still writes too, but reach for it only when the field’s key clashes with one of the CLI’s own global flag names (see Customising workflows).
For full details on field keys and which fields each type requires, see Writing tasks an agent can build.
When a field can be written
Some per-task fields only accept a write while the task sits at the stage that produces them. ch task fields <taskId> shows, for every field this task’s type declares, whether it’s required and whether it’s writable right now:
KEY TYPE REQUIRED WRITABLE VALUE (preview — see `ch task fields show` for the full body)
acceptance checklist required@plan writable - [ ] Login form accepts… [2 outstanding]
plan markdown required@plan locked (writable@plan) (unset)
artifact markdown required@code locked (writable@code) (unset)
verification markdown required@verify locked (writable@verify) (unset)
On the built-in story and feature types, acceptance is writable at any stage. A bug follows the same shape, with repro in place of acceptance — and no plan field at all. plan, artifact, and verification are more restricted: plan only while the task is at plan, artifact only at code, verification only at verify — each opens at the one stage that produces it and locks again once the task moves past it. A workspace that has customised its story or feature workflow may not carry a plan field; this sample reflects the built-in one.
Write to plan, artifact, or verification at any other stage and ch task edit refuses, naming the stage that does accept it — including after the task has moved past it, not just before it arrives:
edit task: HTTP 409: field "artifact" cannot be written while the task is at stage "review"
hint: this field is writable only at stage(s): code
The web app shows the same rule as a lock icon next to a field that isn’t writable at the task’s current stage — hover it to see which stage the field can be written at. A checklist field like acceptance also carries a ticked/total progress badge once it has items, whether or not it’s currently writable.
Sending a task backward — a rejected review, say — can archive a field’s current value and reset it, so whoever picks the work back up can still see what was there before. ch task fields versions <taskId> <key> reads that history; it’s read-only, with no way to restore an old value. See Version history and going back for how a task field’s history compares with a workflow’s, a stage’s, a wiki page’s, a task’s own definition, and a stage rubric.
This page covers editing the task itself. To correct a comment already posted on a task — a workspace-owner, web-app-only action — see Collaborating.
A task’s own definition history
Separately from its per-task fields, CodeHerder keeps a saved history of a task’s own definition — its title, description, type, status, priority, required capabilities, required device capabilities, due date, parent, workflow override, requested agent, join policy, cost budget, and base branch. Every edit that changes one of those saves a new version automatically; you don’t have to do anything to turn it on.
ch task versions <taskId>
ch task versions <taskId> --version 5
ch task versions <taskId> lists the saved versions newest first, paged with --limit and --cursor. Add --version N to read one version’s full definition instead of listing. This is CLI-only and read-only — there’s no app panel for it and no way to restore an old version. See Version history and going back for what counts as a change and what the version number actually means.
Completing a task early
Normally a task walks its full pipeline — plan, code, review, and so on — one stage at a time until it reaches done. But sometimes the work is already finished, or the rest of the pipeline no longer applies, before the task gets there on its own. In that case you can move the task straight to done from whatever stage it’s currently on, without walking the remaining stages.
Done vs. cancelled: these are the two ways a task’s active work ends, and they mean different things. Use done when the work was completed successfully — even if it finished early, outside the normal pipeline order. Use cancelled when the task is being abandoned without completing the work; cancelling always requires a reason (see below). Picking the right one keeps your task history honest about what actually happened.
Completing early doesn’t skip the checks that normally gate a close: the current stage’s required field must still be filled, every item on the task’s acceptance-criteria checklist must still be ticked — including one you deferred to a later stage — and any child tasks must still be finished or cancelled first. If one of those is unmet, the jump to done is rejected the same way a normal advance to done would be.
From the web app
Find the status dropdown — it’s on the task’s own page, on each row of the Tasks list, and on each card on the board view — and select → Done.
From the CLI
ch task status <taskId> done
No --reason is required to complete a task — unlike cancelling, below.
Cancelling a task
From the web app
Open the task, find the status dropdown, and select → Cancelled. A Reason required dialog appears — enter a brief explanation and click Change status. The reason is posted as a comment on the task so there is a record of why the work was stopped.
From the CLI
ch task status <taskId> cancelled --reason "Superseded by task 019abc"
The --reason flag is required when cancelling — the server rejects the request without one. Keep the reason short and specific so future readers understand why the work stopped.
Cancelled is a terminal status: the task is no longer active and agents will not pick it up. There’s no further status to move it to by hand — the dropdown on a cancelled task is disabled. To pick the work back up, reopen it instead (see below).
Archiving a finished task
Archiving is how you move a finished task out of the way for record-keeping. The default Tasks list and board view already hide finished tasks, so archiving doesn’t change what you see there — it just marks the task done-and-filed. ch task list --all (and the equivalent filters in the web app) still include an archived task, and opening it directly always works.
For a story, feature, bug, or task, archiving is reachable only from done — the task still working its way through review, merge, or verify has to finish first. Initiatives and epics can also archive from in_progress, since that’s their own normal holding stage. There’s no separate archive command or button: you archive a task the same way you change any other status.
From the web app
Find the status dropdown and select → Archived.
From the CLI
ch task status <taskId> archived
Archiving is reversible — see below.
Reopening a cancelled or completed task
ch task reopen sends a done, cancelled, or archived task back to an earlier stage in its pipeline. An agent can then pick up the work instead of you starting over. It also works on a task that’s still active, along any backward edge — for the common case of sending a task at review or verify back to code for another build round, see Sending work back for rework in Reviewing an agent’s work.
ch task reopen <taskId> <stage> --reason "why this needs another pass"
Use --reason-file <path> for a longer explanation. The reason is required — the command refuses without one. CodeHerder stores it as a comment on the task, and the next agent reads it as part of its brief.
Only a human can run this — an agent that tries gets a permission error. The web app has its own control too: open the task and check the More menu next to the status dropdown. On a finished task it reads Reopen…; on a task still active at an earlier stage it reads Send back… — both open a panel with a stage picker and a required Reason field, and both warn that output fields from the target stage onward are cleared for a fresh attempt (their old values are kept as versions, and a required checklist comes back unticked with its text intact).
Which stages you can reach depends on the task’s type:
- Story, feature, or bug — any of
todo,plan,code,review,merge,verify. - The lightweight task type —
todo,code, ormerge. - Initiative or epic —
todo,planning, orin_progress.in_progressis a genuine target here: it’s that pipeline’s normal holding stage, so the container returns to “children are being worked” and closes again once they finish. See How work flows for the container pipeline.
You’re not limited to the stage immediately before the one the task finished at. A story that reached verify before it closed can reopen straight back to plan. Run ch task show <taskId> and read its reopen targets row for the exact set this task can reach right now — that’s the same list the server checks. Naming a stage the task can’t reach is rejected, and the error names the ones that are.
A task stranded at in_progress
For a story, feature, bug, or task, in_progress is not a stage in that type’s pipeline — it’s the holding stage for initiatives and epics while their children work, and CodeHerder refuses to move one of these four types there directly. If you’re looking at a task that got left at in_progress some other way, reopen it straight into a real stage:
ch task reopen <taskId> code --reason "picking this back up"
There’s no need to cancel it first. Blocked has its own way out — unblocking a task returns it to the stage it was working when the blocker was filed, so it never needs this treatment.
If a finished story, feature, bug, or task needs more work
Reopen it when the work is a continuation of the same task — that’s exactly what the command above is for. File a new task only when the follow-up is genuinely separate work, not as a stand-in for reopening:
ch task create --title "Follow-up: …" --type story --parent <parentId>
and reference the original task in the new one’s description. If you want to record that the follow-up builds on the original, --depends-on against a cancelled or archived original is safe to use — a dependency clears once its upstream finishes, no matter how, so it won’t hold the follow-up back. It just won’t gate anything either, since the original is already finished. See Task dependencies for the full model.
If the task hasn’t reached a terminal status yet — it’s still sitting at review or verify — send it back for another build round instead of letting it finish and reopening it afterward:
ch task status <taskId> code --reason "…"
See Sending work back for rework in Reviewing an agent’s work.
For the full list of pipeline stages and how tasks move between them, see How work flows. To route or assign an active task to a specific agent or person, see Assigning and claiming work.
Last updated