Capabilities
What a capability label is, and the places it controls routing and requirements.
The word “capability” shows up in several places in CodeHerder — on agents, on launch configs, on workflow stages, and on tasks and schedules — and the one rule underneath all of them is: capabilities is what a thing HAS; a “required capabilities” field is what a thing NEEDS. Agents, launch configs, and devices all carry a capabilities set (what they can do); tasks, schedules, and workflow stages carry a required-capabilities set (what they need). This page is the single place to check which rule applies where.
What a capability is
A capability is a short, free-form label string. You choose the labels to match your own conventions — common examples are go, frontend, or op.review. One convention is built in: model:<tier>, where the tier is haiku, sonnet, or opus, in increasing order of capability and cost. Use model:<tier> labels to describe which AI model an agent should run, and plain skill labels for anything else you want to track. For the rest of the model story — naming an exact model, restricting a config to an Allowed models list, and cost-aware routing — see Which model your agents run.
The two questions capabilities answer
Every place capabilities appear is answering one of two questions, with one exception covered below:
- What can this agent do? — a capability declared on an agent or a launch config, describing a skill it carries.
- What does this work need? — a capability declared on a task, a schedule, or a workflow stage, describing a requirement.
The rule that matters is this: skill labels on an agent only ever rank — everything else, including a task’s or a schedule’s own required capabilities, is a hard gate. The sections below walk through each place in turn; the table at the end summarizes all of them.
Agent skill labels — soft
Set with ch agent create --cap <label> (repeat the flag for more than one), or edit afterward. These are the skill labels shown on the agent itself.
ch agent create --display-name "Go coder" --cap model:sonnet --cap go
When the engine auto-staffs an unassigned task, it ranks available agents by how much their skill labels overlap with the task’s required capabilities — more overlap means higher priority. A partial or even zero overlap never blocks placement: the task still runs on the best available agent, even a plain skill miss. Skill labels are a ranking signal only.
Launch-config capabilities — hard
Set with ch agent config edit <agent-id> --cap <label> (or ch agent config create for an additional config). This is a different set of labels, stored on the agent’s launch config, not on the agent itself — a SUPPLY set, exactly like the agent’s own skill labels above, just scoped to what this one config can run. Passing --cap replaces the config’s whole capability list rather than adding to it, so include every capability you want the config to keep on that call — every other field on the config (command, args, environment, persona, and so on) stays as it was, since config edit only changes the flags you actually pass:
ch agent config edit <agent-id> --harness claude --cap model:opus --cap model:sonnet
You can set this same list when you create the agent, in the same call — spelled --config-cap there instead of --cap, since ch agent create already uses --cap for the agent’s own skill labels above:
ch agent create --display-name "Builder" --harness claude --config-cap model:opus --config-cap model:sonnet
See Creating a runnable agent in one call for the full flag set this unlocks.
The engine runs a workflow stage only on a launch config whose capabilities fully cover what that stage requires. If no launch config in your workspace carries a capability a stage requires, that stage is unstaffable — any task reaching it queues indefinitely, with no agent ever picking it up. This is a hard gate, not a preference.
Covering a whole workflow. Different stages of a workflow can require different capabilities — the built-in feature, story, and bug types need both the model:opus and model:sonnet tiers across their stages. A single launch config needs every tier the workflow uses to carry a task through it end to end; split across configs (one per tier) works too, since the engine picks whichever enabled config covers a given stage. See Agents and the CLI for the setup walkthrough.
For the full walkthrough of launch configs — including multiple configs per agent and how the engine picks between them — see Agents and the CLI.
Stage required capabilities — hard
Set on the stage’s own content — the shared library stage for a composed workflow, which every built-in type is, or the type’s own schema for a hand-written one, edited through the CLI. For a composed workflow the editor shows the list read-only, alongside a link to edit it at the library stage. A stage’s required capabilities are the list a launch config must fully cover before the engine will run that stage on it — the other side of the launch-config gate above. See Customising workflows for the full explanation of where each kind of workflow keeps this setting.
Agent device requirements — hard
Set with ch agent edit <agent> --device-requirement <token> (repeat for more than one), or --clear-device-requirements to remove them all. This is a fifth place capabilities appear, and it answers a different question from the other four: not “which work should this agent get” but which machine is this agent allowed to run on at all — hard, like the launch-config and stage gates above.
ch agent edit "Builder" --device-requirement bin:claude
CodeHerder only places a session for this agent on a device that reports every token the agent requires — a device missing one is skipped for that agent’s work entirely, the same hard-gate behaviour as a launch-config capability. See Editing an agent for the full agent edit flag set.
Task and schedule required capabilities — hard
Set with ch task create --cap <label> or ch schedule create --cap <label> (repeat for more than one). These describe what the task, or the tasks a schedule creates, actually needs.
ch task create --title "Write Go migration" --type story --cap go --cap model:opus
This is a hard gate, not a ranking signal: only an agent whose launch config covers every one of these capabilities can ever run the task, and the set is combined with each workflow stage’s own required capabilities as the task moves through it. A capability no launch config in your workspace covers strands the task at that stage indefinitely — there is no fallback to “best available agent” the way there is for agent skill labels. See Writing tasks an agent can build for the rest of task creation.
Two different passes over the same labels. It’s easy to conflate this with agent skill labels above because both compare a task’s required capabilities against an agent’s, but they run at different times for different reasons. First, when the engine is deciding which agent to auto-staff onto an unassigned task, it ranks candidates by skill-label overlap — that pass is soft, and zero overlap never blocks it. Separately, once an agent is about to actually run a stage, the engine checks whether that agent’s launch config covers every required capability — that pass is hard, and a miss refuses to start the stage at all. A task can clear the first pass (get assigned to an agent) and still stall forever at the second.
Repairing a stranded task. If a task was created with a capability typo’d or no longer covered by any launch config, fix it in place rather than recreating it:
ch task edit <taskId> --cap <label>...
ch task edit <taskId> --clear-caps
--cap replaces the task’s whole required-capabilities set; --clear-caps empties it. The two are mutually exclusive on a single call. A schedule works the same way: ch schedule edit <id> --cap <label>... replaces the set, and ch schedule edit <id> --clear-caps empties it. Creating or editing a schedule with a capability set runs the same launch-config coverage check as a task — see the caveats below.
Soft vs. hard, at a glance
| Where it’s set | Command | Effect |
|---|---|---|
| Agent | ch agent create --cap <label> |
Soft — ranks agents during auto-staffing; never blocks |
| Launch config | ch agent config edit <agent-id> --cap <label> |
Hard — a stage only runs on a config whose caps cover its requirements |
| Workflow stage | Library stage for a composed workflow, type schema (CLI) for a hand-written one | Hard — the requirement side of the launch-config gate above |
| Agent device requirement | ch agent edit <agent> --device-requirement <token> |
Hard — restricts which devices the agent’s sessions can be placed on |
| Task / schedule | ch task create --cap <label> / ch schedule create --cap <label> |
Hard — only a launch config covering every token can run the task; combined with the stage’s own caps |
Setting a required capability on a task, or on a launch config, needs the Starter plan — skill labels are free on every plan. See Plans and limits.
When capabilities stall a task
A task that queues without an agent ever picking it up has two possible causes:
- A stage’s required capabilities go uncovered by any launch config. The web app’s Capability gap callout on the Set up → Agents page lists each stage that no enabled launch config covers, with the capabilities it needs. A task can still stall for the other reason below.
- The task’s own required capabilities aren’t covered by any launch config. This is checked once up front — creating or editing a task with
--capis rejected outright if no launch config in your workspace covers the set, once your workspace has at least one launch config configured. But that check only looks at the task’s own set: a stage’s required capabilities are combined into the same set as the task moves through the workflow, so a task that passed the check at creation can still stall later once it reaches a stage whose own requirements push the combined set past what any launch config covers.
How you see it. A task carrying required capabilities no launch config can ever satisfy shows a Start refused badge next to its title (task list and task detail) and a Start refusal row on the task detail page, giving the exact reason and how many times the engine has retried; ch task show <taskId> prints the same thing as a start refusal row, and --json carries it as startRefusal.
To fix it, either add the missing capability to a launch config (see Agents and the CLI), or repair the task itself with ch task edit <taskId> --cap <label>... or ch task edit <taskId> --clear-caps.
For the full diagnostic walkthrough, see Why isn’t my task moving?; for reading the Capability gap callout itself, see Monitoring your agents. To see which capability tokens your fleet actually advertises, and which stages are covered because of it, see Staffing coverage.
Related guides
- Agents and the CLI — creating agents, launch configs, and how the engine selects between them
- Which model your agents run — naming an exact model, Allowed models, and cost-aware routing
- Customising workflows — setting a stage’s required capabilities
- Writing tasks an agent can build — setting a task’s required capabilities
- Monitoring your agents — reading the Capability gap callout
- Why isn’t my task moving? — diagnosing a task stuck on a capability mismatch
- Core concepts — the full object model
Last updated