evalctl
Local-first evals for agents, not just prompts.
evalctl treats eval cases as files, runners as shell commands, and results as durable artifacts. It scores what agents actually do — files written, diffs produced, commands run — on your own machine, with no gateway, dashboard, or SaaS account.
v0.4 writes durable run metadata, supports crash resume, adds local run-state inspection, diagnoses runtime health, produces side-effect-free execution plans, and can optionally delegate runner execution to spoolctl. The standalone synchronous path remains complete and requires no external service. inferctl preflight provenance can be captured before runner execution without changing report scoring.
About
evalctl is an agent-first evaluation harness for local workflows. It runs eval cases as filesystem fixtures, invokes agents through ordinary runner commands, and grades the resulting workspace with deterministic scorers. The core artifact is a portable run directory that another agent can inspect, report on, and re-score without access to the original shell history.
Keywords: agent evals, LLM evaluation, AI agents, local-first, CLI, code review, workspace diff, deterministic scoring, artifact replay, eval harness.
How it differs from promptfoo
promptfoo is the incumbent local eval CLI, and it is prompt/chat-shaped. evalctl is agent-shaped.
| Axis | promptfoo | evalctl |
|---|---|---|
| Unit under test | prompt → completion | agent run → files, diffs, command logs, artifacts |
| Scoring surface | text of a response | resulting workspace: git diff, expected/forbidden file changes, exit codes, plus text |
| Execution | in-process, synchronous | v0.4 synchronous by default; plan previews actions, doctor diagnoses state, run --resume resumes crashed runs; optional --queue spoolctl delegates runner execution |
| Model context | provider API keys | Optional inferctl preflight provenance through run --inferctl-task; route capture remains deferred |
Status
Python pre-release. v0.4 provides scaffold, validate, doctor, plan, bounded
parallel run execution, durable run metadata, crash resume, bounded local jobs
inspection, optional spoolctl queueing, optional inferctl preflight provenance,
status, report, deterministic local scorers, CLI authoring verbs, execution
replay for failed cases, command scorers, truthful warnings/errors, real schema
output, and artifact replay from a copied run directory.
contract_version remains 1.
Install
Requires Python 3.11+ and has no runtime dependencies (standard library only).
From source:
pip install "git+https://github.com/evalctl/evalctl.git"
For development, clone and install editable:
git clone https://github.com/evalctl/evalctl.git
cd evalctl
pip install -e .
Quickstart
Scaffold a project, author a suite, run it, and read the report:
evalctl init --json
evalctl suite add demo --runner-argv "python3 $EVALCTL_WORKSPACE/r.py" --json
evalctl case add demo --task "do X" --workspace fixtures/x --expect-json '{"exact":"ok"}' --json
evalctl scorer add demo --name exact --required --json
evalctl run demo --json
evalctl doctor --json
evalctl plan demo --json
evalctl run demo --inferctl-task code --json
evalctl run --resume <run-id> --json
evalctl jobs list --limit 50 --json
evalctl run demo --queue spoolctl --slots 4 --json
evalctl replay --failed <run-id> --json
evalctl report <run-id> --format json
Durable Runs
Every run writes run.json before executing cases and writes
cases/<case_id>/state.json only after the case artifacts needed for reports are
complete. manifest.json is finalized from that durable state. If a process is
killed mid-run, evalctl run --resume <run-id> --json reuses the original
suite snapshot and run parameters, skips terminal cases, deletes partial
unfinished case directories, and executes only the remainder.
Reservations are liveness-only .reservation.json files with a TTL and
background heartbeat. A live reservation returns E_RUN_BUSY; a stale
reservation is reclaimed by explicit --resume. jobs list|get|prune inspects
completed, running, stale, and orphaned local run state and safely prunes only
with explicit confirmation.
jobs list is bounded by default. Use --limit and --cursor to page through
large run directories; the JSON envelope includes pagination metadata and a
paste-ready next-page command when more rows are available.
Durability sidecars are operational state. Reports and artifact replay do not
require run.json, .reservation.json, .spoolctl.db, state.json, or
job.json; report_hash stays based on the report projection. SOURCE_DATE_EPOCH
controls created_ts for deterministic manifest parity.
Doctor And Plan
evalctl doctor --json reports runtime, suite root, runs root, reservations,
spoolctl, inferctl, and runner-safety state without failing just because a
component is degraded. Use --component NAME to scope diagnostics and --fast
for PATH-only optional integration checks.
evalctl plan <suite> --json resolves the case set without creating run
directories, enqueueing jobs, executing runners, scoring, or writing inferctl
artifacts. The plan includes run-id strategy, execution mode, independent-case
dependency metadata, parallel tracks, per-case actions, warnings, and
paste-ready follow-up commands. --resume, --queue spoolctl, --slots, and
--inferctl-task mirror the run surface for planning.
Optional Spoolctl Queue
evalctl run <suite> --queue spoolctl --json delegates only runner execution to
spoolctl (>= 0.4.11, speaking contract >= 2). A spoolctl reporting a newer
contract is accepted. Evalctl still prepares workspaces, normalizes stdout and
stderr, captures workspace diffs, scores cases, and writes terminal markers. If
spoolctl is absent or incompatible, queued runs fail explicitly; non-queued runs
do not need spoolctl.
The queue database is per-run at .spoolctl.db; v0.4 starts one ephemeral
spoolctl work --drain worker per queued run. General externally managed worker
fleets are not part of this release.
Inferctl Preflight Provenance
evalctl run <suite> --inferctl-task TASK --json probes inferctl once per run
and, when compatible preflight support is available, writes per-case
inferctl-preflight.json and inferctl-provenance.json before runner execution.
Queued spoolctl runs capture the same artifacts before enqueue. Absence,
incompatibility, parse failures, timeouts, and policy/readiness blocks are
warnings; the runner still executes and scoring proceeds.
The v0.4 capture mode is preflight only. inferctl route is not called, and
report projection is unchanged, so report_hash remains comparable to an
equivalent run without inferctl.
Authoring
suite add, case add, and scorer add let agents build a suite without
hand-editing suite.json or cases.jsonl. Authoring verbs are idempotent on
retry: adding the same canonical object returns created:false; reusing the
same key with different content returns E_RUN_CONFLICT.
case add only writes paths under the suite tree. Absolute paths and ..
segments are rejected so generated suite files remain portable.
Replay
replay --failed selects failed/errored cases from the source run's recomputed
report projection, not from score.json or stored manifest status. It re-runs
only those cases against the current suite and writes a fresh partial run whose
manifest replayed_from names the source run.
replay --run-id names the destination run. It never resolves the source. Pass
--suite <suite-or-path> when replaying a copied run or when the current suite
cannot be resolved by manifest suite name.
Command Scorers
scorer add <suite> --name command --id judge --argv "python3 scorer.py" adds an
external scorer. The scorer receives EVALCTL_CASE_FILE,
EVALCTL_OUTPUT_FILE, and EVALCTL_WORKSPACE, and emits one JSON verdict.
Command-scorer verdicts are captured once under
cases/<case_id>/scorers/<id>.json. Reports and artifact replay read that
artifact and do not re-execute the scorer binary. Command scorers run arbitrary
local code and are covered by the same unsandboxed-runner warning as runners.
License
Apache License 2.0. See LICENSE.
Home
- Site: evalctl.dev
- Repository: github.com/evalctl/evalctl
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file evalctl-0.4.2.tar.gz.
File metadata
- Download URL: evalctl-0.4.2.tar.gz
- Upload date:
- Size: 88.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62dcf72f450a79d4f5de59465198f2b45257cb22fe596b10baf1de77e71563ff
|
|
| MD5 |
94b89a1901863046a1edf825699f325a
|
|
| BLAKE2b-256 |
bb45e8591cb09ba88ef6588409a11298ce5743c795e0cf1b4f9820f3b2547251
|
Provenance
The following attestation bundles were made for evalctl-0.4.2.tar.gz:
Publisher:
publish.yml on evalctl/evalctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evalctl-0.4.2.tar.gz -
Subject digest:
62dcf72f450a79d4f5de59465198f2b45257cb22fe596b10baf1de77e71563ff - Sigstore transparency entry: 2279495018
- Sigstore integration time:
-
Permalink:
evalctl/evalctl@420ff72f563ca859f5d47e0bc77d065bae52c258 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/evalctl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@420ff72f563ca859f5d47e0bc77d065bae52c258 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file evalctl-0.4.2-py3-none-any.whl.
File metadata
- Download URL: evalctl-0.4.2-py3-none-any.whl
- Upload date:
- Size: 64.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
defe149643aefadde1dcf1b6325dfe164c3821faa48d46fb619ca198b0351866
|
|
| MD5 |
8994fa494d4176d48ac5c0afa29e427e
|
|
| BLAKE2b-256 |
366899f57b59cd1c37093aaa92052315b26deea58cac28b77c31efa02f70d24a
|
Provenance
The following attestation bundles were made for evalctl-0.4.2-py3-none-any.whl:
Publisher:
publish.yml on evalctl/evalctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evalctl-0.4.2-py3-none-any.whl -
Subject digest:
defe149643aefadde1dcf1b6325dfe164c3821faa48d46fb619ca198b0351866 - Sigstore transparency entry: 2279495026
- Sigstore integration time:
-
Permalink:
evalctl/evalctl@420ff72f563ca859f5d47e0bc77d065bae52c258 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/evalctl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@420ff72f563ca859f5d47e0bc77d065bae52c258 -
Trigger Event:
workflow_dispatch
-
Statement type: