CodeHerderSearch⌘KRequest access →

The stage library

Browse and author the shared stages your workflows are built from, and compose a workflow's pipeline from them instead of writing one by hand.

A workflow’s pipeline can be built from stages in your workspace’s shared stage library: reusable definitions of what a stage does, referenced by any workflow composed from the library. A stage carries the prompt an agent gets when it works there, whether it can commit code, which capabilities an agent needs to run it, and its verifications — one of which can name a rubric for stage judging to grade the attempt against. Every built-in type your workspace starts with is already composed this way, so the prompt its code stage runs is the library’s code stage — change it there and every type pointing at it picks the change up. A type can also be written out by hand instead, with each stage’s settings stored on the type directly; see Customising workflows for how to tell which kind you’re looking at.

This page covers the library itself — browsing it, customising a stage, and seeing what depends on one — and how to build a workflow’s pipeline out of library stages instead of writing every stage’s settings by hand. If you just want to reorder or edit a type’s existing pipeline, start with Customising workflows instead; this page is about the shared building blocks underneath it.

You can work with the library either from Settings → Stages in the left sidebar or from the ch CLI. This page mostly shows the CLI, since it’s the fuller surface, but most examples below have a web equivalent — the one exception is per-workflow verification overrides, covered below, which today is CLI-only.

Browsing the library

ch stage list

This lists the library as it resolves for your workspace: your own stages plus every built-in and inherited one, one row per key. Each row shows the stage’s key, label, owner (builtin, workspace, or inherited from <workspace>), whether it’s enabled, its version number, whether it’s agent-writable, and any required capabilities.

CodeHerder ships with a set of built-in stages — code, review, merge, verify, and others named after the stages a typical pipeline has — available for any workflow you compose from the library. Add --disabled to see only the ones you’ve turned off, or --enabled for the reverse — see Listing enabled and disabled records for the shared grammar behind those two flags.

To see one stage in full — its prompt, declared fields, required capabilities, writable setting, container image, and verifications:

ch stage show code

Settings → Stages shows the same list, with a search box and an All / Enabled / Disabled picker in place of --enabled/--disabled. Click any row to open that stage’s own page, which shows everything ch stage show prints.

Customising a stage

A built-in stage is read-only: you can’t edit code or review directly. To change one, create your own entry at the same key. That local entry shadows the built-in for your workspace and everything nested under it — any workflow you compose from that key, now or later, resolves to your version instead.

ch stage create code --stage-file my-code-stage.json

The file is the stage’s document — its label, spec (prompt, writable, required capabilities, image, and so on), and any fields it declares. --change-note records an optional note against the new version.

On a built-in stage’s page in Settings → Stages, the same move is a Customize button, which opens a form for the same document instead of a file.

An inherited stage — one your workspace picked up from a parent — is read-only too, but for a different reason: it’s only editable from the workspace that actually owns it. ch stage list marks it inherited from <workspace> so you know where to go; its page in Settings → Stages shows the same thing and names the workspace to edit it from.

Changing an existing stage

ch stage edit is a sparse patch: send just the key you want to change (label, spec, or fields) and CodeHerder merges it into what’s already there — you don’t have to restate everything. The safe way to build that document is a show → edit → apply round-trip:

ch stage show my_review --json | jq '.data | del(.enabled, .disabled)' > stage.json
# edit stage.json
ch stage edit my_review --stage-file stage.json

show always includes enabled, so strip it before editing — ch stage edit doesn’t accept enabled/disabled at all; use ch stage enable/ch stage disable for that instead, covered below. In Settings → Stages, the equivalent round-trip is a form: open the stage’s page and click Edit.

Saving a stage carries the change straight to every workflow in your workspace that composes a pipeline from it. Every workflow that references my_review is automatically rebuilt with the new version, in the same save — you don’t need to re-apply anything. Two things worth knowing about that:

  • It only reaches workflows owned by your own workspace. If a descendant workspace inherited the stage and built one of its own workflows from it, your save doesn’t touch that workflow — the descendant workspace has to rebuild it there itself.
  • CodeHerder checks the rebuild before it commits to anything. Say your change would break a workflow that references the stage — a route_to the new version no longer declares a matching verification for, for instance. The whole save is refused, nothing is saved (not even the stage itself), and the message names which workflows couldn’t be rebuilt and why. A stage referenced by an unusually large number of workflows is refused outright rather than rebuilding all of them at once. And a workflow whose rebuilt pipeline comes out identical to what it already had gets no new version — nothing changed, so nothing is recorded.

Whenever a write leaves a stage agent-writable or requiring capabilities, CodeHerder prints a note naming ch stage references <key> — on create, edit, enable, and disable alike, regardless of whether that particular write actually changed it. Because saves now propagate, that note is a live warning, not a heads-up for later: every workflow it lists picks up the new writable/capability setting from this same save. The web editor shows the same warning inline, listing the affected workflows, before you click Save.

Want to see the effect before you commit to it? Preview it:

ch stage impact my_review --stage-file stage.json

This runs the exact same check edit would, without saving anything — which workflows would be rebuilt and to what version, which would block the save and why, and which belong to descendant workspaces and will only pick the change up on their own next save. Drop --stage-file to preview the stage exactly as it stands today, which is also a way to check a pending disable before you run it. In Settings → Stages, this preview runs automatically while you edit — no separate command needed.

ch stage impact also reports what your edit does to every workflow’s own verification overrides on this stage: which overrides each workflow currently holds, which verifications your edit newly adds (split into whether a workflow’s own override already covers that id, or whether it will simply inherit the new default), and — this is the one that blocks the save — any override that names a verification your edit is about to remove. CodeHerder refuses to strand a workflow’s override on a verification that no longer exists, so a dangling override in the preview means the save won’t go through until you either keep that verification or clear the override first. The preview also lists any workflow in your workspace with no library instance of this stage at all — a heads-up that it won’t pick up whatever you’re about to ship.

The preview can also report stale variants: workspaces holding their own copy of a built-in type that includes this stage, instead of tracking the shared definition. Your edit doesn’t reach those workspaces, because their copy no longer references this shared stage at all. Catching up is opt-in per workspace, from that workspace’s own adopt-shared — see Go back to the shared definition.

Seeing who depends on a stage

ch stage references my_review

This lists every workflow whose pipeline currently references the stage, and the instance name each one gave it. Check this before editing a shared stage’s privilege settings, or before disabling one — or use the impact preview above, which shows the same references alongside what a specific change would do to each. Settings → Stages shows the same reference list on a stage’s own page.

Disabling a stage

ch stage disable my_review
ch stage enable my_review

There’s no ch stage delete. If you type it, the CLI answers Did you mean: ch stage disable ?. CodeHerder never hard-deletes a stage. Disabling is refused while a workflow still references the stage, and the error lists which ones — clear them out first, or leave the stage enabled. The one exception: if your key also shadows a built-in, disabling it is allowed, because the key still resolves — it just falls back to the built-in’s definition, so nothing referencing it is left dangling.

Version history

ch stage versions my_review
ch stage versions my_review --version 2

Every save adds a new version, with a timestamp, who made it, and any change note. Add --limit and --cursor to page through a long history. History only exists for a stage your workspace authored itself. Run this against a built-in or an inherited key and CodeHerder tells you it isn’t owned here — for an inherited key, check ch stage list to see which workspace it’s inherited from and ask there; a built-in has no owning workspace and no version history to look up anywhere. A workspace-owned stage’s page in Settings → Stages shows the same Version history list.

The Change column sums up each save. The app’s history lets you view and compare versions. To go back, use the CLI:

ch stage revert my_review 2
ch stage revert my_review 2 --change-note "Undo the prompt rewrite"

A revert writes a new version with version 2’s label, settings and fields. Every earlier version stays in the history. It doesn’t change whether the stage is enabled or disabled. Use ch stage enable or ch stage disable for that. Reverting needs an owner or admin. Add --expected-version <n> to make the revert fail if someone saved a newer version in the meantime. See Version history and going back for how that compares with the other things CodeHerder keeps a history of.

Composing a workflow from the library

A workflow’s pipeline doesn’t have to be written out stage-setting by stage-setting. Instead, you can define it as an ordered list of instances, each pointing at a library stage:

ch workspace workflow edit hotfix --from-file composition.json

composition.json looks like this — the same file the edit command already accepts, just with a top-level instances list instead of a flattened schema:

{
  "key": "hotfix",
  "label": "Hotfix",
  "instances": [
    { "name": "todo", "stage": "todo", "class": "todo" },
    { "name": "quick_fix", "stage": "code", "class": "doing", "to": ["double_check"] },
    { "name": "double_check", "stage": "review", "class": "doing", "to": ["done", "quick_fix"] },
    { "name": "done", "stage": "done", "class": "done", "terminal": "success" }
  ]
}

Each instance’s name is the stage name the task actually sits in (what shows up in ch task status); its stage is the library key supplying that instance’s content — its prompt, whether it’s writable, and so on. They don’t have to match: quick_fix above gets its content from the code stage, and double_check from review. That’s also how two instances in the same pipeline can reuse one stage under two different names.

class, to, terminal, and a verification’s route_to — plus gate, escalate_after_failures, and max_reject_loops for a reject loop, and an optional skills list narrowing that instance’s own sessions (see Which skills a stage gets) — stay per-instance, because they describe how this workflow uses a stage, not the stage’s own shared definition; the same stage can be a plain forward step in one type and a reject-loop target in another.

CodeHerder checks a composition document in this order, and reports the first problem it finds:

  1. Two instances can’t share a name.
  2. Every instance’s stage has to resolve to a stage in your library — and to an enabled one; a key that only matches a disabled entry is treated the same as a missing one.
  3. A route_to on a verification has to name a verification the resolved stage actually declares.
  4. A max_reject_loops cap is rejected if nothing ever routes back to that instance — there’d be nothing for it to cap.

Once it’s applied, ch workspace workflow show prints the type’s pipeline back with each instance annotated by the stage and owner it came from, under instances (composed from the stage library).

If you’d rather write the flattened schema by hand instead — every stage’s settings spelled out directly, the way Customising workflows describes — --from-file still accepts that format too. Use whichever fits how you think about the type; CodeHerder tells the two apart by whether the file’s top level has instances or stages. A file with both is rejected, so it doesn’t have to guess which one you meant.

In Settings → Workflows, a type’s Stages section lets you build the same kind of pipeline: add instances that each point at a library stage, instead of authoring every setting by hand. A type that’s already composed — every built-in one is — shows its instances there straight away. A type written out by hand doesn’t switch over on its own: click Convert to composition… on its editor first. It tells you up front what that changes (each stage’s content will come from the library stage you pick, replacing what was stored on the type directly) before anything is actually written.

A composed type’s editor looks a little different from a hand-written type’s. Each stage instance shows a read-only preview of the library stage’s required capabilities and prompt, plus an Edit library stage link that takes you straight to that stage’s page here to change them. What stays editable per instance is its name, class, gate, approval rules, verification routing, transitions, escalation settings, and — from the CLI only, today — a configuration overlay turning individual verifications on or off for this workflow; see Changing a stage’s verifications for one workflow below. All of it is about how this workflow uses the stage, not the stage’s own shared content.

An admin composing a type’s pipeline this way isn’t the only place a composition happens. An auto task’s own agent selects and orders stages from this same library at run time, for that one task — see Auto tasks — the agent chooses the workflow. Either way, a workspace’s composition policy (a floor of required stages, a ceiling on model tiers, or both) applies the same checks to what comes out.

Changing a stage’s verifications for one workflow

Saving a shared stage carries the change to every workflow that composes from it — that’s the point of the library. But sometimes one workflow genuinely needs to differ: turn a verification off for a lightweight type, or make one that’s optional on the shared stage required here. Rather than forking the whole stage into a copy just to flip one setting, a composed instance can carry a small configuration overlay that adjusts individual verifications for that workflow alone, everything else still coming straight from the library.

The overlay lives on the instance itself, in the same composition document you already use to build a pipeline:

{
  "name": "quick_fix",
  "stage": "code",
  "class": "doing",
  "to": ["double_check"],
  "config": {
    "verifications": {
      "lint": { "enabled": false },
      "tests": { "required": true }
    }
  }
}

