Workbenches is an open standard. Read why
Docs The manifest

Building Workbenches

The manifest

A package is a directory under .workbenches/ with a workbench.yml manifest, its instructions, and its skills. Every field is specified on Spec 1 manifest; this page is how to write one.

.workbenches/
  core/
    workbench.yml
    instructions.md
    skills/
    runner/
      opencode.json

A package must sit at .workbenches/<name>/workbench.yml. The engine refuses one anywhere else, for every command: Workbench must live beneath a .workbenches directory.

Create a package#

Scaffold#

wb init core
wb init core --dir /path/to/repository
wb init migrations --runner opencode --model openai/gpt-5.6-terra
wb init migrations --runtimes local,docker --image ghcr.io/example/migrations:0.1.0

wb init writes a spec 1 package deterministically, with no model run.

Option Effect
--dir <dir> The repository. Defaults to the current directory.
--runner <runner> The runner recorded in the manifest.
--model <id> The model. Rejected when the model catalog does not know it.
--runtimes <list> Comma-separated runtimes, written in that order. Defaults to local.
--image <ref> The image for docker, e2b, and daytona, which need one. daytona is written with class: linux.

The scaffold is a placeholder. Replace every generated line.

Let the creator write it#

wb create core --task "Create a focused expert for this repository"
wb create .#core --task-file improvements.txt

wb create runs the official creator Workbench against your repository. It inspects the repository, writes .workbenches/<name>/workbench.yml and its supporting files, and finishes only when the package passes engine-owned verification: it validates the candidate, checks its version and package scope, and runs wb smoke. Invalid packages, out-of-scope edits, missing version increments, and failed smoke checks fail the result with a nonzero exit. A completed creator turn alone is not success.

  • Naming an existing local package edits it. create never changes an immutable saved snapshot.
  • Give exactly one brief: --task, --task-file, or --stdin. --from <session> can infer improvements from stored session evidence without one.
  • --detach --json returns a receipt; wb wait waits for verification too, and its authoring result reports package selectors and paths, changed files, and the evidence path.
  • Candidates that declare environment values, named workspaces, or host Docker access are verified with --env-file, --env, --workspace, and --allow-host-docker. Those values are used only for the final smoke and never written to the authoring record.
  • --runtime picks which declared runtime the final smoke checks, and --allow-unchecked-gpu accepts a GPU discovery cannot see.

wb create spends a model run. The creator receives the exact wb build that started it.

Write the manifest#

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
runner_config: ./runner/opencode.json

instructions: ./instructions.md
skills:
    - ./skills/migrations

tools:
    - cargo
    - dbctl

env:
    DATABASE_API_TOKEN:
        required: false

requirements:
    os: [linux, macos]
    memory_gb: 8

runtimes:
    docker:
        image: images.workbenches.dev/acme/migrations:0.1.0
    local: {}

Rules#

  • Spec. Write the newest spec your installed wb supports: wb init scaffolds it and wb validate rejects anything else. Never guess a future version.
  • Version. Semantic versioning. Increment it whenever package content changes, so each content identity has a distinct version. The registry refuses a push that is not higher than the last.
  • Runner and model. Choose them from trial evidence on this Workbench's real tasks, and record the evidence. Without evidence, keep the existing valid choice and say it is unevaluated. They are locked: consumers connect credentials, they never override the runner, model, routes, or native configuration.
  • runner_config. Put runner-native configuration inside the package. Use it to enforce what the instructions promise: deny sub-agent delegation, scope file permissions, deny secret files. Never package credential files or literal secrets.
  • Instructions and skills stay inside the package, so it can be saved as a self-contained snapshot.
  • Tools are exact executable names already on the runtime's PATH: installed on the host for local, or in the image otherwise. A gate script inside the package is not on PATH; invoke it by its skill path, and declare it in tools only when the image installs it. Presence is preflighted; version compatibility is not.
  • env declares names, never values. Mark one optional only when the Workbench stays useful without it.
  • mcps are remote HTTP MCP servers only. Reference declared variables in headers as ${NAME}. An MCP server that depends on an unset optional variable is disabled for that run.
  • runtimes lists the providers the Workbench supports, default first. Declare local: {} only when host execution is intentional. Never write runtime, a top-level image, or a top-level docker.
  • requirements only for constraints the work truly depends on.

See Runtimes, models, and permissions for the runtime, image, model, and permission choices.

Check it#

From the repository root:

wb validate .#migrations
wb view .#migrations
wb smoke .#migrations
wb smoke .#migrations --runtime docker
wb add ./.workbenches/migrations --as migrations
wb run migrations --dry-run --task "Review the latest migration"

From elsewhere, use /path/to/repository#migrations or the path /path/to/repository/.workbenches/migrations. After wb add, the alias is live: new runs read the current files.

Check Proves
validate The package parses and its local files satisfy the schema.
view The resolved contract and authorization bindings, without values.
smoke The runner and every declared tool exist in the selected runtime. No model request.
run --dry-run The request resolves, preflights, and translates without launching the runner.

None of these says whether the package produces good work. Only trials do. Smoke every declared runtime; on e2b and daytona it creates a billable sandbox. Then run one tiny real task without approving any permission prompt: a stall means the permission config is incomplete.

Keep four results distinct when you report: a file change, passing validation, passing smoke, and an end-to-end successful run.

Boundaries the standard enforces#

A Workbench prepares execution. Task graphs, schedules, approval stages, retries, and product interface behavior belong to whatever orchestrates it, not to the package.