The mental model
The engine owns the loop: it reads your files, decides which claims to check, collects verdicts, computes exit codes, and stamps banners. It does not know how to grade an anchor by itself. That knowledge lives in resolvers, which the engine drives over a small RPC protocol. This keeps two promises. First, a resolver runs out-of-process (a separate program the engine talks to over its standard input and output), so a resolver crash, hang, or dependency never destabilizes the core, and the engine never has to load a resolver’s runtime. Second, the boundary is deterministic by default: the resolvers that produce verdicts are pure graders fed the file contents they need. A flag is a request to re-verify, not a claim that the doc is wrong, and that contract holds no matter which resolver produced it.The built-in code-anchor, doc-anchor, and supersession resolvers ship in-tree,
but they speak the same protocol as anything you write. There is no privileged
internal path; your resolver is a first-class participant.
The protocol
Resolvers communicate over JSONL-RPC over stdio: newline-delimited JSON-RPC messages on the program’s standard input and output. The engine sends a request, the resolver writes back a response, one JSON object per line. No network, no ports, no shared memory. There are three methods.method
Announce what this resolver handles: the anchor
kinds it can grade and the
verifierKinds it can run. The engine calls describe first and routes work
only to resolvers that claim it. Verifier kinds are open strings: the
engine dispatches a verifier to a runner by exact string match between the
verifier’s kind and the verifierKinds declared here.method
Grade an assertion’s anchor into a verdict. The engine reads both sides of the
bidirectional anchor and passes the file contents in as
files { doc, code: { path: content } }, so the resolver stays pure: it grades what it is handed
and never touches the filesystem itself. It returns a verdict on the anchor
resolution axis (unchanged / moved / changed / ambiguous / orphaned).method
Run an executable verifier for a behavioral claim and report whether it
passed. A pass contributes
supported; a failure contributes refuted (the
only behavioral state that may gate, and only on an enforced claim). The engine
itself never executes verifiers in-process; it dispatches them to a runner
resolver through this method, and only under check --run-verifiers.Enabling a resolver
Resolvers stay off until you list them. The manifest is default-deny: a resolver that is not in.claims/resolvers.json is never launched, so dropping a
file in a directory can never start running code in your check loop.
.claims/resolvers.json
Provenance for model-backed advisors
An advisor may layer context on top of a verdict, but when a model produced that context the model state has to be on the record — Hibi will not let hidden LLM output ride alongside a deterministic verdict unattributed. Two schema fields make that enforceable:- The wire
Advisoryobject gains an optionalprovenanceobject —{ model, promptHash, contextHash, params? }— the model name, the prompt and context hashes, and any sampling parameters behind the advisory. - A resolver’s manifest spec (
ResolverSpec) gainsmodelBacked, a boolean defaulting tofalse. Set it on any resolver whose advisories are produced by a model.
modelBacked resolver returns an advisory that lacks provenance, the
registry drops it and prints one stderr warning per run per resolver:
modelBacked resolver is unaffected, and an advisory that carries
provenance passes through untouched. Either way the gating path stays
deterministic — this only governs which advisory context is allowed to surface.
Where resolvers fit
Two distinct out-of-process roles ride the same protocol, and it is worth keeping them apart:
The verifier runner is how Hibi proves behavioral claims that structural checks
cannot judge on their own: “retries with backoff”, “sorts ascending”. The engine
detects that reachable evidence changed and routes attention; an author-supplied
verifier, dispatched through
verify, is what can confirm or refute the
belief. The full behavioral model lives on its own page.
Behavioral claims & verifiers
How the change-gate routes attention and how verifiers push a belief to
supported or refuted, without a model on the verdict path.
Verifier kinds and the built-in command runner
A verifier’skind is an open string — any non-empty value. It is not an
enum in the schema; it is a routing key, matched against the verifierKinds a
runner resolver declares in describe. The conventional kinds — a
recommendation, not schema — are command, example, snapshot, contract,
property, metamorphic, and formal.
One runner ships in-tree: the built-in command runner. It handles
kind: "command" and executes the verifier’s ref as a shell command via
sh -c on POSIX or cmd /c on Windows, with the working directory set to the
repository root. The two shells differ, so cross-platform repos should keep
verifier refs shell-neutral (e.g. bun test retry, not a shell builtin or an
&& chain):
The command runner has its own timeout, 120 seconds by default, set with
--verifier-timeout <seconds>.
Writing your own
Because the protocol is plain JSONL-RPC over stdio, a resolver is any program that reads requests on its standard input and writes responses on its standard output. You handle the framing yourself, or you lean on an SDK that does it for you.1
Implement the three methods
Respond to
describe, resolve, and verify. A grader that only handles
anchors can leave verify as a no-op; a verifier runner can return an empty
kinds list from describe and handle only verify.2
Validate against the schemas
Every protocol message has a published JSON Schema, so you can validate
requests and responses in any language with a JSON Schema toolchain, no need
to hand-transcribe the shapes.
3
List it in the manifest
Add the resolver to
.claims/resolvers.json with its command and args.
Until it appears there, the default-deny manifest keeps it off.schemas/*.v1.json) are
generated from the single Zod source of truth, so the schema and the engine can
never disagree, and any language that can read JSON Schema can validate against
them.
Where to go next
SDKs
The TypeScript and Rust SDKs that handle protocol framing so you write only
the resolver logic.
Behavioral claims & verifiers
The change-gate and verifier model behind the
verify method.
