The Workbench standard
Spec 1 manifest
The Workbench standard defines a small, versioned package that prepares an AI to do a class of work. This page describes every field of a spec 1 manifest, the stable form of the standard.
The standard is open source and licensed under the Apache License 2.0. The specification, the JSON schema, the event protocol, and the outcomes contract are the interoperability contract. The wb CLI is the reference engine, not the only permitted host.
What the standard covers#
A Workbench is a repository-owned execution recipe. It is not an agent, and it does not define a DAG, workflow, user interface, or product-level run. A host can add those things around it.
Neither spec defines setup hooks, knowledge-file semantics, orchestration, user interfaces, or registry behavior. Implementations must reject unsupported runners, runtimes, images, or integration transports explicitly.
Package layout#
.workbenches/
core/
workbench.yml
instructions.md
skills/
migrations/
SKILL.mdEach immediate child directory of .workbenches/ is one Workbench. Its manifest is named workbench.yml. The reference engine refuses a package anywhere else.
A complete example#
spec: 1
version: 0.1.0
name: migrations
description: Design, review, and safely apply project migrations.
runner: opencode
model:
id: openai/gpt-5.6-terra
routes:
- provider: openai
- provider: openrouter
instructions: ./instructions.md
skills:
- ./skills/migrations
tools:
- cargo
- dbctl
mcps:
- name: database
transport: http
url: https://api.example.com/mcp
headers:
Authorization: Bearer ${DATABASE_API_TOKEN}
env:
DATABASE_API_TOKEN:
required: false
workspaces:
api:
required: true
access: read-write
requirements:
os: [linux, macos]
cpu: 4
memory_gb: 8
runtimes:
local: {}
docker:
image: ghcr.io/example/migrations:0.1.0Fields#
Required fields are spec, version, name, runner, model, instructions, and runtimes. No other top-level field is allowed.
| Field | Type | Required | Meaning |
|---|---|---|---|
spec |
integer 1 |
yes | The manifest schema version. |
version |
string | yes | The author's semantic release version, such as 0.1.0 or 1.2.0-rc.1. |
name |
string | yes | The package name. The default alias and registry name. |
description |
string | no | One line on what the Workbench is for. |
runner |
string | yes | The harness that executes the work, such as opencode, pi, or claude-code. |
model |
object | yes | The model policy. See Model. |
runner_config |
string | no | A package-relative file or directory of runner-native configuration. |
instructions |
string | yes | A package-relative Markdown file. |
skills |
list of strings | no | Package-relative skill directories. Defaults to []. |
tools |
list of strings | no | CLI executables that must exist before a run starts. Defaults to []. |
mcps |
list of objects | no | Remote MCP servers. See MCP servers. Defaults to []. |
env |
map | no | Environment variable names the Workbench reads. See Environment. |
workspaces |
map | no | Named workspaces beyond the primary one. See Workspaces. |
requirements |
object | no | What the environment must satisfy. See Requirements. |
runtimes |
map | yes | The runtimes the author supports, in order. See Runtimes. |
spec and version#
spec is the integer Workbench schema version. Engines dispatch on it, accept every spec they support, and reject any other value with Manifest spec N is not supported by this engine; upgrade wb. They never guess at an unknown spec.
version is the author's semantic release version. Source revisions and content digests remain the authority for reproducibility. The registry requires each pushed version to be higher than the last.
Model#
runner: opencode
model:
id: openai/gpt-5.6-terramodel.id uses lab/model, where lab is the organization that defines the model. It is not a provider selection. It must match ^[a-z0-9][a-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._:/-]*$.
An engine resolves the model through verified model metadata and chooses the first route for which the selected runner has compatible credentials. The metadata version used for a run is part of the resolved runner configuration.
model.routes restricts or orders the providers that may serve the model. It is a non-empty list:
| Field | Type | Required | Meaning |
|---|---|---|---|
provider |
string | yes | A provider identifier matching ^[a-z0-9][a-z0-9._-]*$, such as openai or openrouter. |
model |
string | no | The provider-native model identifier, when it differs from the catalog or the model is absent from it. |
Routes must serve the same model identity. They are not fallbacks to different models.
model:
id: private-lab/review-model
routes:
- provider: internal-gateway
model: deployment-42
runner_config: ./runnerAn unknown model requires an explicit native model identifier for every route and a packaged runner_config.
The author locks the runner and model policy. A person starting a run cannot replace the runner, model, allowed provider routes, or packaged native runner configuration, so a published Workbench stays equivalent to the configuration its maintainer tested.
runner_config#
runner_config is a package-relative file or directory interpreted only by the selected runner adapter. Pi requires a directory. The path must stay inside the package and cannot contain symbolic links, credential files, or literal credential values. Configuration can name environment bindings declared by the manifest; values are supplied only when the Workbench runs.
Credentials#
A Workbench package never owns a person's runner credentials. Engines verify that at least one allowed route is ready before sending a model request. When a runner has a documented command-line sign-in, an engine can offer it during connection setup. An engine must never inject login commands or simulated input into a runner conversation, and must not parse, copy into package state, upload, or print provider token contents.
Instructions and skills#
instructions is a package-relative Markdown file. Each skills entry is a package-relative directory containing a portable SKILL.md. Adapters expose skills through the runner's native on-demand skill mechanism.
Tools#
tools names CLI executables that must be available in the selected runtime before a run starts. A tool on the host does not satisfy a container or sandbox runtime. Presence is checked, not version compatibility or behavior.
MCP servers#
mcps describes remote Streamable HTTP servers.
| Field | Type | Required | Meaning |
|---|---|---|---|
name |
string | yes | Lowercase and hyphenated, matching ^[a-z0-9]+(?:-[a-z0-9]+)*$. |
transport |
http |
no | The only transport. Defaults to http. |
url |
string | yes | An http:// or https:// URL. |
headers |
map of strings | no | Request headers. Values can reference declared environment variables as ${NAME}. |
A server whose headers reference an unset optional variable is omitted from the run. An unset required variable rejects the run. Adapters keep the reference instead of placing the secret value in generated configuration or dry-run output.
Environment#
env:
DATABASE_API_TOKEN:
required: falseenv maps variable names to declarations. Names match ^[A-Z][A-Z0-9_]*$. Each declaration has one optional field, required, a boolean that defaults to true.
Environment values never appear in the manifest. The person or host starting a run supplies them.
Workspaces#
Every run has one primary workspace, chosen by whoever starts the run. It is the runner's working directory and is read-write. A Workbench can declare named workspaces when its expertise spans more repositories or directories:
workspaces:
api:
required: true
access: read-write
schemas:
required: false
access: read-onlyNames are lowercase and hyphenated, and primary is reserved. required defaults to true and access defaults to read-only; access is read-only or read-write. Declarations hold logical names and access, never machine paths.
A host binds a directory to each name for one run. The reference CLI takes repeatable --workspace NAME=PATH and rejects missing required bindings, undeclared names, unavailable directories, and duplicate resolved paths before launch. Runtimes expose each location as WORKBENCH_WORKSPACE_<NAME>, with hyphens replaced by underscores.
The local runtime checks readability or writability but cannot stop a host process from writing elsewhere. Engines must not present local access declarations as an isolation boundary.
Requirements#
requirements is optional, and every field in it is optional. An absent field places no constraint.
requirements:
os: [linux, macos]
arch: [arm64, x64]
cpu: 4
memory_gb: 8
disk_gb: 20
gpu: false| Field | Type | Meaning |
|---|---|---|
os |
non-empty list of linux, macos, windows |
The environment runs any one of them. |
arch |
non-empty list of x64, arm64 |
The environment uses any one of them. |
cpu |
positive integer | Minimum vCPUs. |
memory_gb |
positive number | Minimum memory in GiB. |
disk_gb |
positive number | Minimum disk in GiB. |
gpu |
boolean, default false |
true means the Workbench needs a GPU. |
Requirements are checked against the selected runtime before it is prepared, so a mismatch stops the run before it spends anything. A requirement an engine cannot verify or enforce must be reported as unchecked, never as satisfied. How each runtime checks them is on Runtimes.
Runtimes#
runtimes maps a provider name to its configuration. It must declare at least one runtime. Order matters: the first entry is the default.
| Provider | Configuration |
|---|---|
local |
None. Write local: {} or local:. |
docker |
image (required), docker (optional host engine binding). |
e2b |
image (required). |
daytona |
class (required: linux, windows, gpu, or macos), image (optional in the schema, required to run). |
image is either a string naming a published image or a build object:
runtimes:
docker:
image:
build: ./Dockerfile.workbench
context: .build is a package-relative Dockerfile and context is package-relative, defaulting to .. Both must stay within the containing repository. Providers decide how to cache prepared images, but the result must be equivalent to preparing the declared input again.
The reference engine runs only the linux Daytona class. It creates the sandbox from the Daytona entry's own published image, with no fallback to another entry's image; a missing image or a build object fails and names runtimes.daytona.image.
A Docker entry can request the host's Docker engine:
runtimes:
docker:
image: ghcr.io/example/project-workbench:0.4.0
docker:
engine:
mode: hostmode: host is the only mode. The declaration is not authorization: the person starting each run must grant it, with --allow-host-docker in the reference CLI. Host engine access gives code in the Workbench effective administrative control of the Docker host. The image must contain the Docker CLI.
An unknown provider name is an error and must not silently fall back to the host. runtime, top-level image, and top-level docker are spec 0 fields and are invalid in spec 1.
Runtime selection#
The person or host starting a run can name one declared runtime. The reference CLI accepts --runtime <name> on wb run, wb smoke, wb build, and wb create. Without it, the first declared runtime is used. Naming an undeclared runtime is an error that lists the declared ones.
Execution lifecycle#
- Resolve and validate the Workbench package.
- Select the named runtime provider and prepare its environment.
- Mount or copy the primary workspace, named workspaces, and package assets, then bind the run environment.
- Verify every declared tool inside that environment.
- Launch the runner and permit model requests only after preflight succeeds.
Preflight failure stops the run before spending model tokens and names the missing or incompatible tool. Durable executions collect changesets, artifacts, and links through the outcomes contract.
Versioning#
A spec version that has shipped never changes. An addition, removal, or change of meaning requires a new spec number. Once a spec is released its parser is immutable: engines select the matching parser and keep old parsers for backward compatibility.
Spec 0 is frozen legacy. It has a single runtime field with top-level image and docker, and no requirements or runtimes. Engines still read it and treat its runtime as a one-entry runtimes map internally. Every 1.x engine reads spec 0 manifests. wb init writes spec 1.