Skip to content

Commit d004428

Browse files
varunursekarclaude
andcommitted
Add a run-benchmark skill: how to launch and health-check a run
A provider-agnostic runbook for launching one benchmark's optimization end-to-end (compile -> gateway -> optimizer -> sandboxed evals -> finalize), with the preflight to confirm before launching and the health checks after. No secrets or environment-specific paths; points at CONFIGURATION.md and each build.yaml for the per-benchmark specifics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ece0c5e commit d004428

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

  • harness-engineering-bench/skills/run-benchmark
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
---
2+
name: run-benchmark
3+
description: >-
4+
Run one harness-engineering-bench optimization end-to-end (compile → inference
5+
gateway → optimizer agent → sandboxed evals → finalize on held-out test),
6+
including the preflight to confirm before launching and the health checks to
7+
verify after. Use when launching, reproducing, or debugging a benchmark run.
8+
---
9+
10+
# Running a harness-engineering-bench optimization
11+
12+
This is a runbook for launching one benchmark's optimization run and confirming
13+
it is healthy. It is written to be provider-agnostic: fill in your own inference
14+
endpoint, Modal account, and W&B account. Treat every `<placeholder>` as
15+
something you supply. **Never commit real keys, tokens, endpoints, or absolute
16+
personal paths** — they belong only in a local, git-ignored `secrets.env`.
17+
18+
## What a run is
19+
20+
Each benchmark compiles from `harness-engineering-bench/<benchmark>/baseline/build.yaml`,
21+
the single source of truth. `vero harbor run` compiles it into a Harbor task and
22+
stands up three things: an **inference gateway** (holds the real upstream key,
23+
enforces per-scope model allow-lists), an **evaluation sidecar** (owns the cases,
24+
scoring, and final candidate selection), and the **optimizer agent** (a coding
25+
agent that edits only `target/`, commits candidates, and scores them via the
26+
`evals` CLI). When the optimizer finishes, the trusted verifier scores the
27+
selected candidate on the held-out `test` partition and writes the final reward.
28+
29+
The optimizer never sees the real upstream key — it gets a scoped producer token
30+
pointed at the gateway. Keep it that way.
31+
32+
## Prerequisites (confirm these exist first)
33+
34+
- The repo checkout containing both the `vero/` CLI package and
35+
`harness-engineering-bench/`. Run commands from the `vero/` subdirectory.
36+
- `uv` installed (the CLI is invoked as `uv run vero ...`).
37+
- **Docker** running — needed for `--environment docker` (the outer optimizer
38+
compose runs locally).
39+
- A **Modal** account + tokens — the inner evaluation sandboxes run there by
40+
default (`environment_name: ${inner_env:-modal}`).
41+
- A **Weights & Biases** account + API key (self-hosted or cloud) for telemetry.
42+
- An **OpenAI-compatible inference endpoint** + key that can serve both your
43+
optimizer model and each benchmark's target model. The gateway proxies to it.
44+
- A local `secrets.env` (copy from a benchmark's `secrets.env.example` where
45+
present). Required keys (names only — never values in any committed file):
46+
`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `MODAL_TOKEN_ID`, `MODAL_TOKEN_SECRET`,
47+
`WANDB_API_KEY`, `WANDB_BASE_URL`. A few benchmarks that run an in-container
48+
judge/user-sim also need `OPENAI_API_BASE` (set it equal to `OPENAI_BASE_URL`).
49+
See each benchmark's `baseline/README.md` and `CONFIGURATION.md`.
50+
51+
## Launch command (template)
52+
53+
```bash
54+
cd <repo-root>/vero
55+
uv run vero harbor run \
56+
--config ../harness-engineering-bench/<benchmark>/baseline/build.yaml \
57+
--env-file secrets.env \
58+
--environment docker \ # outer optimizer location (see tradeoff below)
59+
--agent <agent-name> \ # e.g. codex or claude-code (exact registry name)
60+
--model <optimizer-model> \ # must match the gateway producer allow-list
61+
--param wandb_run=<benchmark>__<optimizer-model> \
62+
--yes \
63+
-o ../runs/<benchmark>/<optimizer-label>/jobs
64+
```
65+
66+
Notes that bite if you get them wrong:
67+
68+
- **`--agent` is the exact Harbor registry name**, not a friendly alias. If it is
69+
wrong you get `Agent name <x> is not valid. Valid agent names: {...}` at init
70+
(a fast, harmless crash — no containers start). The Claude Code agent is
71+
`claude-code` (not `claude`); the OpenAI coding agent is `codex`. If unsure,
72+
run with a deliberately bogus `--agent` once and read the valid-names list.
73+
- **`--model` must be spelled exactly as the producer allow-list** in the
74+
build.yaml (`inference_gateway.producer.allowed_models`, usually
75+
`${optimizer_model:-<default>}`). `--model` is threaded into that placeholder,
76+
so the agent's model and the allow-list stay in lockstep; a mismatch is a
77+
gateway 403 on the optimizer's first request. If a coding agent rewrites the
78+
model string on the wire (some strip a provider prefix), pass the bare form
79+
that both the upstream serves and the allow-list expects.
80+
- **`-o <dir>`** is forwarded to Harbor; use the systematic layout
81+
`runs/<benchmark>/<optimizer-label>/jobs`.
82+
- **`--environment`** controls where the *optimizer* runs: `docker` = local,
83+
fully observable, but the run dies if the machine sleeps; `modal` = survives a
84+
local sleep/disconnect, less local visibility. Inner evals are on Modal either
85+
way (override with `--param inner_env=docker` only for a local shakedown).
86+
87+
## Preflight — confirm BEFORE launching
88+
89+
Do a dry compile and inspect the artifacts. This catches almost every
90+
misconfiguration for free:
91+
92+
```bash
93+
cd <repo-root>/vero
94+
VERO_SKIP_SECRET_CHECK=1 uv run vero harbor build \
95+
--config ../harness-engineering-bench/<benchmark>/baseline/build.yaml \
96+
--output /tmp/precompile
97+
```
98+
99+
Then check, in the compiled `/tmp/precompile`:
100+
101+
1. **`environment/sidecar/serve.json``backends`**: each partition backend has
102+
the `n_attempts` / `aggregate_attempts` you intend. If you want the noisy
103+
held-out finalize averaged over N, only the **test** backend should show
104+
`n_attempts: N` / `aggregate_attempts: mean`; development and validation stay
105+
at the global (usually 1). This is set per target in build.yaml
106+
(`targets[].n_attempts` / `aggregate_attempts`).
107+
2. **`serve.json``wandb`**: `project`, `group`, `name` are what you expect.
108+
3. **Model allow-lists** (`serve.json` and `environment/gateway/config.json`):
109+
the **evaluation** allow-list is the benchmark's target model; the
110+
**producer** allow-list is your optimizer model (it shows the build default
111+
here — at launch `--model` overrides it).
112+
4. **`instruction.md`** (the optimizer's task prompt): read it end to end. The
113+
objective, the `evals run --backend ... --partition ...` command, and the
114+
exposed partitions should be right, and it must not advertise tools/skills/
115+
subagents that aren't actually shipped in the workspace.
116+
117+
Also confirm, out of band:
118+
119+
- **The optimizer model actually serves on your upstream** — send one tiny
120+
request to your inference endpoint with that exact model string. A model the
121+
upstream doesn't recognize 403/404s the optimizer immediately.
122+
- **W&B auth works** and the project name is correct (a viewer query against your
123+
W&B host with the key).
124+
- **Docker is up** (`docker info`) and **Modal tokens are valid**.
125+
- **The target model and `baseline_reward`** pinned in build.yaml are the ones
126+
you mean to compare against (`CONFIGURATION.md` records the held-out baselines).
127+
128+
## Launch discipline
129+
130+
- **Launch detached** so the run outlives your shell/session. Redirect to a log
131+
and record the PID, e.g. `nohup bash launch.sh > run.log 2>&1 &` (or `setsid`;
132+
on macOS, which lacks `setsid`, a Python double-fork + `os.setsid()` daemonizer
133+
works). Keep `launch.sh`, `run.log`, `run.pid` alongside the `jobs/` output.
134+
- Prefer to **confirm the launch** (of a full-budget run) before firing — these
135+
spend real target-model and optimizer tokens plus Modal compute.
136+
137+
## Post-launch health check — verify AFTER launching
138+
139+
Watch for a fast crash first (invalid agent name, missing secret, model 403,
140+
Docker down), then confirm forward progress. A tail filtered for failure
141+
signatures catches the crashes:
142+
143+
```bash
144+
tail -f run.log | grep -E 'Traceback|Exception|Error|denied|40[0-9]|429|Killed|OOM|not valid|Cannot connect to the Docker'
145+
```
146+
147+
After a few minutes (first build can be slow), confirm the good path:
148+
149+
- Outer process still alive; no traceback in `run.log`.
150+
- The compose containers are up (gateway, sidecar, optimizer/agent).
151+
- The optimizer is issuing **gateway-authorized** requests — no 403s in the
152+
gateway request log (a 403 storm = producer model ≠ allow-list).
153+
- Inner eval sandboxes are dispatching (Modal auth is good) and `result.json`
154+
files begin appearing under the `jobs/` tree.
155+
- A W&B run shows up under the expected project, not immediately failed.
156+
- Provider **429s**: the seed agents retry transient rate limits, but sustained
157+
429s mean you are hitting a shared upstream quota — throttle concurrency.
158+
159+
If you see a clear failure signal, **stop the run and diagnose from disk** rather
160+
than letting it burn budget. Detached/daemon-owned sandboxes can keep running
161+
after the parent dies, so check for and clean up orphans when you kill a run.
162+
163+
## What "done / green" looks like
164+
165+
- **`finalize.json`** (admin volume) / **`harbor-finalization.json`** (session):
166+
`shipped: true`, a `rewards` map (keyed by the target's `reward_key`), and
167+
`baseline_rewards`. `shipped: false` means selection produced nothing.
168+
- Session artifacts exported: a portable `experiment.html` report, the session
169+
archive, and the candidates repo.
170+
- The W&B run is finished (not failed); its summary carries `shipped`.
171+
- Compare the candidate's held-out reward against the pinned `baseline_reward`
172+
for that benchmark — an improvement is `shipped: true` with a higher reward.
173+
174+
## Cost awareness
175+
176+
A full run spends: target-model tokens on development + validation (bounded by
177+
each partition's `total_cases`), the optimizer-model session, plus finalization
178+
(`n_attempts × test_size` case-evaluations; the pinned baseline is not re-scored
179+
when `score_baseline: false`). Read `budgets.json` in the session for actuals.
180+
181+
## Per-benchmark gotchas
182+
183+
Don't hardcode assumptions — read `CONFIGURATION.md` and the benchmark's
184+
`baseline/build.yaml` and `baseline/README.md`. Common differences: a benchmark
185+
may pin a **different target model**; some run an **in-container judge or
186+
user-simulator** on the real upstream (extra credentials + cost, and reduced
187+
isolation); some pull **large prebuilt images or corpora** (long first build);
188+
timeouts and partition sizes vary widely. When in doubt, dry-compile and read the
189+
rendered `instruction.md` and `serve.json`.

0 commit comments

Comments
 (0)