Whippletree

Docs / Harnesses

Harnesses

Three targets today. They do not support the same things, and the differences decide what your contract can actually promise.

harnessbackendinstalltested against
claude-codehooks-jsonits own plugin marketplace2.1.0 and up
codexhooks-jsonits own plugin marketplace0.144.0 and up
opencodets-pluginWhippletree writes the shim1.18.10 and up

What each event maps to

A dash means the harness has no equivalent, so a requirement bound to that event lands ABSENT there, and REFUSES if you marked it hard-required.

eventclaude-codecodexopencode
session-startSessionStartSessionStartsession.created
session-endSessionEndSessionEndnone
turn-endStop, blockingStop, blockingnone
tool-prePreToolUse, blockingPreToolUse, blockingtool.execute.before, blocking
tool-postPostToolUsePostToolUsetool.execute.after

What each tool class maps to

This is what the file-read, file-write and shell-exec aliases resolve to.

classclaude-codecodexopencode
readReadno such toolread
writeWriteapply_patchwrite
shellBashBashbash

claude-code

The most complete target. Every event maps to a native hook, all three tool classes have a dedicated tool, and skills ride inside the plugin. Nothing degrades.

codex

Identical on events, but it has no file-read tool. Rather than drop file-read, the target definition declares a degradation to T2:

file-read  want ≥T4  got T2  SATISFY   matcher Bash|Edit|Write|apply_patch;
                                       misses reads in pipelines/heredocs/scripts;
                                       may double-count

The lossage is written into the target definition, not discovered at runtime, so preflight prints it before you install rather than after something goes wrong. If T2 is not good enough, declare minTier: T1 and it will REFUSE here instead.

opencode

The one that genuinely cannot do something. It has no blocking stop event. A thrown error fails a single tool call and the agent loop carries on, so there is nothing to bind turn-end to.

A hard-required blocking-gate on turn-end REFUSES on opencode. That is the correct answer, not a limitation to work around: a gate that silently becomes advice is worse than one that will not install, because you would believe it was running.

If advice is acceptable, pair the gate with a skill through fallbackSkill and the step compiles into instructions at T3 instead of refusing. It also uses a different backend: opencode has no plugin manifest to extend, so install writes a TypeScript shim into .opencode/plugin/ and copies skills into .opencode/skills rather than shipping them in the bundle.

How these claims are checked

Every row above comes from a target definition in the repository, and each one records the harness versions it was probed against. Those definitions are established by running the harness in a sandbox, not by reading its documentation, and the probe notes are kept with the code. A nightly job reinstalls all three and reruns the end-to-end suite, so a harness changing under a definition shows up as a failure rather than as a wrong answer in your terminal.