Cadence
Reference

Fleet

Pools, workers, worker versions (including an agent version's LLM vendor, model and effort), and the agent-skill and image catalogues that staff stages.

The fleet is what staffs your stages: a pool leases a task to a worker, which runs a specific worker version. Agents can carry skills, and every worker runs from a container image. This page is the reference for all of them.

Viewing the fleet needs read; configuring it needs admin. See Authentication. Replace https://cadence.example.com with your instance's address. For how these fit together, see Pools and workers.

Pools

A pool pins exactly one worker version and leases tasks to it. POST/GET/PATCH /api/v1/spaces/:space_key/pools[/:id] and .../pools/:id/repin; MCP pools_create, pools_get, pools_list, pools_update, pools_repin, pools_archive, pools_delete; the web interface (pause, resume, repin). Listing and getting need read; the rest need admin.

Fields:

FieldNotes
worker_version_idThe one version this pool runs. Required.
sizeHow many workers the pool runs. Zero or more.
debugRuns the pool in debug mode. Defaults to false.
max_lifetime_secondsA cap on a lease's lifetime, counted from when the lease was dispatched. Without one, the space's default lease lifetime applies; without that, a platform default for the worker kind applies. See When a lease reaches its max lifetime.
lease_scopesWhich tasks a lease may reach (see below). Defaults to task and parent.

lease_scopes is any of:

  • task — the leased task and its children.
  • parent — the leased task's parent and its children.
  • dependencies — the task's direct dependency neighbours.
  • workflow — every task on the lease's workflow.
  • space — every task in the space.

Any other value is refused.

Over the API, pool create takes kind — AgentPool, RunnerPool, or ClassifierPool — with size and worker_version_id; a create with any other kind is refused with invalid_kind. Over MCP, pools_create takes no kind: it derives the kind from the pinned version, so an agent version makes an AgentPool, a runner version a RunnerPool, and a classifier version a ClassifierPool. Pool update accepts only size and worker_version_id. debug, max_lifetime_seconds, lease_scopes, and pause state are set over MCP or the web interface. Pausing a pool stops it dispatching new leases while keeping its size.

A pool has no LLM vendor of its own. An agent pool's workers draw on the vendor named by the llm_vendor of the agent version the lease runs, and are served LLM_TOKEN and LLM_PROVIDER from that vendor's registration — the pool's space's registration, otherwise the instance-wide one. Repinning a pool to a version with another vendor changes the vendor its workers draw on. A worker does not start while no token of that vendor can be served: the dispatch records a spin-up failure and holds the task until a person acts. See LLM providers.

pools_create and pools_update no longer take llm_vendor, and the pool form in the browser no longer has an LLM vendor field. Both tools' input schemas declare no other arguments, so an MCP client that checks a call against the schema refuses an llm_vendor argument before sending it; Cadence itself ignores one that arrives and sets no vendor. To change the vendor, repin the pool to a version that names another vendor.

A classifier version carries no LLM vendor. A classifier draws on a question-answering provider, and that provider's key reaches it as a credential-sourced environment variable declared on the pinned version, the way any worker's secret does (see Credentials). Like a runner pool, a classifier pool takes the shorter platform lifetime leash, not the longer one an agent pool gets when no max_lifetime_seconds or space default is set.

Pinning a pool to a version it cannot use is refused, and what you see depends on the surface. Over the API, updating a pool (PATCH .../pools/:id) to a version that does not exist returns unknown_worker_version_id; a version that exists but is the wrong kind or is not yet ready is refused with invalid_request, on create, update, and repin alike. Over MCP, pools_create and pools_update report the message unknown worker_version_id for a version that does not exist. Archiving a pool with in-flight leases is refused with pool has in-flight leases; pause and drain before retiring. A pool that has ever run a lease cannot be deleted — pool has lease history and cannot be deleted; archive it instead — only archived.

When a lease reaches its max lifetime

A lease's max lifetime is the cap resolved from the max_lifetime_seconds row above, counted from when the lease was dispatched, not from when the task was queued.

In the last 120 seconds before the cap, the lease's heartbeat answers expire, unless the lease is being cancelled, when it answers cancel. expire is the worker's warning to wind down. The worker hears it only if it sends a heartbeat in that window. See the command-line client's heartbeat for the control words and what a worker does with expire.

Cadence checks every lease against its cap once a minute, so it kills a lease within about a minute after the cap passes. This applies whether the lease is running, paused or being cancelled. The lease ends killed, and Cadence does not retry it. If the lease was working a task, Cadence also holds the task with this reason:

dispatch blocked — manual intervention needed: <pool> (lease killed: exceeded its max lifetime of <N>s)

<pool> is the pool's name and <N> the cap in seconds. If the task already carries a blocking hold, Cadence leaves that hold in place and adds no second one. While a blocking hold stands, Cadence does not lease the task again, and Cadence never clears a blocking hold itself. To get the task worked again, see Hold a task and clear a hold.

Workers

A worker is a named agent, runner, or classifier. POST/GET/PATCH /api/v1/spaces/:space_key/workers[/:id]; MCP workers_register, workers_list, workers_update, workers_delete (delete is MCP-only). Listing needs read; registering, updating, and deleting need admin.

A worker has a kind — Agent, Runner, or Classifier — a key (lowercase letters, digits, -, and _, starting with a letter) unique in the space, a name, and a description. Registering is idempotent on the space and key: registering the same key again returns the existing worker.

You can update a worker's name, description, and key. Any other field is refused with unpermitted_fields. Renaming the key while a lease is active is refused with worker has an active lease — drain or cancel before renaming. Deleting is refused for a worker with lease history or one pinned by a pool.

Worker versions

A worker version is a frozen, runnable configuration of a worker. POST/PATCH /api/v1/spaces/:space_key/workers/:worker_id/versions[/:id], .../publish, .../fork; POST /api/v1/worker_versions/:id/archive; DELETE /api/v1/worker_versions/:id; MCP worker_versions_create_draft, worker_versions_update_draft, worker_versions_publish, worker_versions_fork, worker_versions_archive, worker_versions_discard, worker_versions_list, worker_versions_get. Listing and getting need read; the rest need admin.

A version moves through three states: draft, ready, and archived. You build a draft, then publish it to ready; publishing freezes its declarative fields, so a published version is immutable and a change means a new draft.

An agent version carries image_id, env, size (cpu and memory), services, system_prompt (with system_prompt_mode of replace or append), llm_vendor, model, an optional effort, mcp_servers, skill_version_ids, initial_prompt, and the hooks up, run, and down. A runner version carries a command. An agent version needs initial_prompt, an llm_vendor, and a model defined for that vendor to be published — see Choosing a vendor, model and effort. A runner version needs command.

A classifier version carries image_id, env, size, services, the hooks up, run, and down, and — in place of a prompt or a command — two JSON documents, input and output. Each defaults to an empty object, so a classifier version needs neither an initial_prompt nor a command to be published:

  • input names what Cadence hands the worker: attachments, a list of { name } objects naming which of the task's attachments to hand it, and questions, a list carried through to the provider untouched.
  • output names how the worker's answer reaches the task: checks, a map of Check code to a Liquid expression, and properties, a map of Property key to a Liquid expression. Each expression is rendered against the worker's reported result — see How an output expression is rendered.

A classifier version does not take the other kinds' fields: system_prompt, system_prompt_mode, llm_vendor, model, effort, and mcp_servers. worker_versions_create_draft drops any of them it is given and creates the draft without them; worker_versions_update_draft refuses them (for example, System prompt is not allowed on a Classifier). initial_prompt and command must be blank. An output checks map is refused if it names a Check code the space does not have; each expression must be valid Liquid template text. Both documents join the version's other declarative fields in being frozen once the version is published.

How an output expression is rendered

Each checks and properties expression is Liquid template text, rendered over a single variable, result — the JSON object the worker reports as the details of its completion (cadence worker complete --details <json>, or the details object on POST /api/v1/leases/:id/complete; see the CLI's complete). Its keys are whatever the worker put there — the answers it composed from the task and the version's questions. result is the only variable in scope: an expression sees the reported result and nothing else — not the task, the questions, or the attachments.

Write a properties expression to render to the value to set. Write a checks expression to render to pending, passed, or failed — a check that renders to anything else fails the whole application (below). An expression that renders to an empty string sets nothing: that entry is skipped, which lets an expression decline to write on a given result.

For a worker that reports { "verdict": "spam", "confidence": 0.98 }, this output records the verdict as a property and passes a check only when the verdict is not spam:

"output": {
  "properties": {
    "triage_verdict": "{{ result.verdict }}"
  },
  "checks": {
    "not_spam": "{% if result.verdict == 'spam' %}failed{% else %}passed{% endif %}"
  }
}

Rendering is strict: an expression that is malformed, or that names something the reported result does not carry, fails to render, and a report whose details is not a JSON object is rejected the same way — either failure fails the whole application, as below.

When a classifier pool's lease reports success, Cadence renders the pinned version's output against the reported result and writes what it names to the task: each properties entry as a task Property, each checks entry as a CheckResult. This is applied all-or-nothing — if any entry cannot be rendered or applied, nothing is written and the lease completes as failure instead, which holds the task for an operator. An entry whose expression renders to an empty string is skipped. Cadence records a classifier_outcome_applied activity on the task naming the properties and checks it wrote. See What moves a task without a person.

Environment variables are declared per version, each with a name and a source: a literal value, or a credential — the name of a live credential Cadence can resolve for the worker's space: a credential defined in the space, or an instance-scope credential set up for the whole instance. A credential source is refused only when no live credential of that name can be resolved.

The lifecycle refuses in these cases:

  • not_ready — archiving a version that is not ready.
  • pinned — archiving a version a pool still pins; repin the pool first.
  • active_leases — archiving a version with active leases; drain them first.
  • not_draft — discarding a version that is not a draft; archive a ready one instead.

Choosing a vendor, model and effort

An agent version names three things about the model its workers run:

  • llm_vendor — the vendor that sells the model: one of anthropic, openai, google, eurouter, or tensorx. Required to publish.
  • model — the model string the vendor's API expects. It must be a model defined for that vendor. A model is identified by vendor and model string together: the same string can appear under two vendors with different efforts.
  • effort — optional; how hard the model reasons. It must be one of the efforts the model's definition lists. null or "" means the model's own default. A model that defines no efforts refuses any effort.

Set them with worker_versions_create_draft or worker_versions_update_draft (llm_vendor, model, effort), or over the API on POST/PATCH /api/v1/spaces/:space_key/workers/:worker_id/versions[/:id]. Worker-version JSON, over MCP and the API, returns llm_vendor and effort; each is null when unset. A runner or classifier version does not take either. Over MCP, worker_versions_create_draft drops them and creates the draft without them, and worker_versions_update_draft refuses them — for example, Llm vendor is not allowed on a Runner. Over the API the fields are refused with unpermitted_fields.

Find the defined vendors and models

Call llm_models_list to see every model you can choose. It returns one row per model, sorted by vendor and then model:

FieldNotes
vendorThe vendor that sells it.
modelThe model string to set as model.
titleA readable name, the one the browser shows.
effortsThe efforts you can set, from least to most. Empty when effort cannot be set.
default_effortThe effort used when you set none. null when the vendor documents none.
deprecatedtrue for a model that can be saved on a draft but not published.

Deprecated models are included. Pass vendor to list one vendor's models; an unknown vendor is refused with unknown vendor "<vendor>" (known: anthropic, openai, google, eurouter, tensorx). The tool is read-only, needs no space, and can be called by a signed-in user or from a lease. The list changes as vendors add and retire models, so read it rather than relying on a copy.

What a draft refuses

A draft's vendor and model are checked when the draft is created from scratch, when a new draft's vendor or model differs from the version it was forked from, and whenever an edit changes either. Its effort is checked whenever it is set and the draft's vendor, model or effort was chosen or changed. The refusals name what you can choose instead:

  • No vendor: Llm vendor must be chosen (defined: anthropic, openai, google, eurouter, tensorx).
  • An unknown vendor: Llm vendor "<vendor>" is not defined (defined: …).
  • A model not defined for the vendor: Model "<model>" is not defined for <vendor> (defined: …), listing that vendor's models that are not deprecated.
  • An effort the model does not list: Effort "<effort>" is not an effort of "<model>" (allowed: …).
  • An effort on a model that defines none: Effort "<effort>" can't be set: "<model>" defines no efforts.
  • An effort when the model is not defined for the vendor, or no vendor is set: Effort "<effort>" can't be set: "<model>" has no defined efforts for <vendor>. With no vendor set, <vendor> reads no vendor.

A deprecated model is accepted on a draft. Publishing refuses it.

What publishing refuses

worker_versions_publish checks the vendor, model and effort in full, whatever the draft went through, and refuses:

  • No vendor or an unknown vendor: LLM vendor must be chosen (defined: …) or LLM vendor "<vendor>" is not defined (defined: …).
  • A model not defined for the vendor: model "<model>" is not defined for <vendor> (defined: …).
  • A deprecated model: model "<model>" is deprecated for <vendor> (choose one of: …).
  • An effort the model no longer lists: effort "<effort>" is not an effort of "<model>" (allowed: …).
  • An effort on a model that defines none: effort "<effort>" can't be set: "<model>" defines no efforts.
  • An effort when the model is not defined for the vendor, or no vendor is set: effort "<effort>" can't be set: "<model>" has no defined efforts for <vendor>, where <vendor> reads no vendor when none is set.

When more than one applies, publishing names them all in one refusal, separated by ; .

Forking copies llm_vendor and effort along with model. A fork of an older version that names no vendor is a draft that cannot be published until you choose a vendor, and a fork of a version whose model has since been deprecated cannot be published until you choose another model.

What the choice means for a pool

The version's vendor decides which LLM-provider registration its workers draw on. A pool pinned to the version runs no work unless the registration of that vendor that serves its space has a token it can serve: live, readable, and not held back by its limit or by the vendor. That registration is the space's own if the space has one of that vendor; the instance-wide one is used only when the space has none. A space registration with no token to serve stops the pool even when the instance's registration has tokens. If no token can be served, the worker does not start: the dispatch records worker_spin_up_failed, Cadence holds the task, and nothing retries. A person has to fix the cause — see LLM providers § How a token reaches a worker — and then clear the hold on each held task. See Hold a task and clear a hold § 3.

When effort is set, Cadence hands it to the worker in its lease payload; when it is not, the payload carries no effort. What the worker does with it is up to the worker's image. See What an agent reads about its model.

In the browser

The worker version's new and edit form offers three selects:

  • LLM vendor, listing the vendors by title.
  • Model, listing the chosen vendor's models that are not deprecated. It reloads when you change the vendor. A draft's current model stays selectable, marked (deprecated) or (not defined), so saving the form never switches it for you.
  • Effort, offering Model default (<default>) — or Model default when the model documents none — and the model's efforts. It reloads when you change the model.

The version's page shows the vendor beside the model — (vendor: None) when unset — and the effort, or the model's default it falls back to.

Agent skills

A skill is a unit of instruction an agent version can carry. This is the space's own skill catalogue: GET/POST /api/v1/spaces/:space_key/agent_skills, PATCH .../agent_skills/:id, and nested versions; MCP agent_skills_register, agent_skills_list, agent_skills_update, agent_skills_delete, and agent_skill_versions_create_draft, ..._update_draft, ..._publish, ..._fork, ..._archive, ..._discard, ..._list, ..._get. Listing and getting need read; the rest need admin.

A skill has a key, a name, a description, and a deprecated flag. Its versions follow the same draft → ready → archived lifecycle as worker versions, each carrying content and a description. An agent version pins skill versions by id.

Images

An image is a container image a worker version runs from. This is the space's own image catalogue: GET/POST /api/v1/spaces/:space_key/images, PATCH .../images/:id; MCP images_register, images_list, images_update, images_delete. Listing needs read; the rest need admin.

An image has a ref — a registry-and-tag or @sha256: digest reference — a name, a description, and a deprecated flag. The ref is immutable: to swap it, register a new image. Deleting an image that a worker version still references is refused.

On this page