config.verifications is a map keyed by the verification’s own id — the same ids ch stage show lists for that stage. Each entry can set enabled, required, or both:

  • Leave a key out of the entry, or leave the whole config out — that facet stays inherited: this instance uses whatever the library stage itself declares, and picks up the library stage’s own future changes automatically.
  • Set enabled: false — this workflow drops that verification entirely. It never runs here, and the advance gate never counts it. Every other workflow composing from the same stage keeps running it.
  • Set required on its own — the verification still runs, just with a different pass/fail requirement for this workflow than the stage’s own default.

CodeHerder refuses two shapes outright, rather than guessing what you meant:

  • An id your resolved stage doesn’t declare a verification for. You can only override a verification that actually exists on the stage this instance points at.
  • An entry that sets neither enabled nor required — an empty {} is indistinguishable from a typo, so it’s rejected rather than silently doing nothing.

Resetting a verification back to inherit uses the same spelling either way: set its entry to null, or drop the key from config.verifications altogether. Saving a composition is a whole-document write, so simply leaving a key out is enough — you don’t need to explicitly restore the library’s values, CodeHerder does it as soon as your own entry is gone.

Reading what’s actually in effect: ch workspace workflow show prints each instance’s verifications with a source next to every setting:

verification lint                  enabled=no (overridden)   required=yes (inherited)
verification tests                 enabled=yes (inherited)   required=yes (overridden)  kind=command

(inherited) means that facet is coming from the library stage as it stands today; (overridden) means this workflow’s own overlay set it. A disabled verification prints no kind, since CodeHerder drops it from the workflow’s actual runtime spec entirely — there’s nothing left to describe. The same command also prints the reset spelling above, so you don’t have to remember it.

Before you edit a shared stage, ch stage impact (see above) also reports this overlay’s effect. It names which referencing workflows carry an override, and which override your pending edit would leave dangling — one that names a verification your edit is about to remove. It also splits the stage’s newly added verifications: some a workflow will simply inherit, others its own override already covers. A dangling override blocks the save until you resolve it, one way or the other.

There’s no web control for this overlay yet, and no web readout of the inherited/overridden split either — both are CLI-only today. The composed editor’s Edit library stage link still takes you to the shared stage itself; setting a per-workflow override needs the ch workspace workflow edit round-trip described above.

Moving a forked stage onto a shared stage, with an overlay

A workflow may have forked its own copy of a stage in the past, or been hand-written from the start. If you’d rather it composed from the shared library instead — keeping the one or two settings that made it different — ch workspace workflow preview-migrate-stages and ch workspace workflow migrate-stages do that move in one step. Each re-points one or more instances onto a library stage plus a configuration overlay, without hand-editing the whole composition document.

Always preview first — it writes nothing:

ch workspace workflow preview-migrate-stages --from-file migration.json

migration.json names the instances to move, each with the library stage to point at and, optionally, the overlay to apply:

{
  "instances": [
    { "typeKey": "hotfix", "instanceName": "quick_fix", "stage": "code", "config": { "verifications": { "lint": { "enabled": false } } } }
  ]
}

The preview shows a before/after diff of what each addressed instance would actually resolve to, and marks any workflow it can’t recompose as blocked, naming why. Once you’re happy with it, run the identical file through ch workspace workflow migrate-stages to apply it — it mints one new workflow version per affected workflow and prints a rollback record alongside it. If any addressed workflow would come out blocked, the whole run is refused before anything is written, so you never end up with some workflows migrated and others not.

Who can do what

Anyone in the workspace — human or agent — can read the library: list, show, versions, and references are all open to any member.

Previewing a change with ch stage impact (or the web editor’s live preview while you edit) needs a workspace owner or admin, the same as making the change would — but not necessarily a person. It saves nothing, so an agent with that role can run it too.

Actually writing to the library is narrower still. create, edit, enable, and disable need a workspace owner or admin, and they have to be a person — an agent’s own credentials are refused, even for an agent with an admin role. Writing also requires the Starter plan or above, the same plan floor as customising a workflow.

Settings → Stages carries the same rules: anyone can browse, but the create/edit/disable controls only appear active for an owner or admin — everyone else sees a read-only view with a note explaining why.

Last updated

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch the work move. Costs update as it happens.

Try "pricing", "connect a device", or "who reviews the code"

↑↓ move · ↵ open · esc close