Whippletree

Docs / Concepts

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.

The contract

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.

Requirement kinds

Five kinds, a closed set. The kind determines which other fields apply.

kindwhat it asks for
blocking-gateRun a handler at a point where it can stop what happens next. Exit 2 blocks.
lifecycle-signalRun a handler when something happens. It asks for no power to stop what follows.
observation-signalRun a handler when a tool of a given class is used, such as a file read.
executable-pathNo handler. Asks only that a binary in the bundle is reachable at runtime.
skillShip a SKILL.md into the target's skill channel, where the model can read it.

Events

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:

aliasexpands to
file-readtool-post filtered to the harness's read tool
file-writetool-post filtered to its write tool
shell-exectool-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.

The tier ladder

A requirement lands at the best tier the harness can carry it at.

tiermeaning
T1Native. The target has a mechanism that meets the requirement outright, with nothing standing in for it.
T2Degraded. A coarser mechanism approximates it, and the report states the lossage.
T3Compiled to instructions. The model is instructed to run the step and usually will, but can skip it under pressure.
T4Observer. 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.

Verdicts

verdictmeaningexit
SATISFYReached your declared minimum, or better.0
DEGRADEBelow the minimum, but the requirement is soft. Installs, and says so.0
REFUSEBelow the minimum and hardRequired. Whippletree installs nothing.1
ABSENTThe harness has no mechanism at all, and the requirement is soft.0
A silent downgrade is the dangerous case. A security gate that becomes advice is worse than one that fails to install, because you will believe it is running. opencode has no blocking stop event, so a hard turn-end gate refuses there rather than pretending.

Instruction fallback

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:

best-effort, no harness-level enforcement on this target: the model is instructed to run the step and usually will, but can skip it under pressure

Probed versions

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.

Next