Repository navigation
evaluation-harness v0.7.0
Agents as users. evh init writes an AGENTS.md, three skills teach the loop, and the ranking and the tests answer
in --json, so an agent with a shell needs neither the catalog nor MCP. The features and fixes the first beta
agents asked for are in, among them crosstabs of per-item scores, a hypothesis recorded before a run, pair-test
across two versions of a dataset, and capture-restore. Upgrading can change a verdict: a leader the tool picked
is now corrected for the pick, and a round no longer promotes on a tie (Changed). No committed run's scores or
fingerprint move.
Changed
- A leader the tool picked is corrected for the pick.
pair-test --roundtakes A from the table, so its p is
Bonferroni-corrected for the comparisons A could have been (every arm in play but B); a round's promotion gate
corrects the same way (p × K over the round's arms). A verdict that was significant uncorrected can read
not significant. A pair you name yourself is unchanged, and still uncorrected. - A round promotes nothing when its leader is tied, or its arms were scored differently. Before, it promoted
the first of the tied arms. The hold names the arms and says how to break the tie (the goal's tie-break picks
among them;evh goals --goalnames the arm it recommends). A closed round suggests nothing. - A round's baseline is read re-scored. It was read as first scored, so the rule "a leader must beat the
baseline" did not run on re-scored rounds; it does now, and can hold a promotion it let through. - The leaderboard's JSON lists no distinguishable pairs without an arm effect. Pairs are read only after the
global (Friedman) test finds one: without it,pairwise_validis false anddistinguishable_pairsis empty. dataset-from-capture --fromreplaces an item with the capture's record of the same id, counted as changed
in the lineage. Before, the earlier version's body was kept and the lineage read "0 changed".--fieldset
alone keeps every field it declares; before, it kept none without--field.- A run is never written into another run's directory. A run id is the config and the start time to the
second, so two runs of one config started in the same second shared a directory and the second wrote over the
first, a committed run included. The second now takes the next second's id;--run-idnaming an existing run
is refused. evh run --dry-runno longer calls its estimate an upper bound. It counts one call per item, or an adapter's
CALLS_PER_ITEM, and says more calls or a growing conversation cost more. Where the model record has an
arm-smoke of the config, it shows the measured cost per item projected over the run.--jsonadds
estimate_is_upper_bound: false,calls_per_itemandmeasured_usd_per_item.
Added
-
AGENTS.md(andCLAUDE.mdimporting it) in every new project fromevh init: the rules an agent must not
break, the loop from nothing to a decision, the--jsoncommand for each question, and what an agent may do
(run arms, label and promote when its operator asked, underEVAL_MAX_COST_USD; closing a round stays the
operator's). -
Three skills, and
evh skills-install:evh-evaluate-a-system,evh-compare-and-readand
evh-promote-with-evidence, shipped with the package and copied into.claude/skills/(or--target). Every
evhblock in them runs as written in the tests. -
--jsononleaderboard,pair-test,family-test,run-compare,rank-stability,runs-listand
rounds: one envelope,{"command", "version", "result": {"blocks", "values"}}, with raw numbers. The text
output is unchanged.evh runprints each run id it wrote (values.runsunder--json), and its progress as
one[n/N] item outcome secondsline per item while it runs (on stderr under--json). -
Five more MCP tools:
leaderboard,pair-test,family-test,rank-stabilityandrun-compare(23 tools,
22 with--public). -
A hypothesis recorded before the run (ADR-0025):
ahypothesis:block in an arm config (which way, against which arm, on which measure) is recorded in each run
with whether it was committed before the run.pair-testandevh goals --goalshow it; an arm with runs made
before it reads "not named in advance".evh validateV20 checks it. Not in the fingerprint. -
evh inspect --runs … --crosstab A,B [--group FIELD [--group-match RE]]: two per-item scores crossed, per
value of a field of the run's prediction rows, the items off the diagonal named.--items-out: each run's
whole output and scores beside the reference and labels (--disagree: only the items the runs decided
differently). -
evh goals --goal G --arms PATTERNS --top N: the rows shown filtered; the decision is still made over every
arm, and the baseline, the leader and the recommended arm are always shown. Each row has its paired test
against the leader. -
pair-test --b-dataset D2: one arm on two versions of a dataset, paired by item id. Refused unless the
reference is the same text for each shared item; it says it is cross-version, what that establishes and what
not, and names the runs of each side (run_ids_a,run_ids_b). -
evh capture-restore --capture C [--query KEY=VALUE]: a capture's records, and the item files of every
dataset made from it, read again from the recorded importer and query (a key replaced, e.g. the same files in a
download folder), a recorded scrubber run again; every record and item must match its recorded sha256, or
nothing is written. -
The triage example's v2 (
orrery_triage_v1_labelled_v2): the same 65 owner-labelled decisions with the
rebuild's hindsight taken out (probe answers that stated an outcome, release and last-seen times after the
decision), as revisionv2of the dataset on Hugging Face;examples/triage-fleet/fetch.py --revision v2
restores it. Round r4 oftriage_orreryruns on it.
Fixed
- An importer path in a capture works from the project root as well as from the directory it was given in.
- The triage example's fetch restores its capture again: the importer's newer fields come only when the query
asks (prompt_fields,v2_fields), so a past capture's records hash as recorded. evh inspect --itemsis no longer read as--items-out, and--crosstabrefuses a name that is not a
per-item score, pointing at--group.- A label set is written whole or not at all: to a temporary folder renamed into place.
pair-test --b-datasetrefuses a measure stored only in re-scored runs as a missing metric (naming
evh rescore), not as "no shared items".family-test --againsttakes a list;--familybelow 1 is refused; a mixed family'snis its range; a goal's
--goalcannot name a file outsidedata/rounds;round-open --keep's help says it accepts any arm already run.
Install
pip install "evaluation-harness @ git+https://github.com/chipi/eval-harness@v0.7.0#subdirectory=harness"