Eval Datasets
Everything a live evaluation runs on lives in one directory: evals/ inside
your skill. This page is the contract for that directory — what goes in it,
the dataset format tier3 evaluate reads, and how to generate and refine
cases.
You can generate a starter dataset with no API key; LLM-backed generation and
refinement need a configured provider (see
Providers & Credentials).
Tier 3 executes these cases through
Harbor, the open-source agent
evaluation framework. Native Harbor task bundles are the bring-your-own-task
alternative to the evals.json dataset described below.
The evals/ directory
SkillEvaluator recognizes this tree inside your skill:
Only the dataset is required. Everything else is optional and additive:
Unrecognized files in evals/ are ignored, and the validator warns about them
so typos don’t silently drop a fixture.
Dataset format
New datasets use the agentskills.io shape: a
top-level object with skill_name and an evals array.
The dataset file can be named evals.json, evals.jsonl, evals.yaml, or
evals.yml — tier3 evaluate accepts all four.
Legacy flat format
Older datasets are a flat array of entries with question instead of
prompt. They still run, but the validator emits a deprecation warning —
migrate to the agentskills.io shape for new work.
Generate a dataset
create-eval-dataset writes evals/evals.json for you, grounded in your
SKILL.md name, description, body, and script filenames:
Without an LLM provider configured, generation automatically falls back to the
same templates --no-llm uses, so the command always succeeds. Exact defaults
for every flag are in the
CLI Reference.
You don’t have to generate a dataset up front at all: validate PATH,
tier3 PATH, or tier3 evaluate PATH --autopilot creates exactly
one eval case when no evaluation source exists — LLM-generated with your
configured provider, falling back to a deterministic template case when no
key is available. Autopilot never overwrites an existing source; if
evals/evals.json (or evals/harbor/) is already there, it runs with what
you wrote. Dataset generation does not replace the provider and runtime
requirements for the live evaluation. Use validate PATH --no-autopilot to
require an existing source instead of generating one.
--force overwrites evals/evals.json without a backup. --refine implies
overwrite too — it produces the final version of the dataset.
The four case buckets
--full with an LLM provider targets one case per bucket, including a negative
case that should not trigger the skill. In --no-llm template mode, the negative
bucket is omitted unless eval guidance includes an off-skill prompt under a
supported negative heading (see below).
Guide generation with EVAL.md
Place an EVAL.md in evals/ and the generator picks it up automatically.
Four headings are parsed as structured hints; everything else is general
context:
Author guidance is the highest-priority input: behaviors you list here are preserved in the generated cases even when a trial agent skipped them — that is exactly the regression the eval should catch.
Refine from real runs
--refine grounds the dataset in real agent behavior. It looks for existing
trajectories from a previous evaluation (checking --from-results or the
latest resolved results first); if none exist, it runs the claude-code agent
via Harbor to collect one, then rewrites expected_output and assertions from
what the agent actually did:
Collecting a fresh trajectory needs Docker, the tier3 extra, and a configured
provider. Refining from existing results does not need the tier3 extra: case
IDs are read from each trial’s reward.json or Harbor result.json, and
multimodal trajectory messages are read as text, with image and audio parts
shown as [image] and [audio] markers. With --no-llm, refinement still
works — it applies template-based updates from the trajectory instead of an LLM
rewrite.
Run policy: evals/config.yml
The dataset says what to evaluate; evals/config.yml (or config.yaml)
says how to run it. CLI flags always override config values.
Environment constructor kwargs are deliberately not accepted in this
skill-owned file. Operators can pass non-secret kwargs for Harbor-native
non-Docker backends with repeatable --environment-kwarg KEY=VALUE / --ek KEY=VALUE CLI flags. Backend credentials stay in the host environment;
runtime-policy, mount, network, lifecycle, and pod-security fields are
reserved.
Unknown keys are rejected, not ignored — a typo like n_attemps fails the
config load instead of silently running with defaults. Use only one of each
alias pair. Agent and provider credentials never go through runtime_env —
they come only from your host environment; see the two-credential-roles
section in Providers & Credentials.
Fixtures and custom environments
Two optional directories shape the world the agent works in.
evals/files/ holds input fixtures — test data, configs, sample
documents. An eval entry’s files string or list selects only those paths
(relative to evals/) for its /workspace/input/. An explicit empty list or
null stages no fixture. For backward compatibility, an entry that omits
files receives the entire shared evals/files/ directory.
evals/environment/ customizes the container itself:
In Docker mode, Claude Code and Codex setup no longer installs the
distribution nodejs and npm packages, except on Alpine images. On
Debian-based images, including the default python:3.12-slim image, those
packages also brought npx and a C/C++ toolchain (gcc, g++, make).
Claude Code containers now have no Node.js unless the image provides it.
Codex installs its own Node.js, which is on PATH only for the Codex process
and the commands it runs. If your skill’s scripts, its tasks, or a stdio MCP
server (for example command = "npx ...") need these tools, install them in
the image: list the packages in scripts/apt-packages.txt, one per line, for
the auto-generated image, or install them in evals/environment/Dockerfile.
Validate before you run
Check the whole contract — dataset fields, case IDs, config.yml, Dockerfile
rules, MCP declarations — without launching anything:
Run it in CI or before a long evaluation: it catches an empty dataset, missing required fields, duplicate case IDs, and invalid config in seconds.
validate PATH also checks the selected Tier 3 source (dataset entries and
case IDs, config.yml, or the native Harbor task layout) before any live run.
When that check fails, each error names the entry or file and what is wrong,
for example entry case-3: Missing required fields: question:
- The terminal lists the errors under the Tier 3 finding when the full CLI
report prints (
-r clior--verbose), otherwise in the compact Tier 3 panel. When the run also writes an HTML or JSON report, the terminal shows the first five and names that report for the rest. When it writes neither (for example-r clialone, or--verbosewithout-r), the terminal lists every error. - The HTML report shows the exact error on the Tier 3 dashboard when there is one, or the error count when there are several. Full Insights → Source Validation Errors lists every error in order.
- The JSON report keeps every error under
tier3.provenance.source_validation.
Passing checks and warnings are not listed as source errors.