skill command; source availability does not establish npm or
PyPI release availability.
EVALGATE_SKILL_VARIANT to with or without, plus
EVALGATE_SKILL_DIR to the absolute directory. The command string is split into
an executable and arguments and executed without a shell. Pipes, substitutions,
redirection, and shell expansion do not execute. Put complex setup in a runner
script.
Define cases and task checks
Each JSONL line has a unique case id, prompt, and expected trigger label. Include at least ten distinct paired cases; repeating one prompt ten times does not establish ten independent cases.exit_code compares the reported task exit code with expected. includes_all
requires a successful task exit and every expected substring. json_equals
requires a successful task exit and structurally equal JSON, ignoring object key
order. JSON numbers must be finite; integer values must be between
-9007199254740991 and 9007199254740991 so TypeScript and Python compare the
same values. Unsafe integers in expected JSON are refused; unsafe integers in
runner output fail the task check. Encode high-precision identifiers as strings.
Task checks are optional for trigger-only testing; uplift needs task checks.
Runner contract
Write exactly one JSON line to stdout:variant must echo the requested variant. invokedSkills lists skills actually
loaded by the agent. The tested skill’s name comes from its SKILL.md frontmatter
name, falling back to the directory name. Mentioning the skill in an answer
does not count as an invocation. The runner process itself must exit zero to
deliver usable evidence; exitCode records the task’s outcome.
The runner controls isolation: it must remove the skill from the agent’s view in
the without variant, including global/project skills, agent configuration,
caches, and prior conversation state. EvalGate rejects a without observation
that reports loading the tested skill, but cannot prove your runner hid every
copy. Use fresh work directories and fresh sessions for both variants.
Execution defaults to a 30-second timeout and a combined 1 MB stdout/stderr cap.
Set --timeout-ms (maximum 600000) and --max-bytes (maximum 10000000) explicitly
when needed. Runner errors, malformed JSON, variant mix-ups, and resource-limit
failures cannot produce a passing result. Error reports omit raw output, stderr,
and prompts. Runner code executes with your user permissions and inherited
environment; run only code you trust.
On POSIX systems, EvalGate terminates the runner’s process group after every
outcome, including success, to remove remaining child processes. Windows
termination covers the direct runner process; descendant cleanup is not
verified there. A Windows runner must stop its own child processes before
exiting. These limits do not isolate filesystem or network access.
Example: Claude Code headless runner
This example uses fresh temporary project and configuration directories. Check the stream contract for your installed Claude Code version before using it for release evidence. It readsSkill tool-use records from stream JSON; it never
infers skill loading from the final answer.
Use recorded observations
For harnesses that cannot be scripted, record the same runner fields pluscaseId and a one-based trial:
(caseId, trial, variant). Duplicate,
missing, extra, and unknown-case observations are refused rather than scored on
a selected subset. Choose exactly one of --runner and --observations.
Case and observation inputs must be regular files (no symlinks, devices, or
named pipes), with at most 50 MB per file and 4 MiB per JSONL record. Inputs are
read incrementally with LF or CRLF delimiters; Unicode line and paragraph
separators inside JSON strings remain data. Blank lines count toward the byte
limit without creating retained records. Parsing stops when the case/trial
population bound is exceeded or a record fails validation.
Interpret and gate results
The report contains trigger precision, recall, F1, false/missed-trigger case ids, task pass rates for both variants, a paired difference, its seeded 95% percentile bootstrap interval, and a two-sided exact sign-test p-value. Trials are paired by case and trial; task results are averaged within each case before bootstrapping case differences, so repeated trials do not inflate the independent sample size. The sign test uses nonzero per-case differences and excludes ties. Undefined trigger metrics arenull. No task checks means task: null, not
zero uplift. The report includes both the SKILL.md SHA-256 and a SHA-256 of all
relative filenames and bytes in the skill directory, including scripts and
references. Symlinks are refused. A directory changing during execution invalidates
the evidence. Keep observation files and generated reports outside that directory.
--min-uplift 0.05 requires the interval’s lower bound to be at least 0.05.
An ungated report describes the observations; it does not prove the skill will
improve every deployment or that a nondeterministic agent always behaves alike.