What each agent CLI's hooks actually send — recorded from live sessions, once a
day, and published here as pack.json.
This repo holds data, not code. It is written by the contracts lab, a job that runs in failproofai's integration suite.
failproofai enforces policies by reading the payloads agent CLIs hand to their hooks — a shell command here, a file path there. Every one of those reads depends on a translation written against one version of one CLI, verified once, live, and then left alone.
Vendors move. When GitHub Copilot 1.0.71 renamed a file tool's file_path to
path, the hooks kept firing, the CLI kept reporting success, and the policy
that blocks reading .env files quietly stopped matching anything. Nothing
failed. That is the shape of the problem: enforcement does not break loudly, it
goes inert.
A machine cannot notice this about itself. It only ever sees the payloads its own agents happen to produce, and if a vendor rejects a hook config outright it sees nothing at all — which looks exactly like a quiet day. So the observation has to happen somewhere that drives every CLI deliberately. That is the lab, and this is what it found.
Key names only — never values. The lab records the shape of a payload, not its contents, so nothing here can carry a path, a command, or anything else from the session that produced it.
verdict says whether that entry can be trusted:
OK |
a tool ran and the vendor called our hook — the entry describes a real session |
DRIFT |
the tool ran and no hook arrived; the vendor no longer accepts our config |
INCONCLUSIVE |
hooks fired but the model never used a tool; nothing to record |
ERROR |
the CLI could not be driven at all |
There is deliberately no schema version. Consumers ignore fields they do not recognise, which is what lets this file grow without a coordinated release on the other side.
| Branch | What it is | Who follows it |
|---|---|---|
packs |
The lab pushes here unattended, the moment it measures a change. Cuts a prerelease. | failproofai's own machines |
main |
Reached only by a reviewed pull request from packs. Cuts the release. |
Everyone else |
releases/latest skips prereleases, so the split needs no extra machinery: a
pack from a lab run that went wrong cannot become the one client machines
resolve to. It has to be promoted first, and promotion takes two independent
agreements —
- a machine that runs these CLIs for real compares the pack against its own accumulated observations and agrees with it, and
- a human reads the diff and approves the pull request.
Step 2 is not politeness. The diff is the vendor's own key names, so approving it is a person agreeing that somebody else shipped a change.
pack.json is compared before it is committed, with generatedAt removed — so
a run that finds everything unchanged pushes nothing. A commit here means a
vendor's contract actually moved.
If releases ever start appearing daily, that property has been lost and the notification has stopped being worth reading.
curl -sL https://github.com/FailproofAI/hook-contracts/releases/latest/download/pack.jsonfailproofai fetches this on a schedule and reports anything in it that its own
translation maps can no longer read. See failproofai doctor.
{ "generatedAt": "2026-08-18T06:00:00.000Z", "clis": { "goose": { "version": "1.43.0", "hooks": { "PreToolUse": { "envelope": ["event", "session_id", "tool_input", "tool_name", "working_dir"], "tools": { "write": ["content", "path"] } // the vendor's own names } }, "probe": { "verdict": "OK", "note": "tool ran and we were called" } } } }