Claude Code¶
Summarizes SPEC.md §13.5; SPEC.md is authoritative.
Each of the three parts below is optional, and the command shows a diff before writing anything.
1. Skill: .claude/skills/vouch/SKILL.md¶
Loads only when relevant, so it costs no context otherwise. Its description
triggers on editing .tex in a vouch project, writing experiment code that
produces results, or mentions of results, numbers, tables or claims. It has
four workflows:
- Record results in an experiment — see While writing experiment code: a docstring on the function, a run per configuration, checked properties as claims with their evidence, tables whose cells are keys
- Write a results paragraph: search → cite (its
context:line is the producing function's own docstring, if it has one;[w],[,d],[.0pct],[.2fx]for prose) → compare or define (a definition may read a file or a table) → claim → check; a definition is added before the sentence that cites it - Handle changed values:
vouch changes→ re-read each sentence → fix the text → report suspicious changes → ask before acking; a redefined experiment gets new key names - Convert an existing paper:
suggest→--apply→ resolveno-sourcewith the user
Patterns for agents is the long form, with examples.
It ends with a command cheat-sheet, including vouch document — see CLI reference.
2. Rules block¶
Appended to CLAUDE.md (or AGENTS.md), between <!-- vouch --> markers so it
can be updated in place:
<!-- vouch -->
## Numbers in the paper (vouch)
- Never type an empirical number into LaTeX. Find it (`vouch search`, `.vouch/CATALOG.md`), then paste what `vouch cite KEY` prints.
- If it doesn't exist: record it in the experiment, `@vouch.derive` it, or `vouch.expect()` it and tell the user it is owed.
- Give a new tracked function a real docstring -- vouch shows it back at lookup time (`vouch cite`, `vouch search`) as a sanity check. If one already exists on a function you're writing about, use it instead of guessing a description.
- Never compute with numbers in prose (differences, ratios, "2x"): `vouch compare A B --write`, then cite the derived key.
- Qualitative comparisons ("outperforms", "all seeds") go in `\vouchclaim` backed by a claim.
- Before finishing: `vouch build` and `vouch check` must pass. Before submitting: `vouch check --strict`, which also fails on pending `vouch.expect()` values -- allowed while results are owed, never replaced by invented numbers.
- If `vouch changes` lists anything: re-read each cited sentence, fix wrong text, and report SUSPICIOUS changes to the user.
- Never run `vouch ack` or `vouch accept` without the user's approval. Never edit `.vouch/` or generated files.
<!-- /vouch -->
3. PostToolUse hook¶
In .claude/settings.json:
{"hooks": {"PostToolUse": [{"matcher": "Edit|Write|MultiEdit",
"hooks": [{"type": "command", "command": "vouch hook claude"}]}]}}
The agent runs the hook from its own shell, not from the environment vouch init
ran in, so init writes the command that shell can reach:
| the project | the command |
|---|---|
a uv project whose uv.lock installs vouch-paper |
uv run --no-sync vouch hook claude |
| vouch in a virtual environment inside the project | "$CLAUDE_PROJECT_DIR"/.venv/…/vouch hook claude |
vouch on PATH, outside any virtual environment |
vouch hook claude |
| anything else | this Python's absolute path, in .claude/settings.local.json |
vouch hook claude reads the hook payload from stdin and ignores anything that
isn't a .tex file in a configured paper. For those, it checks just the edited
file for:
- unknown keys, with did-you-mean
- malformed macros (
\vouch{never closed,\vouchclaimwithout its prose) - typed numbers: the exact
\vouch[fmt]{key}to replace each with, or "matches no recorded value"
If it finds problems, it prints them with their fixes to stderr and exits 2,
which feeds them back to the agent in the same turn, so a mistake is corrected
immediately instead of at commit time. Pending keys are not problems: citing one
is the approved move. When the file cites owed values and has no problem, the hook
exits 0 with hookSpecificOutput.additionalContext on stdout, which tells the
agent which values are owed, and by what, without blocking it.
vouch: paper/results.tex has 3 problem(s) -- fix them now:
line 1: 93.2% is typed by hand; it is cifar.resnet.acc.mean: replace it with \vouch[.1pct]{cifar.resnet.acc.mean}
line 1: unknown key cifar.resnt.acc: no run, definition or vouch.expect provides it (did you mean cifar.resnet.acc?)
line 2: \vouchclaim takes two arguments: \vouchclaim{key}{the prose it vouches for}
It's stdlib-only and reads a precomputed index (.vouch/cache/index.json,
written by build), so a tex edit costs on the order of 200–300 ms including
interpreter startup. A run you record after the last build counts at once: the
hook also reads the run records written since the index, so their keys are known
and their numbers are matched without another vouch build.
vouch init --agents [skill,rules,hook] [--yes] [--stop-gate]¶
- Prints a unified diff of every file it would write. Without
--yesit writes only after a yes at a terminal, and never when run non-interactively; an end of input at the prompt (a stdin redirected from/dev/null, which Windows reports as a terminal) is a no. - The rules block replaces an existing
<!-- vouch -->block in place. - The hook merges into existing settings and never duplicates itself.
--stop-gatealso installs aStophook,vouch hook stop. It runsvouch check --strictand exits 2 with the issues, so an agent can't finish while the gate fails. It is off by default, because blocking an agent from finishing is intrusive.
See also: CLI reference — vouch init, Patterns for agents, MCP server.