The Placement report
Read the Placement report to see which devices could run a task or agent right now, and exactly why the others can't.
When nothing seems to be picking up a task, the fastest way to find out why is to ask CodeHerder directly. The Placement report answers one question — which of your devices could take this work right now, and why not the others — using the same checks the engine itself runs when it staffs a task, with one exception: it does not check device health (see What the report doesn’t check). Outside that one gap, nothing here is a guess: every eligible/ineligible verdict, and every reason given for it, is the actual decision the engine would make.
Placement answers this for one task or agent at a time. To see the same coverage question answered for every stage across your whole workspace at once, see Staffing coverage.
Where to find it
-
On a task’s detail page, the Placement panel.
-
On an agent’s detail page, the Placement panel.
-
From the CLI, for a task:
ch task placement <taskId> -
From the CLI, for an agent:
ch agent placement <agentId>Inside an agent’s own session, leave off
<agentId>to report on that agent.
The Placement panel on a task’s detail page always answers for the task alone, so it always shows the “Agent requirements not folded” note. To narrow the answer to one agent, use the CLI’s --agent flag (see Narrowing the answer with --agent below).
Reading the header
The CLI prints a header line before the device table:
fleet: candidates_available · requires: gpu(agent), bin:codex(harness)
fleet: is one of three states:
| State | What it means |
|---|---|
no_devices_linked |
No device is linked to this workspace at all. This never fixes itself — link a device (see Managing your devices). |
candidates_available |
At least one linked device is eligible right now. |
none_eligible |
Devices are linked, but none currently passes every check. This often clears on its own — a device reconnecting, a cooldown expiring, or a slot freeing up — so it’s worth a second look a few minutes later before you change anything. |
requires: lists every capability the work needs and where each one came from:
| Source | What it means |
|---|---|
agent |
Comes from the target agent’s own required capabilities. |
harness |
Comes from the coding-agent CLI the agent’s launch config uses. |
git-host |
The task’s stage needs to read or write the repo’s git host, so a device must be authenticated to that host. |
task-device |
A hard device requirement set directly on the task or its schedule. |
When the work needs nothing, requires: prints none. A git-host requirement can also name the repository it applies to, for example auth:github(git-host: <repo>).
Any report-level caveat (see Caveats below) prints on its own caveat: <name> line under the header. For git_host_auth_ungated, the line lists the hosts in parentheses:
caveat: git_host_auth_ungated (git.example.com)
The device table
Below the header, a table lists every device the report considered, with four columns:
| Column | What it shows |
|---|---|
| DEVICE | The device’s name. |
| ELIGIBLE | Whether this device could take the work right now (true/false). |
| REASON | If not eligible, the single decisive reason — see the table below. Prints an em dash (—) when eligible. |
| DETAIL | Whatever else is worth knowing: missing capabilities, other failing checks beyond the headline reason, any caveats (below), and whether the device is linked to this workspace at all. |
Eligible devices are listed first, then every device is sorted by name. The report caps the list at 500 devices; if your fleet is that large, it prints a truncation notice rather than silently dropping the rest.
The table also lists devices you own, or can otherwise see, that aren’t linked to this workspace — DETAIL carries “not linked to this workspace” for those rows, so you can spot a device you meant to link but haven’t.
The eight reasons, and what to do about each
| Reason | What it means | What to do |
|---|---|---|
device_offline |
The device has no active connection right now. | Start or restart ch device-server on that machine — see Managing your devices. |
device_disabled |
Someone paused the device. | Resume it with ch device enable <deviceId>, or Enable it in the web app — see Managing your devices. A cloud device draining ahead of an AWS reclaim shows this same reason and needs no action — it clears on its own; see If AWS reclaims the instance. |
device_not_workspace_linked |
The device isn’t linked to this workspace or an ancestor group. | Link the device to the workspace — see Managing your devices. |
device_excluded_by_workspace |
This workspace, or a group above it, turned the device off for itself. The device still serves other workspaces. | Run ch device enable <deviceId> --workspace <workspace> against the workspace or group that turned it off. See Turning a device off for one workspace. |
device_caps_mismatch |
The device doesn’t advertise a capability the work requires. | Find which of the four requirement sources asked for it (in the header’s requires: list), then either make the device advertise it (install the missing tool, or set it as a device-side token with ch device edit <deviceId> --cap <label>) or relax the requirement at its source. See Capabilities. |
device_sub_exhausted |
Every Claude credential on the device has hit a usage window or spend cap. | No action needed — it resumes automatically once a meter resets. Adding another Claude credential to the device keeps it eligible sooner. See AI usage limits and AI credentials on a device. |
device_provision_cooldown |
This task recently failed to provision on this device and is briefly avoiding it — only shows up in a task’s report. | Wait for the cooldown to expire, or try a different device. |
device_at_capacity |
The device has no free slot for new work. DETAIL carries caveats:run_cap_saturated when the cause is every running-session slot being in use; without that caveat, every sandbox slot is in use instead. |
Wait for a slot to free up, or raise the device’s concurrent-session limit — see Concurrency and capacity. |
An eligible device carries no reason at all. If a device fails more than one check, DETAIL lists the rest after also: — fixing only the headline reason won’t be enough on its own.
What the report doesn’t check
The report evaluates every gate the engine uses to staff a task, with one exception: device health. Every linked device row carries the health_unknown caveat below, because the report never reads a device’s Health snapshot.
This matters because the engine’s real staffing decision does check device health, and it checks that a device can authenticate to each repository the task’s stage needs. So a device can read eligible here and still not pick up the task, because it fails one of those two checks:
- Overall health — the device’s readiness rolls up to unhealthy. Check its Health snapshot; see Unhealthy device.
- Git credentials for one repo — the device is healthy overall, but its git credentials for a specific repository the stage needs are failing. This holds back only tasks that need that repository, not every task on the device. See the Git credentials row in Device health and readiness checks.
Caveats — gates that would otherwise be invisible
A caveat calls out something the header and table can’t otherwise show you. Some apply to the whole report:
link_state_unknown— the workspace-link check couldn’t run this pass, so every device was treated as linked. Re-run the report if a device you expect to see missing isn’t.capacity_unknown— the capacity check couldn’t run this pass, so every device reads as full. This is a temporary blip, not a genuinely saturated fleet — check again shortly.git_host_auth_ungated— the work needs to reach a self-hosted git host, and CodeHerder has no way to check that host’s authentication in advance, so this check is skipped by design. The report names the host so you know which one to double check yourself.agent_requirements_not_folded— you asked for a task’s report without naming an agent, so the agent’s own required capabilities aren’t part of the answer. See Narrowing the answer with--agentbelow.dependency_blocked— this task (task reports only) has an unmet upstream dependency. No device is offered the task while that holds, regardless of what the table below shows. See Unresolved dependency.
Others apply to one device, and show up in that row’s DETAIL after caveats::
run_cap_saturated— this device has no free running-session slot right now, so the report marks itdevice_at_capacityeven if it has free disk (sandbox) capacity. A slot frees on its own as a session finishes, or raise the device’s concurrent-session limit — see Concurrency and capacity.health_unknown— the report doesn’t check device health (see What the report doesn’t check above), so every linked device carries this caveat. It’s expected on every row, not a sign anything is wrong.secret_not_acknowledged— you named an agent, and every other check passed, but that agent would still fail to start on this device: its launch config references a secret (@secret:<key>) the device owner hasn’t acknowledged yet. This doesn’t cover a plain secret variable, only a launch-config secret reference. See Secrets on a device to acknowledge it.device_metadata_unknown— CodeHerder couldn’t read this device’s name or secret-acknowledgement state this pass, so the row can’t say more than that. This is a transient blip, not a real problem — re-run the report.
Narrowing the answer with --agent
ch task placement on its own answers “which device could take this task,” using only the task’s own requirements — it doesn’t yet know which agent would run it, so any capability that agent itself requires isn’t part of the check (that’s the agent_requirements_not_folded caveat). Naming a candidate agent sharpens the answer to exactly that agent’s chances, folding in its required capabilities, its harness’s requirements, and whether any secret it references is acknowledged on each device:
ch task placement <taskId> --agent <agentRef>
For a task already assigned to a specific agent, or when you’re deciding which of several agents to assign, run it once per candidate to compare.
Related guides
- Staffing coverage — the same coverage question, answered for every stage across your whole workspace at once
- Why isn’t my task moving? — the full diagnostic walkthrough, with the Placement report as the first stop for a staffing problem
- Managing your devices — online status, health checks, concurrency, and pausing a device
- Capabilities — what a capability label is and where each kind is set
- AI usage limits — how a usage window or spend cap pauses a device and recovers on its own
- Secrets on a device — acknowledging a device-side secret an agent’s launch config references
- Agents and the CLI — creating and configuring agents
Last updated