hunch lint checks every key on this page; unknown keys are warnings, not errors.
For completion and checks while you type, put this line at the top of a spec (the recipes start with it):
redhat.vscode-yaml). The schema is also in the installed package, for working offline: python -c "import hunch, pathlib; print(pathlib.Path(hunch.__file__).parent / 'spec.schema.json')". It covers keys and allowed values; lint also checks what it cannot, such as whether a column reaches the spec.
A complete example
Every root-judgment key exceptwhere, chain, union, question, reviews and view. Passes hunch lint against a CSV with columns id, request, cwd, description, command, sample, gold_purpose, gold_destroys, gold_side_effects:
command_guard.yml
Top-level keys
judgment
Unique within a project; also the table name in the store. answers, traffic, and names starting with _ or sqlite_ are rejected.
model
The engine, pinned to an exact version. Names ending in latest or containing :~ are refused: different versions would share cache keys.
--model <engine> overrides it for one run. See Engines.
source
Where the rows come from. Relative paths resolve from the spec’s folder.
--source other.csv replaces a root spec’s source for one run; --traffic replaces it with rows logged by judge(...).
view
How traces(...) turns sessions into rows. Ignored for other sources.
next_message: the developer’s next message.- Injected text (IDE context,
!shell output, reminders, messages from other agent sessions) is removed from user messages. A message that is only injected text, a slash command, a context-continuation summary or an interruption is not a turn. - A session’s last turn has no next message, so it is not a row.
ran_after_edit:yesif the agent ran a command after its last edit,noif not, empty if it made no edits.final_messages(runs): the agent’s last three messages.commands:requestis the last message the person typed before the command;cwdthe folder it ran in;descriptionthe agent’s own note on it;rejectedispersonif the person refused the call,classifierif Claude Code’s auto-mode safety classifier did,nootherwise;failedisyesif it returned an error. After a context compaction, a background notification or a slash command (none of which count as requests),requestis the last message the person typed before it. Ids are<session>#<turn>.<n>, the n-th command in that turn.
key
The column that identifies a row; diff, review and on_change pair rows by it. Must be unique (run warns on repeats). A ref(...) spec inherits its upstream’s key.
state
The columns the model sees, sent as a JSON object. Nothing else is sent. The cache key is computed from exactly this, after redact and clip; \r\n and \n give the same key.
run and compile warn above 80% and name the largest column; use clip.
redact
Rules applied to every state value before it is hashed, sent or logged. Named rule sets:
Anything else is a regular expression; matches become
[REDACTED].
clip
N keeps a state column’s first N characters, -N its last N. The column must be in state; N is a nonzero integer.
where
Keep only rows where a condition holds, before asking. Can use source columns and, on a ref(...) spec, the upstream’s answer columns. Allowed: column names, constants, ==, !=, <, <=, >, >=, in, not in, and, or, not. Compared with a number, text is read as a number; an empty cell matches no numeric condition. lint rejects a column that does not reach the spec.
chain
Needs where. Confidence (<question>_p, and what act compares) becomes the judgment’s own confidence times the probability that where holds, computed from the upstream answers’ distributions across every hop.
union and question
Combines one question’s answers from several judgments into one table, so a tree of judgments is tested as one classifier. Every branch asks question with the same type; a row may reach only one branch (overlapping wheres stop the run). Rows gain _branch. A union has no model, source or questions.
reviews
The CSV where hunch review saves verdicts, which become gold for test and diff. Default: <judgment>.reviews.csv next to the spec. Specs asking the same question of the same rows can share one file.
weights
Corrects for a sample whose mix differs from production. Each row gets _w = population share ÷ sample share of its by value; test weights accuracy, calibration and estimates by it. Every by value needs a share; shares sum to 1. Set it on the spec that reads the file; downstream specs carry _w.
tests
Per-question thresholds for hunch test. A failure exits 1, unless the entry has severity: warn.
multi question’s own entry takes min_accuracy only; per-option tests go under <question>__<option>. A metric takes its own tests under its name.
Limits are shares between 0 and 1 (0.9, not 90). severity: warn in an entry makes its failures print WARN and leaves test’s exit code alone: for a check you want to see on every run but not block on. The default is error.
metrics
A metric is a rule over one row: a condition on the judgment’s answers and its columns, in the same language as where. In a rule, a question’s name stands for that row’s answer ('yes'/'no', an option, a level), with <question>_p and <question>_pyes beside it; source columns work too. hunch test counts the rule twice, and compares the two.
This example is from the command guard, whose three yes/no questions are named destroys, reaches_outside and sends_out:
Why only random spot checks, and how gold stands in for an answer: How the numbers work. A rule that reads only columns, no answers, gets the first line only.
test writes the same numbers to the results file under metrics.
examples
Statistical tests say how often a judgment is right; they cannot pin a case that must never go wrong. An example is a row you write in the spec, with the answers it must get. hunch test asks it like any other row and turns each example into one check.
severity: warn, so it is reported without failing test; without it, the line reads FAIL and test exits 1.
An example is asked once and then comes from the store like any answer, so it costs nothing in later runs. It tests the spec’s own model:
escalate is not applied, and where does not filter it. Results are in the results file under examples.
on_change
What happens to past answers when the spec changes:
description
A sentence saying what the judgment decides, shown by hunch docs. Never sent to the model, and not part of any answer’s key or the spec hash, so editing it re-asks nothing and doesn’t make a test stale.
exposures
What uses the judgment’s answers. hunch never calls them: hunch docs shows them, and hunch diff names the ones a change reaches. Like description, they are outside every key and the spec hash.
Answers an exposure relies on
uses as a mapping names, per question, the answers the exposure’s code compares against:
Lint, which every command runs first, fails while a listed value is not one the question can answer.
hunch diff counts, per exposure, the rows whose answer moved into or out of a listed value. Change a spec safely shows both.
Question keys
Keys not sent are not in the cache key; changing them asks nothing.
Question types
- choice
- noul
- score
- multi
One option.
criteria maps each label to a description. Describe all options or none (lint warns on a mix). At most 255 options.act
Confidence at or above which an answer is acted on; below it, the row goes to the review queue. A number in (0, 1], or for noul one threshold per answer:
chain: true, the confidence compared is the chained one.
gold
The source column with the right answer. Empty cell: no gold for that row.
Reviews override this column for the rows they cover and can accept two labels.
escalate
Re-asks answers below act on another engine. The new answer is used only if it clears act itself; both are stored and <question>_by names the engine used. Needs act and a different model.
none
Adds an option none_of_these with this description, so declining is an answer rather than low confidence. test reports how many rows declined.
Columns a judgment adds
A judgment’s output rows (whatref(...) and where read) have their input columns plus:
lint rejects a question named like an input column (often its gold column). The store also adds <question>_key and _hunch_run_id; see Store.
YAML details
- Only
trueandfalseare booleans.yes,no,onandoffstay text, so options andact: {yes: …, no: …}keep their names. - Relative paths (
source,reviews, files intraces(...)andpy(...)) resolve from the spec’s folder.