Evarness guide¶
Audience: new users · about fifteen minutes · basic CLI familiarity.
From install to a signed, offline-verifiable proof bundle. Everything here runs in simulation — no API keys, no network, no side effects.
1. Install and orient¶
pip install git+https://github.com/evarness-ai/evarness
evarness patterns
A pattern is a packaged harness: graph.json (the topology), fixtures/
(scripted scenarios), and a lesson. Three ship built in:
| Pattern | What it teaches |
|---|---|
single_shot_qa |
The minimal graph: input → llm → output, one scripted answer |
governed_email_assistant |
Routing, interception, retrieval, memory, budget-aware assembly — and a failure fixture the policy gate must block |
approval_gated_send |
A send tool behind a human approval gate, with declared invariants |
User patterns live in ~/.evarness/patterns/.
2. Run a graph and read the trace¶
evarness run path/to/graph.json --fixture path/to/fixture.yaml --json
A run is an event stream: run_started, intent_routed, llm_request,
policy_violation, run_finished, … Failures, blocks, and budget overflows are
first-class events — never silent. The run's identity is its canonical
digest:
canonical formstrips the wall-clock envelope and serializes byte-stably (sorted keys, compact, ascii).trace_digest(events)names it:c1:sha256:…. Thec1names the canonicalization version — anything that changes canonical output bumps it.- Same graph + fixture + seed ⇒ same digest. On your laptop, in CI, on any OS.
Export the canonical stream for other tooling with
run --trace-out trace.jsonl (or --trace-format otlp for an
OpenTelemetry/GenAI-semconv document).
See the run: the render artifact¶
evarness run path/to/graph.json --fixture path/to/fixture.yaml --html run.html
evarness render path/to/graph.json # static canvas, no execution
--html writes the run as one self-contained HTML file: the graph as a
canvas, a playhead that replays the canonical events (a blocked run visibly
stops before the model), and the invariant verdicts in a separate judgment
pane. The rules you already know apply to the artifact itself:
- No external requests, ever. Inline SVG/CSS/JS, a CSP that pins
default-src 'none', and hostile fixture content renders inert. Open it anywhere, attach it to a bug report, email it to an auditor. - The digest travels inside. The embedded data island contains the
canonical events verbatim — recompute
trace_digestfrom the artifact alone and it must match the digest in the provenance bar. - Evidence and judgment stay separate. Verdicts live in their own pane
and their own data key; a failing verdict's
seqlinks scrub the playhead to the cited event. - A not-established footer. Every artifact states what it does not prove, including honest lines for paused runs and zero-contract runs.
Artifacts are byte-stable for deterministic runs and stamped with a renderer
version (r1) — derived evidence, deliberately outside the c1 digest
contract. Bring your own format with register_renderer (io/render.py).
3. Declare invariants — contracts, not prose¶
The guarantees a harness claims live in an invariants.yaml, resolved from the
pattern directory, then ~/.evarness/invariants.yaml, then the packaged
library. A contract is a temporal assertion over the event stream:
invariants:
no-model-call-after-block:
title: No model call after a block
assert:
never:
type: llm_request
after: {type: policy_violation}
approval-precedes-send:
title: Approval precedes send
assert:
precedes:
first: {type: approval_granted}
second: {type: tool_called, where: {tool: email.send}}
A graph opts in via params.invariants: [id, …]. Verdicts are computed from
the events but stored outside them — checking a run never changes its
digest. Unknown or malformed contracts are failed verdicts, never silently
skipped.
4. Prove, then verify anywhere¶
evarness prove approval_gated_send --approve n3=approve -o proof.json --html report.html
evarness verify proof.json
prove runs every scenario twice (digest reproduction demonstrated, not
asserted), evaluates the declared contracts, and pins the subject: graph hash,
tool-manifest hashes, resolved contract-definitions hash, engine version, and
environment. The bundle carries the canonical event streams and a rolling
per-event chain digest.
The verdict's ok is tri-state: true (every declared claim checked and
held), null (PENDING — a scenario paused at a human gate, so nothing was
checked and nothing is claimed; the CLI exits 1 and the CI projections carry
it), or false (a contract failed, a digest didn't reproduce, or nothing was
declared). Without --approve, approval_gated_send pauses by design — you
get PENDING, not a proof.
verify re-checks all of it offline: digests recompute, chains recompute,
verdicts consistent, signature valid (if present). A bundle with no declared
invariants verifies as "nothing asserted" — a loud state, not a quiet pass.
For CI: prove --junit verdicts.xml (test report) or --sarif findings.sarif
(code scanning); the exit code gates the merge. Sign with prove --sign
(Ed25519, key auto-created under ~/.evarness/keys/) and pin the signer with
verify --pubkey … --require-signature.
Browse a bundle¶
evarness render proof.json -o proof.html
The proof browser is the render artifact applied to a whole bundle: the
tri-state verdict badge, then one viewer per scenario — canvas, playhead over
that scenario's canonical events, reproduction status, and its invariant
verdicts — with the bundle's not_proven section rendered verbatim as the
footer. Same self-containment guarantees as run --html, plus three rules of
its own:
- The canvas never lies about identity. A graph is drawn only when its
hash matches the bundle's pinned
graph_sha256: the pattern named by the bundle is auto-resolved and hash-checked;--graph PATHis hash-checked and a mismatch is a loud error; no matching graph means the canvas is honestly omitted, never substituted. - The page never vouches for itself. A signed bundle is labeled
"signature NOT checked by this page" — checking is
evarness verify's job. - The island IS the bundle. The full bundle rides in the data island:
extract its
bundlekey to a file andevarness verifyre-checks it offline, signature included. The HTML file alone carries a verifiable proof.
Export a bundle¶
evarness export proof.json # -> proof-export/
Unpacks a bundle into the standard interchange set for other frameworks and
pipelines: per-scenario canonical JSONL (the digest input, line by line) and
OTLP traces (--format repeatable — any registered exporter format works),
JUnit/SARIF verdicts, and a manifest.json carrying the subject, verdict,
not_proven verbatim, and a sha256 receipt for every file. The bundle is
verified first; a bundle that fails verification is refused with nothing
written — export never launders a tampered bundle into clean-looking files.
5. The honesty rules¶
Worth knowing because they shape every command's behavior:
- Refuse, don't degrade. Real providers (
anthropic:…,ollama:…) andmode: realtools are refused with remediation — they haven't shipped in this release, and a silent sim fallback would make the trace lie about what ran. - Side effects need explicit approval. A tool whose manifest declares
write/destructiveside effects refuses to run unless the node setsapprove_side_effects: true— even in simulation. - Every bundle states what it doesn't prove. Scripted scenarios ≠ universal
safety; a signature ≠ the runs happened. The
not_provensection and theverifyoutput say so explicitly.
Data locations¶
~/.evarness/ holds the activity log (evarness.db, override with
$EVARNESS_DB), user patterns, user overlays (invariants.yaml,
prompts.yaml, tools.yaml, …), and signing keys. The overlays merge over the
packaged defaults; every one has an $EVARNESS_* env override.