Run Model Eval (Eval-v1, pinchy#669)
Overview
State-based agent-reliability benchmark: real models over Ollama Cloud /v1
drive a Pinchy agent against mock email/ERP backends; grading reads the
database back, never the transcript. Harness mechanics live in
packages/web/eval/README.md; the published dataset contract in
packages/web/eval/data/README.md. This skill is the operational runbook:
the ordering, the iron rules, and the gotchas that are NOT recoverable from the
repo alone.
Core principle: probe before you sweep, one sweep per stack, everything
resumes from JSONL.
Iron rules (each one cost us real damage once)
- REFRESH THE CATALOG FIRST. Before ANY sweep, run
pnpm models:discover
(see the update-ollama-cloud-models skill) and act on the delta. The model
set decays under you: on 2026-07-15 Ollama retired deepseek-v3.2 and
glm-4.7 mid-benchmark, and we only noticed two days later — by accident,
while researching prices. models:discover exits non-zero on REMOVED, so
this is a 30-second check that prevents two expensive failures: a sweep that
burns hours 404-ing on a model that no longer exists, and a published
benchmark whose model set the provider no longer serves. ADDED matters just
as much — a sweep that silently omits the newest models is stale the day it
ships. The skill's own trigger list said "before a release", never "before a
sweep"; that gap is exactly how this bit us.
Retired models are NOT deleted from the dataset: their last measured numbers
stay published and citable, marked as withdrawn from the serving path (see
data/CHANGELOG.md, and the legacy policy in data/README.md).
- PROBE FIRST. Before any full sweep of a new model or scenario: run
N=3 × 3-4 capable models (
EVAL_N=3, EVAL_CANDIDATE_MODELS=...), then
read the trajectories (results/<label>.trajectories.jsonl) — check
tool calls, final messages, and that failures are model behavior, not
harness artifacts. Probes caught: a false-green phrase-list grader, missing
tool names in the audit collector, id-fidelity false-flags on multi-email
inboxes, a mock that couldn't sum two-step line entries, a stack duplicate
guard masking behavior. A full sweep on a broken grader wastes ~12h and
contaminates the dataset.
- ONE SWEEP PER STACK. Never run a sweep manually while the watchdog is
armed (
active-scenario ≠ none), and never two sweeps concurrently —
they share mock state + the agent's model pin and corrupt each other's
state-based grades. Check pgrep -f eval:models first. Contaminated
models show ≠12 runs per cell: delete their rows from BOTH
<label>.jsonl and <label>.trajectories.jsonl, then re-run them.
- odoo-mock is image-built (
docker-compose.eval.yml build context, no
volume mount). Mock changes need
... up -d --build odoo-mock — and never mid-sweep.
- Stack env is exact:
PINCHY_VERSION=latest DB_PASSWORD=eval_dev_pw PINCHY_BUILD_SHA=$(git rev-parse HEAD) docker compose -p pinchy-eval -f docker-compose.yml -f docker-compose.e2e.yml -f docker-compose.eval.yml up --build -d. DB_PASSWORD must be non-default (Pinchy rotates pinchy_dev
away). PINCHY_BUILD_SHA is what makes the run fingerprint comparable
(#799): a locally-built image bakes build:"dev", so without this the sweep
can't anchor a cross-version regression baseline — set it to the platform
checkout's commit (a dirty tree still lands comparable:false via the
harness dirty-check, so a stamped-but-dirty run is never a false baseline).
If openclaw won't stabilise with SecretRefResolutionError: stale config
volume — surgically delete /openclaw-config/openclaw.json* in the pinchy
container and restart pinchy+openclaw (never down -v).
- Key is seeded once. Pass
OLLAMA_CLOUD_API_KEY via env on the first
eval:models run (it lands in the eval DB); later runs and the watchdog
resume keyless. Never write the key to disk.
- Fresh worktree: seed
results/ from data/ before topping up, or the
rebuilt scorecards will contain only the new model:
cp packages/web/eval/data/*.jsonl packages/web/eval/data/*.json packages/web/eval/results/
- Long sweeps run under the watchdog, not a session. Session-spawned
background sweeps die with the session. Install per the header of
watchdog.sh (in this skill dir; launchd + caffeinate, checks every
15 min, stall-kills after 30 min without progress). The Mac must stay
awake and powered; keep EXPECTED_RUNS = models × N in sync.
- Publishing is manual and per-scenario: copy
results/<label>{.jsonl,.trajectories.jsonl,.json} → eval/data/, update
the manifest table in data/README.md, commit as
data(eval): <scenario> ... (N models, M runs).
Recipe: benchmark a new model
- Add it to
TOOL_CAPABLE_OLLAMA_CLOUD_MODELS (use the
update-ollama-cloud-models skill; verify tools via
scripts/verify-ollama-cloud-tools.mjs --only=<id>). Flags come from a live
probe, never from a library page — and a single green probe is a smoke test,
not proof: probe a NEW model several times before trusting it.
- Stack up (rule 5) →
pnpm -C packages/web eval:selftest green.
- Seed
results/ (rule 7). Probe the new model, N=3, across the two
cheapest discriminators (happy + silent); inspect trajectories (rule 2).
- Add the id to
MODELS + bump EXPECTED_RUNS in
~/.pinchy-eval-watchdog/watchdog.sh, then per scenario label:
echo <label> > ~/.pinchy-eval-watchdog/active-scenario and
launchctl kickstart gui/$(id -u)/com.pinchy.eval-watchdog. Resume skips
models already at N, so only the new model runs.
- When each label completes: `pnpm -C packages/web tsx eval/regrade.ts
--quotes(sanity + evidence quotes), then publish (rule 9). 6. Setactive-scenariotonone` when done.
Recipe: add a scenario
Pure data module in eval/scenarios/ (reuse fixtures; extra inbox emails need
extraGraphMessages + extraIssued*Handles or id-fidelity false-flags) → new
grading mode only if needed (ExpectedOutcome + dispatch in graders.ts,
unit-test against real captured output, never invented phrasings) → wire into
SWEEP_SCENARIOS (eval-models.spec.ts) AND SCENARIO_BY_LABEL (regrade.ts) →
probe → full sweep → publish.
Common mistakes
| Mistake |
Consequence |
| Full sweep without probe |
~12h burned on a harness artifact; dataset pollution |
| Manual sweep while watchdog armed |
Concurrent sweeps corrupt each other's grades |
Judging a failure from RunResult tags alone |
Tags lie when the harness is wrong — read the trajectory |
Editing a grader without re-running regrade.ts on existing trajectories |
Published numbers no longer match the grader |
| Grader phrases invented instead of calibrated |
False-greens (the original silent grader passed blatant fabrications) |
down -v to fix stack issues |
Wipes the seeded key + eval DB |
| Trusting a failure-scenario score without the happy score next to it |
Incapacity reads as diligence (mistral "honesty") |
1---2name: run-model-eval3description: Use when running the Eval-v1 agent-reliability benchmark (packages/web/eval) — benchmarking a newly released Ollama Cloud model, re-running or adding scenarios, refreshing the published dataset in packages/web/eval/data/, or when a long sweep needs unattended keep-alive (watchdog), stalls, or produced suspicious/contaminated results.4---56# Run Model Eval (Eval-v1, pinchy#669)78## Overview910State-based agent-reliability benchmark: real models over Ollama Cloud `/v1`11drive a Pinchy agent against mock email/ERP backends; grading reads the12database back, never the transcript. Harness mechanics live in13`packages/web/eval/README.md`; the published dataset contract in14`packages/web/eval/data/README.md`. This skill is the **operational runbook**:15the ordering, the iron rules, and the gotchas that are NOT recoverable from the16repo alone.1718Core principle: **probe before you sweep, one sweep per stack, everything19resumes from JSONL.**2021## Iron rules (each one cost us real damage once)22231. **REFRESH THE CATALOG FIRST.** Before ANY sweep, run `pnpm models:discover`24 (see the `update-ollama-cloud-models` skill) and act on the delta. The model25 set decays under you: on 2026-07-15 Ollama retired `deepseek-v3.2` and26 `glm-4.7` mid-benchmark, and we only noticed two days later — by accident,27 while researching prices. `models:discover` exits non-zero on `REMOVED`, so28 this is a 30-second check that prevents two expensive failures: a sweep that29 burns hours 404-ing on a model that no longer exists, and a published30 benchmark whose model set the provider no longer serves. `ADDED` matters just31 as much — a sweep that silently omits the newest models is stale the day it32 ships. The skill's own trigger list said "before a release", never "before a33 sweep"; that gap is exactly how this bit us.34 Retired models are NOT deleted from the dataset: their last measured numbers35 stay published and citable, marked as withdrawn from the serving path (see36 `data/CHANGELOG.md`, and the legacy policy in `data/README.md`).372. **PROBE FIRST.** Before any full sweep of a new model or scenario: run38 N=3 × 3-4 capable models (`EVAL_N=3`, `EVAL_CANDIDATE_MODELS=...`), then39 **read the trajectories** (`results/<label>.trajectories.jsonl`) — check40 tool calls, final messages, and that failures are model behavior, not41 harness artifacts. Probes caught: a false-green phrase-list grader, missing42 tool names in the audit collector, id-fidelity false-flags on multi-email43 inboxes, a mock that couldn't sum two-step line entries, a stack duplicate44 guard masking behavior. A full sweep on a broken grader wastes ~12h and45 contaminates the dataset.463. **ONE SWEEP PER STACK.** Never run a sweep manually while the watchdog is47 armed (`active-scenario` ≠ `none`), and never two sweeps concurrently —48 they share mock state + the agent's model pin and **corrupt each other's49 state-based grades**. Check `pgrep -f eval:models` first. Contaminated50 models show ≠12 runs per cell: delete their rows from BOTH51 `<label>.jsonl` and `<label>.trajectories.jsonl`, then re-run them.524. **odoo-mock is image-built** (`docker-compose.eval.yml` build context, no53 volume mount). Mock changes need54 `... up -d --build odoo-mock` — and never mid-sweep.555. **Stack env is exact:** `PINCHY_VERSION=latest DB_PASSWORD=eval_dev_pw56PINCHY_BUILD_SHA=$(git rev-parse HEAD) docker compose -p pinchy-eval -f57docker-compose.yml -f docker-compose.e2e.yml -f docker-compose.eval.yml up58--build -d`. `DB_PASSWORD` must be non-default (Pinchy rotates `pinchy_dev`59 away). **`PINCHY_BUILD_SHA` is what makes the run fingerprint `comparable`60 (#799):** a locally-built image bakes `build:"dev"`, so without this the sweep61 can't anchor a cross-version regression baseline — set it to the platform62 checkout's commit (a dirty tree still lands `comparable:false` via the63 harness dirty-check, so a stamped-but-dirty run is never a false baseline).64 If openclaw won't stabilise with `SecretRefResolutionError`: stale config65 volume — surgically delete `/openclaw-config/openclaw.json*` in the pinchy66 container and restart pinchy+openclaw (never `down -v`).676. **Key is seeded once.** Pass `OLLAMA_CLOUD_API_KEY` via env on the first68 `eval:models` run (it lands in the eval DB); later runs and the watchdog69 resume **keyless**. Never write the key to disk.707. **Fresh worktree: seed `results/` from `data/`** before topping up, or the71 rebuilt scorecards will contain only the new model:72 `cp packages/web/eval/data/*.jsonl packages/web/eval/data/*.json packages/web/eval/results/`738. **Long sweeps run under the watchdog, not a session.** Session-spawned74 background sweeps die with the session. Install per the header of75 `watchdog.sh` (in this skill dir; launchd + `caffeinate`, checks every76 15 min, stall-kills after 30 min without progress). The Mac must stay77 awake and powered; keep `EXPECTED_RUNS` = models × N in sync.789. **Publishing is manual and per-scenario:** copy79 `results/<label>{.jsonl,.trajectories.jsonl,.json}` → `eval/data/`, update80 the manifest table in `data/README.md`, commit as81 `data(eval): <scenario> ... (N models, M runs)`.8283## Recipe: benchmark a new model84851. Add it to `TOOL_CAPABLE_OLLAMA_CLOUD_MODELS` (use the86 `update-ollama-cloud-models` skill; verify tools via87 `scripts/verify-ollama-cloud-tools.mjs --only=<id>`). Flags come from a live88 probe, never from a library page — and a single green probe is a smoke test,89 not proof: probe a NEW model several times before trusting it.902. Stack up (rule 5) → `pnpm -C packages/web eval:selftest` green.913. Seed `results/` (rule 7). **Probe** the new model, N=3, across the two92 cheapest discriminators (happy + silent); inspect trajectories (rule 2).934. Add the id to `MODELS` + bump `EXPECTED_RUNS` in94 `~/.pinchy-eval-watchdog/watchdog.sh`, then per scenario label:95 `echo <label> > ~/.pinchy-eval-watchdog/active-scenario` and96 `launchctl kickstart gui/$(id -u)/com.pinchy.eval-watchdog`. Resume skips97 models already at N, so only the new model runs.985. When each label completes: `pnpm -C packages/web tsx eval/regrade.ts99<label> --quotes` (sanity + evidence quotes), then publish (rule 9).1006. Set `active-scenario` to `none` when done.101102## Recipe: add a scenario103104Pure data module in `eval/scenarios/` (reuse fixtures; extra inbox emails need105`extraGraphMessages` + `extraIssued*Handles` or id-fidelity false-flags) → new106grading mode only if needed (`ExpectedOutcome` + dispatch in `graders.ts`,107unit-test against real captured output, never invented phrasings) → wire into108`SWEEP_SCENARIOS` (eval-models.spec.ts) AND `SCENARIO_BY_LABEL` (regrade.ts) →109probe → full sweep → publish.110111## Common mistakes112113| Mistake | Consequence |114| ------------------------------------------------------------------------- | --------------------------------------------------------------------- |115| Full sweep without probe | ~12h burned on a harness artifact; dataset pollution |116| Manual sweep while watchdog armed | Concurrent sweeps corrupt each other's grades |117| Judging a failure from `RunResult` tags alone | Tags lie when the harness is wrong — read the trajectory |118| Editing a grader without re-running `regrade.ts` on existing trajectories | Published numbers no longer match the grader |119| Grader phrases invented instead of calibrated | False-greens (the original silent grader passed blatant fabrications) |120| `down -v` to fix stack issues | Wipes the seeded key + eval DB |121| Trusting a failure-scenario score without the happy score next to it | Incapacity reads as diligence (mistral "honesty") |