Tests
Bash checks that score every run.
tests defines how Fiveonefour scores each run. It is one flat list of bash checks. Test scripts receive resolved environment_variables, run identity, and path env vars for staged run artifacts, so any test can inspect final /workspace state, run commands, query its run, or branch on $AX_RUN_CONTEXT_PATH. See Tests for the complete transcript-query example.
tests:
- name: report-exists
description: Confirms the agent produced the required report artifact.
script: test -f /workspace/report.jsonTest runtime environment
Platform tests run in a separate sandbox forked from the agent's saved /workspace. The agent does not see the test scripts or these test-only variables.
| Env var | Available to | Value |
|---|---|---|
AX_RUN_ID | Platform tests | The current run id. Use ax-run-query "$AX_RUN_ID" for read-only access to this run's derived data. |
AX_RUN_CONTEXT_PATH | Setup checks and tests | /tmp/.state/run-context.json, a JSON object with run_id, run_request_id, repeat_idx, variant_id, variant_tag, agent, model, canonical_model_id, effort, context_window_size, thinking, fast, prompt_id, prompt, environment_id, product_id, product_version, tags, and resolved_extend_id. Absent axes/controls are JSON null. |
AX_AGENT_LOG_PATH | Tests only | /tmp/.state/agent.log, staged when the agent log exists. |
AX_VARIANT_ID | Setup checks and tests | The derived variant id. Kept for backward compatibility; the same value is also in run context JSON. |
For "what the agent said" checks, query events WHERE source IN ('stdout', 'transcript') with the test-only ax-run-query "$AX_RUN_ID" command. Use that same filter for every agent: those sources are different capture paths for conversation, and which one is populated depends on the agent. Do not read $AX_TRACE_PATH or $AXP_TRACE_PATH; runs do not stage a trace file. Let missing rows and query errors fail the script. Do not use exit 0 when evidence is unavailable.
For tool-call evidence, query the same events table:
ax-run-query "$AX_RUN_ID" sql "
SELECT payload
FROM events
WHERE kind = 'tool_call'
AND positionCaseInsensitive(payload, 'SKILL.md') > 0
"Use sd_current_tool_calls only when you need its typed status and tool-name columns.
Example:
jq -e '.run_id != null and .agent == "claude"' "$AX_RUN_CONTEXT_PATH"Test object
| Field | Required | Type / values | Notes |
|---|---|---|---|
name | Yes | Kebab-case string | Must be unique. |
description | No | Non-empty string | Explains the test's intent to analysis models. Updating or removing it with ax experiment push keeps the same semantic version and existing runs; ax experiment pull and the mutable description column in ax experiment query return the latest pushed value. Test outcome fields and version-change reports remain unchanged. |
script | Yes | String | Bash script. Streamed over stdin and not shown to the agent. |
files | No | File entry list | Host files staged into this test's sandbox only, immediately before this test's script runs. Never visible to the agent during its turn. See Test-scoped files. |
Test-scoped files
A test's files stage host files into that test's sandbox only, immediately before its script runs, never while the agent's own turn is still in progress. Use it for anything a test must grade against but the agent must not see, such as a reference implementation or an expected-output fixture. Staging that same fixture under top-level files instead puts it in the agent's own workspace, where the agent can read or copy it.
tests:
- name: matches-reference-output
description: Grades the agent's report against an expected fixture it never saw.
script: |
set -euo pipefail
python3 /workspace/fixtures/grade.py /workspace/report.json /workspace/fixtures/expected.json
files:
- name: grader
source: ./fixtures/grade.py
dest: fixtures/grade.py
- name: expected-output
source: ./fixtures/expected.json
dest: fixtures/expected.jsonIsolation from the agent depends on how the run executes:
- Hosted runs (
ax experiment run) keep test-only files out of the agent's turn. They land on disk only immediately before that test's script runs, whether tests run in the same sandbox after the agent or later in a sandbox forked from the run's saved end-of-agent snapshot (for a retest). - Local runs (
ax experiment run --local) execute tests in the same sandbox the agent used, after the agent phase finishes. Test files wait outside/workspaceuntil immediately before each test's script, then extract into/workspace. Treat that as a convenience, not a guarantee. - Local retest (
ax run rerun --tests --local) runs in a fresh sandbox with no agent phase at all, so test files stage per test exactly as above with nothing to isolate from.
Per-test rules, in every run mode:
- Files stage immediately before their owning test's script, not once for the whole run.
- Two tests may declare the same
destwith differentsource; each test sees only its own. - The same
sourcestaged to the samedestacross tests uploads once and is shared; the samesourcestaged to a differentdestis a separate upload. - Test files never appear in the fs-diff artifact, even when a test's own script writes into them.
Staging failures are scoped the same way as top-level files: an unresolvable source aborts the whole run at preflight, and a failure delivering the file into the sandbox rolls the whole variant up as status=error / exit_reason=staging_failed, before the agent runs. Only a failure extracting an already-delivered test file into its owning test's sandbox, immediately before that test's script, fails just that one test; the rest of the run keeps going.
ax experiment validate rejects a test files entry whose dest collides with a top-level or setup files dest, since a local run eventually extracts every scope into the same /workspace. The same dest repeated across different tests is fine. The MCP experiment_run tool cannot run an experiment that declares any files staging, including tests[*].files; use the CLI instead.