JSON output¶
Every attest command prints one JSON document on stdout, and nothing else. All but
attest schema wrap their result the same way:
When a command can't run, ok is false and error replaces data:
{"schema_version": "2", "command": "role", "ok": false,
"error": {"code": "missing_api_key", "message": "Set the anthropic API key ..."}}
error.code is invalid_input, missing_api_key, provider_config or command_failed.
provider_config means the provider rejected the key (401), the key lacks permission (403) or
the model doesn't exist (404); the message never includes the key. command_failed means
an error the checks don't absorb stopped the command, such as a provider error during
attest review; message names the error.
A usage error, such as an unknown option, a missing argument or --max-claims abc, is
invalid_input too. When the command itself is missing or unknown, command is attest.
Only --help prints plain text.
Exit codes¶
| Code | Meaning |
|---|---|
0 |
The command ran. |
1 |
A required API key is missing or rejected (missing_api_key, provider_config), or the command failed (command_failed). |
2 |
The input or an option is invalid. |
3 |
A budget stopped attest run. The runs graded so far are still printed. |
The exit code says whether the command ran, not whether the agent passed.
What data holds¶
| Command | data |
|---|---|
run |
trajectories: one entry per graded run, each a task and its report. Then total (runs in the file), evaluated (runs graded), verdicts (how many runs got each verdict: pass, review, block), passed (runs whose verdict is pass), checks_passed (runs whose checks all passed, including runs that still need review), mean_overall_score, usage (calls and tokens), and budget_exceeded: why a budget stopped the run, or null. |
tools, injection, role |
trajectories: one entry per run, each a task and its result |
review |
A ReviewResult |
demo |
comparisons: one per run, with task, answer, the plain LLM judge's verdict (judge), attest's faithfulness result (attest), and outcome: caught (attest caught a false claim the judge passed), both_caught, both_passed or judge_only |
stats |
rate, low, high, n |
models |
provider, source (live or curated), models |
A report holds results (one per check), overall_score, verdict (pass,
review or block), passed (verdict is pass), checks_passed (every check passed,
ignoring dispositions), blocking (the findings that blocked, each a check, verdict and
subject), scores (each check's score by name) and claim_coverage (the share of claims
faithfulness checked, or null). Each result and finding has the fields
described in How it works; a finding in a report also has
its disposition. See Reports. Gate on verdict or passed; an
inconclusive run can have checks_passed: true and still verdict: "review".
From Python, report.model_dump_json() gives the same JSON as one report in attest run's
output, and a check's result.model_dump_json() the same as one result from attest tools,
injection or role.
Examples¶
# The failed claims, with the evidence against them
attest run run.json | jq '.data.trajectories[].report.results[].findings[]
| select(.severity == "fail") | {subject, reason, evidence}'
# Fail a CI step unless every run's verdict is pass (review and block both fail it)
attest run runs.jsonl | jq -e '.ok and .data.verdicts.pass == .data.total'
# Fail only on blocks, and let runs that need review through
attest run runs.jsonl | jq -e '.ok and .data.verdicts.block == 0'
Schemas¶
attest schema <command> prints the JSON Schema for a command's output. The same schemas are
published as schemas/v2/<command>.json on this site, for example
run.json and error.json.
Within a schema_version, fields may be added but never removed or redefined. Version "2"
changed what passed means (see the changelog); the
v1 schemas are still published.