Upgrading¶
Two cases. If you pin a release, read the section for each release you cross, below:
change the pin, then check what it says can move. If you copied harness/ into your
repository before October 2026, read the rest of this page first: your copy has
correctness bugs, not just missing features.
From 0.6.1 to 0.6.2¶
Change the pin to @v0.6.2. No format changes, nothing to migrate, and no answer changes: views and the catalog build
read each arm's scores once, and evh mcp can serve answers written ahead (--write-answers, --answers).
From 0.6.0 to 0.6.1¶
Change the pin to @v0.6.1. No format changes and nothing to migrate: no run's score or fingerprint moves.
What can read differently (the changelog, Changed): the sheet's win rates and verdict; a goal recommends only from the group it decides on; a config's place in a goal's field counts one place per config, without the arms a round retired; and the catalog's run and spend figures count runs re-scored in place, once each.
From 0.5.0 to 0.6.0¶
Change the pin to @v0.6.0. Two formats change, and both are migrated by a script, never read in the old shape:
- Fingerprints are version 5 (RFC-021):
instrument,data,deployment,settings, each hashed, and the machineevhran on asrunner, outside the hash. A run made with 0.6.0 compares with a v4 run on no hash. Move your committed runs once, from your project's root:evh fingerprint-migrate(--dry-runcounts first; it is idempotent and prints its counts; a baseline'sfingerprint_hashfollows its run). Code that readfingerprint.hostreadsfingerprint.runner;fingerprint.system_under_test.modelisfingerprint.deployment(an adapter'sfingerprint(params)hook keeps its vocabulary; what it declares about the prompt is nowsettings.declared). data/models.yamlis replaced bydata/models/<model_id>.yaml(RFC-020): one record per model, its key in the old file the record's alias. Write a record per entry (model_id,kind,aliases,weights,licence) and delete the file;validate(V1, V18) checks them.
Reference sets are labels you promote (RFC-023, ADR-0022). reference-create and reference-from-outcomes are
removed. A run's outputs become a model's labels (evh label-from-run: the same refusals, and --not-redistributed),
a sheet or a folder of answers a person's (evh label-sheet, evh label-import), a capture's outcomes a rule set
(evh label-from-capture, the same rule language); evh reference-promote makes a reference of a set, at the tier
you choose, with a reason and who promoted it. A reference made before has no promoted_from and fails validate
(V1, V16) until it records one: harness/tools/migrate_references_to_labels.py, run from a checkout, records how each
was made and copies its texts into a label set, no run's score or hash moving. A reference made from outcomes has no rule of its own any
more: promoted, it is a silver like any (with gold beside it, gold still decides).
Renamed to the field's words, with no alias (the old names stop working):
- The command
experiment-runisevh run, and its make targetmake run(wasexperiment-run). What 0.5.0 called an experiment is an eval; the site'sexperiments.htmlredirects toevals.html. - The paired test's verdict is
significant: better,significant: worseornot significant(wasseparated: …andnot separated), inevhoutput, the catalog, the inspector and the site;pair-testprintsSIGNIFICANTorNOT-SIGNIFICANT,family_testandbootstrap_testSIGNIFICANT. A script that matched the old strings matches the new ones. - A goal's arms that may win the tie-break are its contenders: the decision's key is
contenders(wastied), andevh arms --quality contender|below(wasties|below).
A goal's dataset role: is free text and keeps whatever you wrote; the docs now say validation, test and pilot
(were dev and heldout).
Seven behaviours change without a migration:
-
The
--itemsflag ofevh inspectis gone, replaced by the sheet:evh inspect --sheet --dataset-id D --runs A,Bshows every item's scores per arm, beside what each wrote, the labels and the winner (--scores-onlyfor the scores alone). A script that parsed--itemsreads--sheet --csv FILEinstead. -
A reference folder with no manifest fails
validate(V16). Texts copied intodata/references/<tier>/<id>/by hand said nothing of where they came from, and runs scored against them as if promoted. Import them as a person's labels where they are and promote them (evh label-import data/references/<tier>/<id> --dataset-id D --set S --by WHO, thenevh reference-promote … --tier … --reason …): the promotion adopts the folder, its texts being the labels', so no run's reference moves. evh reference-manifestis a promotion too: it takes--labelled-by(whose labels a script builds),--reasonand--by, and records them aspromoted_fromwith the reason; a call without them is refused.-
Each project serves on a port of its own:
catalog-serveon one in 18000–18999 andmcp --httpon one in 19000–19999, the same every time for a project's folder, the next free one if taken (--portoverrides;catalog-serve --publickeeps 8765). Bookmarks of 127.0.0.1:8765 move; the command prints the address. -
The local catalog reads only what git keeps. A
data/runs-*directory your.gitignoreignores is no longer shown, as it was never in a clone or the published catalog. Runs you meant to keep: re-allow the directory in.gitignoreand commit them, or move them intodata/runs. evh runandrescorewarn when they write to a directory git ignores or outside the project's data (throwawaydata/runs-smoke*anddata/runs-tune*stay silent), andvalidatenotes one that holds finished runs.evh rescorewrites to one designated place,data/runs-rescored/<scorer>/<source>/, unless--outnames another; the catalog, the goals and the rounds read an arm from its latest re-score there.
Everything else is added and optional: the capture lane (evh capture, importer-check, captures,
capture-scrub, dataset-from-capture, dataset-split, text: hidden; RFC-019); evaluating what production did
(evh labels, a label accepting more than one answer, {"accept": [...]}, read by answers(reference) from
evaluation_harness.adapter, scoring against a label set with --labels, evh arm-from-capture, inspect --disagree and
the sheet (inspect --sheet, --csv, --fill, read back by evh sheet-import), evh pick, evh picks,
evh dataset-extend, evh deploy-sheet; the catalog's Labels page; an adapter's optional same(a, b),
decision(output), title(item), recorded_output(record) and production_ran(record); RFC-023); judging modes (a scorer's
mode: pick or pairwise, validated in that mode; RFC-005) and an adapter's optional primary_metric(params);
model onboarding (evh model-onboard, evh arm-smoke, evh models, model_record: and deployment: in a
config; RFC-020); scoring that evolves (evh score-preview, evh rescore --goal --round --why,
round-open --keep all --scoring and --adopt, a goal axis's limit: baseline; RFC-022); call_timeout_s for
the built-in providers. evh init's .gitignore now ignores data/runs-smoke*/ and data/runs-tune*/; add the
two lines to an existing project's to keep scratch runs out of git.
From 0.4.4 to 0.5.0¶
Change the pin to @v0.5.0. No record, hash or p-value moves. Three things to check first:
- Python 3.10 or later. The package's floor moves from 3.9, because the official MCP SDK
needs 3.10. On 3.9, stay on
@v0.4.4. - One new dependency,
mcp>=2.3,<3, installed with the package like every other (it bringsstarlette,uvicorn,pydanticand a few more; the CHANGELOG lists them). A lock file or a vendored install changes. evh mcpwrites one file:<project>/.cache/evh-p.jsonl, the goals' paired tests kept between calls (EVH_P_CACHE). Add.cache/to the project's.gitignoreif it is not there. Deleting the file only makes the next call slower.
A dataset id must now be a name (letters, digits, ., _, -): an id with / or .. is
refused by every command that reads or writes a dataset's file. An arm's params as a page or
evh arms --arm shows them hide a URL's user and password and a nested credential-named key
(headers: {Authorization: …}), so a page can differ from 0.4.4's where a config held one.
Everything else is added and optional: evh mcp, --json on the views, evh catalog --mcp; evh dataset-profile and a committed data/profiles/<id>.json give a dataset page its
"what's inside"; an adapter may answer dataset_profile(dataset).
From 0.4.3 to 0.4.4¶
Change the pin to @v0.4.4. No record, hash or p-value moves. Added and optional: evh catalog
--about FILE and --repo URL, and a goal's links are shown. A catalog built as a command now
puts --brand, --link, --head and --api on the arms page too, where 0.4.3 left them out.
From 0.4.2 to 0.4.3¶
Change the pin to @v0.4.3. No record, hash or p-value moves. evh catalog-serve answers 404 to
any /api/ path that is not one of its endpoints, where 0.4.2 could serve a file through it. A
--hooks module now sees such requests as the route /api/other.
From 0.4.1 to 0.4.2¶
Change the pin to @v0.4.2. No record, hash or p-value moves. One command line changes:
evh overview no longer takes --all: evh overview --period all says the same (--period P on every list now,
counted from today). Everything else is added and optional: an adapter may answer
dataset_created(dataset) and model_facts() (each model's weights, open or proprietary; the
harness's own adapter reads an optional data/models.yaml), spend() events may name an arm,
and catalog-serve --hooks takes a deployment's error reporting, tracing or metrics. evh
bootstrap-test now refuses when the gold references are not on the machine, where it read none.
From 0.4.0 to 0.4.1¶
Change the pin to @v0.4.1. Nothing moves: the same records, hashes and p-values. The inspector
fits in a 256 MiB container again after --warm on a large catalog, and evh catalog gains --head,
--api and --brand.
From 0.3.0 to 0.4.0¶
Change the pin to @v0.4.0. One install is everything: openai and anthropic, the
clients of the hosted providers and the judges, are core dependencies now, and the [llm] extra
is gone. If your pin names it (evaluation-harness[llm] @ git+…), drop [llm]; pip warns about
an extra that no longer exists. Nothing else is added. 0.4.0 reads only its own record format,
so check these before you trust a validate:
- Fingerprints are version 4: documentation never moves a hash. Code is hashed by what it
does, its syntax tree without docstrings, comments, formatting or
METRIC_DESCRIPTIONS: your adapter, the harness's measuring modules and a judge scorer's own hash. A judge scorer YAML is hashed by its parsed content, and a reference manifest'scaveatis outside the reference digest. Before, adding metric descriptions to an adapter maderun-compareand the inspector call every earlier run "not the same conditions". v3 and v4 digests are not comparable: a run made on 0.3.0 and one made on 0.4.0 differ in their adapter, scorer and reference hashes even where nothing changed, and 0.4.0 ships no command that rewrites old runs' hashes (this repository's own were migrated once, by matching each recorded hash to the code it came from). Compare runs made under the same version, or re-run the arms you compare. No score changes. -
import-runhashes the scoring code itself:--scorer-filereplaces--scorer-sha256. Pass the file (or several, repeated in a fixed order); the harness hashes it by the same rule, so a docstring in your scorer is not a new scorer, and the hash is the run'sinstrument.adapter.sha256, inside the fingerprint hash. Before, it sat inimported, outside the hash, so two imports scored by different versions of the code looked like the same conditions. A script that passed--scorer-sha256is refused (exit 2): pass the file. -
Derived runs (rescore, judge-score) record
derived: {kind, from, at, with}; the 0.3.0 fields (rescored_*,judged_*,judge_label,primary_metric_asc) are read by nothing. Re-runrescore/judge-scoreon the runs you keep. - Baselines carry the run's reference tier, adapter and fingerprint hash, and V6 fails
one that does not: promote each again (
evh run-promote).data/baselines/superseded/is no longer written; git keeps a replaced baseline. - Configs may hold only
config_id,dataset_id,params,adapter,goal,roundandformat(V1); anything else was read by nothing. - Run records may hold only the fields the harness writes (V1). A run imported with
0.3.0's
import-run --asccarriesprimary_metric_asc, which no version read: the metric was ranked higher-is-better unless every command passed--asc. Replace it withmetric_directions: {<metric>: lower}(or re-import with 0.4.0, which writes that), and--ascbecomes unnecessary. - Judge verdicts need
output_sha256andoutput_withheld(V1): the hash of the stored reply, and false. - Judge scorers state both controls. A scorer's
protocolmust setposition_check_fractionandnoise_check_fraction(ADR-0001 §4): the noise check above 0, and the position check above 0 whenoutputs_per_callis above 1. A scorer without them is refused. Before, a scorer that left one out ran the pass with that check off. Add both (0.5 each is what the tutorial's scorer uses). --resumetakes a run made by 0.4.0 (rows with_extra);--resume-unverifiedis gone.evh judgeis the judge panel (it sat beside the package and could not run from a pin); the tutorial kit shipstutorial/judge_panel.yaml.- Your adapter: put it at the project root as
adapter.pyand an arm with noadapter:uses it. Code that importedcatalog_adapter.load_adapterimportsload_catalog_adapter; a script that relied on importing the package to read.envloads it itself (evaluation_harness._common.load_dotenv()).
What can move a number:
- pair-test and family-test on 30 items or fewer now count the exact p over all 2^n
sign patterns instead of estimating it from 20,000 permutations. Re-run them on any pair
with 30 or fewer shared items: a verdict that sat near its threshold can move. Above 30
items nothing changes, bit for bit. pair-test prints which it did.
- family-test's cache recomputes once: its key includes the new stats.py.
- validate V4 now fails a materialized copy that has lost an item file, and names each
dataset it could not check. A project whose copy is incomplete goes red where it was green.
- The direction of a metric is decided once, for every command and the inspector: a flag,
else the goal your runs name, else the adapter's new METRIC_DIRECTIONS (or import-run
--asc), else the name (costs, latencies and token counts go down). Before, pair-test's
"A wins", family-test's wins and "ahead", the leaderboard's order and its significance
block, holdout and rank-stability counted the higher score unless you passed --asc
(holdout and rank-stability had no flag), and a goal's direction was read by the goal
page alone. p-values do not change: the tests are two-sided. --asc and --desc override.
validate (V14) now fails a goal whose direction contradicts what its runs record.
- validate checks every committed runs* directory (V7, V8, V9), read from git: it
checked only this repository's six directory names, so a project's own data/runs-adjudicated
was never validated. Declare the list, floors, scorer sources and duplicate exceptions in
<data>/validate.json (RUNBOOK §9), which replaces validate_floors.json: move its
floors there.
- The core times every call. latency_ms is recorded for any adapter that does not report
its own (the attempt that answered, not the retries' backoff). A project's runs gain a latency
column, and the leaderboard its cost/speed frontier.
- validate V5 no longer fails two different arms that are both perfect on a binary metric:
their outputs tell two measurements from a copy. A tree that failed only on that goes green.
- provider: openrouter calls OpenRouter by default, with OPENROUTER_API_KEY. Before, an
arm that named it without base_url and api_key_env read OPENAI_API_KEY and called
OpenAI's address. An arm that sets either keeps its own; one that set neither now calls a
different endpoint, so treat its next run as a new measurement.
- A judge goes where its provider: says, and every judge must declare one. Judges used
to ignore it: a LiteLLM proxy when LITELLM_BASE_URL was set, else OpenRouter with the
model looked up in $EVAL_MODELS_FILE or <project>/models.json. Now the judge is called
through the same providers as an arm, with the model as written, and the harness reads no
models table. A scorer whose judges went to OpenRouter that way names provider: openrouter
and the OpenRouter id (vendor/model) the alias stood for; that is a new judge identity, so
its validation must be measured again. The committed scorers declare provider: litellm:
re-running them needs a LiteLLM proxy (examples/litellm.config.yaml serves their aliases).
- Judging can have its own keys. A judge reads every variable as EVAL_JUDGE_<NAME> first,
then <NAME>; with no EVAL_JUDGE_ variable set it shares the arms' keys, as before.
- The judge panel's judges are declared (id, provider, model, usd_per_mtok_in,
usd_per_mtok_out) where its config named a model string, and the fake judge runs only with
--use-fake-judge: a missing ANTHROPIC_API_KEY used to swap it in silently.
- leaderboard prints one table per way of scoring. When a dataset's runs were scored more
than one way (silver and gold, a judge, or runs made before any reference existed), each gets
its own table and tests, gold first, under === scored against <ref> === (judged runs:
=== scored by judge <scorer_id> ===), and nothing is averaged across them. Before, they were averaged into one row and one ranking. The
catalog and the inspector read an arm against the reference of its latest run and say which
earlier runs they leave out; evh rounds reads a round against the reference most of its arms
use and names the arms it does not compare. With one reference the output is unchanged.
- The bundled adapter stores overlap_f1 unrounded (it rounded to 6 decimals); a new run's
mean can differ from an older one's in the sixth decimal, and the leaderboard's rounding note
no longer fires for it.
- --exclude-dataset (pair-test, family-test, holdout, bootstrap-test) reads the
excluded dataset's own item list. It read data/sources/<id>/*.txt, so a dataset whose items
are not .txt files there was excluded wrongly, or not at all.
New, all optional:
- The catalog and the run inspector (evh catalog, evh catalog-serve). On the
harness's own data layout they need no code; with another layout, write one adapter
(tutorial 5). Every
view is also a command: evh overview, evh datasets, evh arms, evh references,
evh metrics, evh goals, evh search, evh inspect (with --items for the per-item table
and --analysis for the charts as numbers). The home page is now a dashboard (index.html
moved, the datasets table is datasets.html). Metric links now go to m/render_inspect gets the arm and metric links only when the built pages
exist (pages=True parameter).
- Goals (data/goals/<goal_id>.yaml, goal: on configs, validate V14). Nothing is
required of an upgrading project until it sets required_from in data/goals/policy.yaml.
- Eval rounds (evh round-open, evh round-retire, evh round-close, evh rounds;
round: rN on configs; validate V14). --round rN on leaderboard, family-test,
pair-test and inspect: inspect --round and pair-test --round default to the goal's
measure and the round's leader (pair-test's B: the baseline the round started from). A
project that never opens a round sees none of it
(tutorial 4).
- A run becomes a reference: reference-create --from-run <run_id> copied a finished
run's outputs and called nothing; the reference recorded the run, and it refused an empty output and
outputs that stopped at max_tokens (--allow-cut-off). (Removed in 0.6.0: evh label-from-run, then
evh reference-promote.)
- Dataset versions keep their lineage: evh dataset-create --dataset-id x_v2 --from x_v1
(derived_from in the dataset file; validate V2).
- Item fields and URL items (data/fields/<fieldset_id>.yaml, dataset-create --fieldset
--fields, --manifest, validate V15). The dataset schema only gains optional keys, so
every 0.3.0 dataset is still valid; a dataset that uses them needs 0.4.0 to validate. If
you keep a second registry of facts about your items beside your datasets, this replaces
it: declare the facts as fields and attach them with --update-fields, which changes no
run's fingerprint.
- success_criteria.secondary is removed; goals weigh axes (ADR-0006). A goal that
still has secondary: fails evh validate (V14). Move each entry to axes:, keyed by its
measure:
# before
secondary:
- {measure: rouge1, direction: higher}
- {measure: usd_per_min, direction: lower}
# after
axes:
rouge1: {direction: higher}
cost: {measure: usd_per_min, unit: USD per audio minute} # a built-in, another measure
A threshold becomes a note (shown), or a limit if an arm past it should be out of the
recommendation. An entry with no number to compare (a human review) belongs in
guardrails ({name, rule, source}), which are shown and never computed, as before. Cost
and speed need no entry. Without a strategy a goal is balanced.
Also changed: pair-test, family-test, holdout and rank-stability read the
primary metric your runs recorded when no --metric is given. They used to default to
coverage, the summarisation example's metric, and said "no runs" or crashed in any project
that does not score it. Pass --metric as before to choose another.
Also:
- A run's instrument.harness.code_sha256 hashes only the code that can move a number (the
run-writing commands and what they import), not the package's tests and read-only
commands. Runs made on 0.4.0 record a different value from 0.3.0 runs, as any upgrade
does; run-compare names it.
- The bundled adapter refuses an unknown provider: (it fell back to echo, so a typo
recorded the echoed input as the model's output) and records what an OpenAI-compatible
provider billed when the response says so.
- Every command refuses with ERROR: … on stderr and exit 1, and refuses an argument it
does not take with exit 2 (validate, self-test ignored them). Exit codes: RUNBOOK.
- import evaluation_harness.judging works, and the package's modules load once each.
- The wheel holds the package's code only: this repository's tests and report checkers
moved to harness/tests/ and harness/tools/ in the checkout.
After upgrading: evh self-test (in a project it checks that the installed harness
works here: its paired test, and the tutorial end to end in a scratch folder), evh
validate, and re-run the small paired tests.
Upgrading a copy of the harness taken before October 2026¶
If you copied harness/ into your own repository before the review rounds of late
September and October 2026, your copy has correctness bugs that can change results, not
just missing features. This page lists what changed since the harness was extracted (the
first commit of this repository, fb7366a0, 2026-09-26), in the order you should care
about it, and how to upgrade.
1. Fixes that may have changed numbers you already have¶
Check each against how your copy was used. A "yes" means re-run or re-score the affected runs after upgrading. Re-scoring from stored outputs costs nothing.
| if you… | the bug | fixed in |
|---|---|---|
used pair_test --family N and called it Holm |
it was Bonferroni labelled as Holm (more conservative, so some real significant differences were reported as not significant). family_test.py now does the genuine step-down; pair_test says what it does |
a57d778b |
| had a run crash or stop mid-sweep | every paid item before the crash was lost: outputs reached disk only after the pass. Now each output is written as it arrives | c6e8365f |
resumed a run (--resume) |
a fully resumed run wrote empty scores and reported success; replayed items lost their cost, tokens and diagnostics (a resumed run under-reported spend: $0.03 for $0.05 real); --resume accepted another arm's outputs without complaint |
c6e8365f, 897c0bbc, 3f16a79b |
| ran out of disk, or killed a run | writes were not atomic, so a half-written output was replayed as the model's answer and scored | 8e98a885 |
| re-scored against a gold set that had changed | the rescored run asserted the old reference hash while scoring against the new files | ebd54b60 |
used a cost cap (EVAL_MAX_COST_USD) in scripts or CI |
a cap that stopped the run before any item exited 0, leaving an empty run that looked complete. Exit codes are now 0 complete, 2 stopped early, 1 nothing measured | 2b6a129a |
ran silver_calibrate on exactly 3 arms |
it crashed; the minimum is now stated as 4, with the reason | ebd54b60 |
read dirty: true on your runs |
every run after the first on a machine was marked dirty, because earlier run directories are untracked files. Untracked paths under data/runs*/ no longer count |
9ec2513a |
| relied on parser hashes | the scoring-code hash covered the function but not the module-level regexes it reads, so a parser change could leave the hash identical. Copy examples/_shared/codehash.py and hash with code_digest(fn, consts=…) |
3f16a79b, c6e8365f |
2. Changes that break an old adapter or an old run¶
- Remove any retry loop from your adapters. The original
adapter.pytemplate had its own_with_retries. The core now retries everycall_system, so an adapter that kept its loop retries a failing item 8 × 8 = 64 times, each billed. To make extra error messages count as transient, declareTRANSIENT_MARKERS = (...)in the adapter; the core honours it. - The adapter contract grew (all optional, all recommended):
warmup(params): load models and prove the endpoint answers before the first item, so warm-up is excluded from latency and a bad key fails before any spend;fingerprint(params): what the arm really ran (the model behind an alias, the weights' revision, prompt hash, scoring-code hash);PRIMARY_METRIC: the metric the leaderboard ranks by;METRIC_KINDS:qualityvsdescriptive, so diagnostics such as parse rate and truncation are not ranked as if they were quality;METRIC_DIRECTIONS(0.4.0):{"wer": "lower"}for a quality metric that improves going down, which a name cannot tell the harness.- The run directory gained files written during the pass:
run.json(written before the first item) andoutputs/_rows.jsonl(appended and fsynced per item). Old runs without them still read, but cannot be resumed safely. - Read cost from the provider's bill, not a price table. The example adapters take
OpenRouter's
usage.costwhen it is returned (examples/_shared/gateway.pyasks for it) and fall back to the config's per-token prices. The price table was off by 0.67×–3.21× per arm when checked. This lives in the adapters, not the core, so an old adapter keeps estimating from prices until you copy the pattern.
3. New tools worth adopting¶
| command | what it gives you |
|---|---|
make family-test DATASET_ID=… A=… AGAINST=… |
one arm against a declared family, genuine Holm, with a result cache |
make leaderboard |
Friedman permutation test and Nemenyi critical difference before it prints an order |
make rank-stability |
how many items a ranking needs, measured by splitting into disjoint halves |
make holdout |
tune on a pilot slice, confirm on the held-out rest |
make bootstrap-test |
intervals for corpus-level metrics (with the caveat that it is anti-conservative) |
make cost-report |
billed cost and projections per arm |
make validate / make check |
schema and tree validation that exits non-zero, for CI |
pair_test / family_test --items FILE |
test on a declared item subset (a stratum), the same ids everywhere |
make judge-score / evh judge-score (0.3.0) |
optional: re-measure stored outputs with LLM judges, refused until validated against human ratings; any text task, declared in the scorer (RUNBOOK §12) |
4. What not to copy¶
These exist for this repository's public showcase and assume its six evals, its
reports and its site: check_report_claims.py, check_model_facts.py,
check_terminology.py, check_links.py, summeval_analysis.py, the per-task
*_report.py scripts, and the site/docs Makefile targets. Copy a report script only if
you run the same task; it is an example, not part of the core.
5. How to upgrade, and stay upgraded¶
Copying the directory once is what caused this page. Since version 0.2.0 the harness is an installable package, so a project can pin it the way it pins any other dependency:
pip install "evaluation-harness @ git+https://github.com/chipi/eval-harness@<commit>#subdirectory=harness"
evh --help # the Makefile's targets, as commands
- Pin a commit, not a branch. An upgrade is then a one-line change to the pin, and
every run records the harness
versionand acode_sha256of its code infingerprint.instrument.harness. A run'scommitis the commit of YOUR project, so those two fields are what say which harness produced a number. - Keep the data in your project. The package holds code and schemas only. Run the
commands from your project root, or set
EVAL_HOMEto it;EVAL_DATA_DIRmoves the data root if your layout is not<project>/data(for exampleEVAL_DATA_DIR=data/eval/stats). - Bring existing results in without re-running them. If your project already has
outputs scored by its own code,
evh import-runwrites those per-item scores as a run the significance tests read (pair-test,family-test,leaderboard,rank-stability). Lower-is-better metrics take--asc. - Delete the copy. Remove the copied
harness/scriptsand callevhinstead. Your adapters, configs and report scripts stay where they are, outside the package, so an upgrade never conflicts with your work. - After every upgrade, run
evh self-testandevh validate, then re-score your committed runs (evh rescore) and compare. A number that moves is either a fixed bug from §1 or a change you need to understand before trusting new runs.