Building Workbenches
The method
A Workbench is an expert package, not a prompt. The good ones share one property: the package decides what "done" means and makes the model prove it. Prose that only describes good work is the weakest part of any package. Build the parts that check the work first, then write the prose around them.
This page is the order of work. Each step produces something a later step depends on.
| Step | Produces | Detail |
|---|---|---|
| 1. Find the failure | A written failure list | Below |
| 2. Choose the boundary and the judging axes | A name and three to five axes | Below |
| 3. Build the gate before the prose | A script whose exit code is done | Below |
| 4. Write the instructions | instructions.md |
Instructions and skills |
| 5. Split skills by judging axis or phase | skills/ |
Instructions and skills |
| 6. Encode taste | A design artifact, a rubric, a blacklist | Instructions and skills |
| 7. Scope, stop, and spend | Task classes and stop conditions | Below |
| 8. Fit the runtime, then verify | Manifest, image, runner_config |
Runtimes, models, and permissions |
| 9. Trial it against the raw model | Worked count and cost per working result | Trials and review |
You can let a model start the package for you: wb create <name> --task "..." runs the official creator against your repository. See The manifest.
1. Find the failure#
Before designing anything, establish what a capable model gets wrong in this domain without the package. Read the repository's code, tests, docs, and agent guidance. If you can, run the model on a representative task with no package and read the transcript.
Write the failure list in concrete terms:
- "fakes first-run data"
- "never adds delete"
- "loses data on relaunch"
- "ships a static mockup"
- "stops after scaffolding"
- "judges the result without looking at it"
The whole package exists to remove these failures. A package built without this list optimizes for the wrong thing.
2. Choose the boundary and the judging axes#
Pick one coherent class of work where the same instructions, skills, tools, model, and runtime apply. Name it for the expertise (ios, migrations, threejs-game), never a generic role (coder, reviewer). Split into several packages only when the instructions, skills, tools, model, or runtime differ materially.
Then name the three to five axes the result is judged on, in words a skeptical expert would use. For an app: it looks designed, it feels alive, it works under real conditions. These axes organize the skills, the rubric, and the report.
3. Build the gate before the prose#
The gate is the script that decides whether the work is done. It is the most important file in a Workbench. A package without one asks the model to grade its own work by feel.
A gate:
- Is one command,
gate [dir]. - Builds and runs the real thing: compile, test, serve, launch the simulator, apply the migration to a scratch database. Evidence comes from the running result, never from reading the source.
- Is manifest-driven. The work declares up front what must be proven (states, viewports, screens, migrations, endpoints) in a file such as
artifacts/evidence.json. The gate fails when a declared item has no evidence, and when evidence exists for an undeclared item. A failing item cannot pass by being deleted. - Numbers its runs. Each invocation writes
run-<n>, and a--nextflag advances it, so history cannot be rewritten to hide a failure. - Writes a machine-readable summary: per-item pass or fail and the reason, in JSON next to the evidence, and one printed line per item.
- Exits non-zero on any failure, including infrastructure failures the work must fix.
- Cleans up after itself: stops servers, deletes simulators, removes scratch databases, even when it fails.
- Is cheap to read: contact sheets, a summary, or a short
review.mdchecklist.
Exit 0 becomes the only definition of done in the instructions.
Where the gate lives#
A script shipped inside the package is not on PATH. Keep it in a skill's scripts/ directory. During a run the shell's working directory is the target workspace, not the package. The agent loads the skill with the runner's skill tool, which reports the skill's directory, and runs the script by that absolute path, for example node <the skill's directory>/scripts/gate.mjs .. Say exactly that in the instructions and the skill's SKILL.md.
That directory is a temporary, read-only, per-run staged copy, so never hard-code it and never write into it. The staged config directory is exported to the runner as OPENCODE_CONFIG_DIR (OpenCode), PI_CODING_AGENT_DIR (Pi), or CLAUDE_CONFIG_DIR (Claude Code).
Alternatively, install the gate on PATH in the image and declare it in tools.
A skeleton#
#!/usr/bin/env bash
# gate: build, run, capture, check. Exit 0 is done.
set -euo pipefail
dir="${1:-.}"
cd "$dir"
manifest="artifacts/evidence.json"
[ -f "$manifest" ] || { echo "FAIL manifest: $manifest is missing"; exit 1; }
run="$(cat artifacts/run 2>/dev/null || echo 1)"
[ "${2:-}" = "--next" ] && run=$((run + 1))
echo "$run" > artifacts/run
out="artifacts/run-$run"
rm -rf "$out" && mkdir -p "$out"
cleanup() { stop_servers; }
trap cleanup EXIT
build_project # compile and test; any failure is a FAIL line
start_server_background # never foreground
capture_every_item "$manifest" "$out"
check_items "$manifest" "$out" > "$out/summary.json"
print_lines "$out/summary.json"
all_passed "$out/summary.json"Replace each placeholder function with the domain's real commands. Keep the gate in the package, versioned with it.
Name the cheats#
Every gate invites a shortcut. Name each one in the instructions as a rule:
- Never remove a failing item from the manifest.
- Test hooks drive real states. Never short-circuit them to satisfy a capture.
- Never edit the gate, its config, or its outputs.
- Never hard-code the data a check reads.
The required look#
Machine checks are the floor, not the verdict. After the gate passes, the instructions require the model to open the evidence and judge it against the rubric, and to record findings in a review file the gate created. A pass with an ugly, wrong, or empty result is a defect: fix it, advance the run number, and gate again. Add the prior: "A review with no findings on a first pass is almost always a review that did not look."
7. Scope, stop, and spend#
Classify the task, such as new work, an upgrade, or a narrow edit, and give each class its own bar. Existing code sets the conventions; house defaults apply to new work only. The user's stated scope beats every skill default.
Give explicit stop conditions: a pass budget ("two proof passes"), and "do not polish past what the task asked for". Tell the model to work lean, read a file once, and prefer contact sheets or summaries over opening every artifact. The person running the Workbench pays for every step.
Anti-patterns#
- A package that validates and smokes cleanly and contains only generic prose.
- Benchmark-shaped rules: "ship the smallest thing", "skip the check if it errors". The gate is mandatory.
- Rules written around one known test instead of the class of work.
- Vendored skills that reference absent tools, workers, or user questions.
- Two entry points that compete, patched over with precedence rules.
- A model choice presented as best without trial evidence for this work.
- Credential values,
.envcontents, or secret files anywhere in the package.
The authoring skill#
The same method ships as the wb-authoring skill, with these references, for a coding agent to follow:
npx skills add pompeii-labs/workbenches --skill wb-authoringIf your agent can spawn subagents, the skill hands the scouting, drafting, critique, and trials to separate passes, and otherwise has the agent do them itself while treating its own draft as untrusted.