Customising a task's workflow
Inspect any task's effective pipeline, and either give one task a different workflow than its type's default (owner/admin) or propose a change to the whole type for review (any member).
Every task runs on a pipeline defined by its type — the ordered stages it moves through from creation to completion. There are two ways to change that pipeline: a workflow override changes one task, right now, and is owner/admin only; a workflow proposal suggests a change to the whole task type and goes to an owner or admin for review before it applies to anyone. This page covers both, plus how to inspect the workflow that is actually governing a task.
Inspecting a task’s effective workflow
Any workspace member can see the pipeline that is actually governing a task:
ch workflow show <taskId>
This prints the task’s full effective workflow as formatted JSON: every stage in order, along with any gate and settings for each stage. When a per-task override is active, the output reflects the override, not the workspace type default. When there is no override, the output reflects the current workspace type schema. Either way, the output looks the same shape — it does not itself flag whether what you’re looking at is an override or the type default.
To tell whether a task is running on an override, look at the task detail page in the web app: a task with an override in place shows a Custom workflow badge next to its status. See The web app view below.
Use ch workflow show before setting an override to understand the starting point, and after setting one to confirm the result.
Setting a workflow override (owner and admin only)
Workflow overrides are owner and admin only. Regular members — including agents — can read the effective workflow with ch workflow show but cannot set or clear overrides.
Capture, edit, and apply
The recommended flow is to start from the task’s current effective workflow, edit it, and apply the result:
# 1. Capture the current effective workflow to a file
ch workflow show <taskId> > override.json
# 2. Open the file in your editor and change the stage list
# (reorder stages, or add or remove a stage)
# 3. Apply the edited schema as a per-task override
ch workflow override set <taskId> --from-file override.json
The file you pass to --from-file must be valid JSON in the same schema format that ch workflow show outputs. You can also pass - instead of a filename to read from stdin.
An override has to genuinely change the task’s stage list — reordering, adding, or removing a stage. If you submit the same stages, in the same order, as the type’s current pipeline, CodeHerder rejects it with an override_redundant error: an override that doesn’t change the stage shape only hides the task from future type-level edits behind a misleading badge. If you only want to adjust a gate or a setting without changing which stages run, that isn’t a per-task override — change it on the task type instead (see Customising task types and workflows), or propose the change for review if you want it reviewed first.
Once applied, the task uses the override for every subsequent stage transition. The change takes effect immediately — the task does not need to be restarted.
What CodeHerder checks before it accepts your schema
Whether you’re setting an override or submitting a proposal, CodeHerder validates the schema before accepting it, and rejects a malformed one before anything is saved. The rules that matter when hand-editing the JSON ch workflow show gives you:
- Every stage name must start with a lowercase letter and contain only lowercase letters, digits, and underscores, up to 64 characters, and can’t appear twice in the stages list.
- Every stage in the stages list needs an entry in
status_classesmarking ittodo,doing, ordone. - A stage name that appears anywhere else in the file has to keep matching the stages list: its
status_classesentry, itsstage_gatesentry, itsterminal_outcomesentry, itsstage_specsentry, and anytransitionsentry that names it as a source or as a target. (A stage gate’s required fields must also be fields the task type actually declares.) In practice: when you add a stage, give it a class — and astage_specsentry, if it’s an agent stage — in the same edit; when you remove a stage, remove every one of those entries for it too. An orphaned reference in any of these is rejected on its own, independently of the others. - The classes have to describe a real pipeline: the first stage must be class
todo, the last must be classdone, there must be at least one stage of each class, and classes can’t move backwards as you read down the stages list (everytodostage before everydoingstage before everydonestage). This applies to reorders as well as additions — swapping two stages of different classes can break it. - If a stage has a verification that routes a failure back to an earlier stage (see Stage verifications in How work flows), that earlier stage has to stay earlier in the stages list than the stage itself. Reordering stages can break this the same way a reorder can break the class order above.
- A workflow proposal’s stages list can’t be empty — it always has to declare at least one stage.
If the schema fails any of these checks, fix the JSON and re-submit — nothing is saved until it passes.
Stage-in-use: when the task is already mid-flight
If the task is currently sitting in a stage that does not exist in the new schema, CodeHerder rejects the request with a stage_in_use error and tells you which stage needs to be mapped:
workflow override: the task's current stage is not in the new schema; supply stageMigrations to remap it
affected task:
<taskId> (stage: <current-stage>)
re-run with --migrate flag for the listed stage:
--migrate <current-stage>=<target-stage>
Resolve it by adding a --migrate flag that tells CodeHerder which stage in the new schema the task should move to:
ch workflow override set <taskId> --from-file override.json \
--migrate <current-stage>=<target-stage>
A per-task override only ever needs one mapping — for the task’s own current stage — so pass --migrate once. The target you give must itself be a stage in the schema you’re applying; if it isn’t, CodeHerder rejects the request with an invalid_stage_migration error naming the stage that doesn’t exist. Once the mapping is accepted, the task is atomically moved to the target stage at the same time the override is written, so the task is never left in an inconsistent state.
Removing a workflow override (owner and admin only)
To remove a per-task override and return the task to the workspace type default:
ch workflow override clear <taskId>
If the task’s current stage does not exist in the workspace type’s live schema — for example, because the type schema was updated after the override was set — CodeHerder rejects the request with the same stage_in_use error (worded for the catalog schema this time: “not in the live catalog schema”) and the same fix applies — pass one --migrate mapping for the task’s current stage:
ch workflow override clear <taskId> --migrate <current-stage>=<target-stage>
After clearing, the task immediately follows the workspace type’s live schema.
Proposing a workflow change for review
A workflow override only ever affects the one task it’s set on. If you think the whole task type’s pipeline should change — for every future task of that type, not just this one — submit a workflow proposal instead. Unlike an override, a proposal is not applied automatically: it goes to the workspace’s Feedback inbox, where an owner or admin reviews it and decides whether to apply it.
Any member of the workspace can submit a proposal, using the same capture-and-edit flow as an override:
# 1. Capture the current workflow as a starting point
ch workflow show <taskId> > proposal.json
# 2. Edit proposal.json — reorder or add a stage, adjust a gate, etc.
# 3. Write your reasoning to a file — why this change, what problem it fixes
# 4. Submit the proposal for review
ch workflow propose <taskId> --schema-file proposal.json --rationale-file rationale.md
Both --schema-file and --rationale-file are required, and each reads a path or - for stdin — never an inline argument. proposal.json must be valid JSON in the same schema format ch workflow show outputs; a malformed schema is rejected before it reaches review. On success, CodeHerder prints the id of the new proposal.
Submitting a proposal does not change anything by itself. It lands in that workspace’s Feedback inbox, in the Workflow proposals section, where an owner or admin reviews the diff and either Applies it (updating the task type’s schema for every future and in-flight task of that type) or Rejects it. See Feedback inbox for what reviewers see and do.
Two things worth knowing about Apply:
- If the proposal removes a stage that a live (non-terminal) task of that type currently sits in, Apply is refused until those tasks are moved off that stage — the reviewer needs to migrate them from Settings → Task types first.
- A task that has its own per-task override is unaffected by an applied proposal: its override keeps governing it, even after the type’s catalog schema changes. Clear the override first if you want that task to pick up the newly-applied type schema.
Use a proposal when the change should apply to the whole task type and you want a human to sign off first. Use an override when only this one task needs a different pipeline right now.
The web app view
When a per-task workflow override is active on a task, the task detail page shows a Custom workflow badge next to the task’s status. The badge itself is just a signal that this task is running on a custom pipeline — it doesn’t list the stages. Below it, a separate Custom workflow stages row shows the actual stage list the override defines.
Setting or clearing a per-task override, and submitting a proposal, are both CLI-only — there is no web UI for either. Reviewing a proposal (applying or rejecting it) is web-app only, from the Feedback inbox.
To change the default pipeline for all tasks of a given type without going through review, see Customising task types and workflows — editing a type in Settings → Task types updates every future (and in-flight) task of that type unless the task has a per-task override in place.
Related guides
- Feedback inbox — where workflow proposals are reviewed, applied, or rejected
- Customising task types and workflows — change a type’s default pipeline directly, without review
