Docs / Concepts
A contract says what your tool needs. A target definition says what a harness can give. Whippletree compares the two and reports the tier each requirement reaches on each target.
A bundle is a directory with a plugin.json and some handler scripts. The
contract lives under the dev.whippletree.v1 key and is a list of
requirements: things your tool needs the harness to do.
{
"name": "my-tool",
"extensions": {
"dev.whippletree.v1": {
"contractVersion": "1.0.0",
"requires": [
{
"id": "capture-gate",
"kind": "blocking-gate",
"event": "turn-end",
"minTier": "T1",
"hardRequired": true,
"loopGuardRequired": true,
"handler": "./handlers/capture.sh"
}
]
}
}
}
That requirement reads: before the agent finishes a turn, run
handlers/capture.sh, and if the harness cannot enforce that natively, do not
install at all. Each of those fields is below.
Five kinds, a closed set. The kind determines which other fields apply.
| kind | what it asks for |
|---|---|
| blocking-gate | Run a handler at a point where it can stop what happens next. Exit 2 blocks. |
| lifecycle-signal | Run a handler when something happens. It asks for no power to stop what follows. |
| observation-signal | Run a handler when a tool of a given class is used, such as a file read. |
| executable-path | No handler. Asks only that a binary in the bundle is reachable at runtime. |
| skill | Ship a SKILL.md into the target's skill channel, where the model can read it. |
A requirement binds to one event. Nine events are primitives, each mapping to the harness's own hook, where it has one:
session-start session-end turn-end
tool-pre tool-post subagent-start
subagent-stop compact-pre compact-post
Three more are aliases that expand to a primitive plus a tool class, so you can write "tell me when a file is read" and let the target definition name the tool:
| alias | expands to |
|---|---|
| file-read | tool-post filtered to the harness's read tool |
| file-write | tool-post filtered to its write tool |
| shell-exec | tool-post filtered to its shell tool |
Harnesses disagree about what tools exist. Claude Code has a dedicated Read
tool. Codex has none, so file-read there degrades to a broader matcher that
misses reads in pipelines, heredocs and scripts, and can count one read twice. You
wrote the same requirement either way; Whippletree tells you which one you got.
A requirement lands at the best tier the harness can carry it at.
| tier | meaning |
|---|---|
| T1 | Native. The target has a mechanism that meets the requirement outright, with nothing standing in for it. |
| T2 | Degraded. A coarser mechanism approximates it, and the report states the lossage. |
| T3 | Compiled to instructions. The model is instructed to run the step and usually will, but can skip it under pressure. |
| T4 | Observer. Reserved. Not implemented. |
You declare a minTier: the worst you are willing to accept. Fall below it and
the verdict turns on whether the requirement is hard.
| verdict | meaning | exit |
|---|---|---|
| SATISFY | Reached your declared minimum, or better. | 0 |
| DEGRADE | Below the minimum, but the requirement is soft. Installs, and says so. | 0 |
| REFUSE | Below the minimum and hardRequired. Whippletree installs nothing. | 1 |
| ABSENT | The harness has no mechanism at all, and the requirement is soft. | 0 |
turn-end gate refuses
there rather than pretending.Sometimes you would rather have the weak version of a step than no install. Pair a
blocking-gate at turn-end with a skill requirement through
fallbackSkill, and on a harness with no native gate Whippletree compiles the
step into the skill's instructions instead of refusing.
That lands at T3, and Whippletree prints the same sentence about it in the preflight
report and in the generated SKILL.md:
A target definition records the harness versions it was tested against. Probe something outside that range and preflight says so:
$ whippletree preflight ./my-tool --target codex
whippletree preflight · target codex (probed 0.100.0)
! probed 0.100.0 is below the tested range >=0.144.0
! the verdicts below were not verified against this version
A warning, never a refusal. A harness shipping a new version must not break every install that day, and Whippletree cannot tell whether the change matters.