Skip to main content
A resolver is an external program that does the work of grading an anchor or running a verifier. Hibi’s core is tiny (“if it isn’t core, it’s a resolver or a consumer”), so even the built-in drift and supersession logic ships as resolvers behind the same contract. To teach Hibi about a new language, a new anchor kind, or a new way to verify behavior, you write a resolver in any language and point Hibi at it.

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.
A round-trip for one claim looks like this: The engine asks what a resolver can do, then hands it only the work it claimed and the file contents it needs.

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
An opt-in semantic resolver (an LLM or formal advisor) may explain a change or triage a suspect set, but it never gates and never marks a claim supported. Verdicts on the gating path stay deterministic; advisory output is layered context on top, never the decision. This is the determinism boundary: no model runs on the verdict path.

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 Advisory object gains an optional provenance object — { 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) gains modelBacked, a boolean defaulting to false. Set it on any resolver whose advisories are produced by a model.
When a modelBacked resolver returns an advisory that lacks provenance, the registry drops it and prints one stderr warning per run per resolver:
A non-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’s kind 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>.
Verifiers are a supply-chain surface. A verifier executes a command committed to the repository, so whoever can commit to the repo decides what runs on the machine of whoever checks. Hibi therefore never runs a verifier implicitly: verifiers execute only under check --run-verifiers. status, query, list, doctor, and plain check never spawn a verifier process. External runner resolvers still require the default-deny manifest (.claims/resolvers.json) on top of the flag — the flag opts in to running verifiers, the manifest opts in to the program that runs them.

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.
The JSON Schemas for every protocol message (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.
You rarely need to handle JSONL-RPC framing by hand. The TypeScript and Rust SDKs implement describe / resolve / verify for you, leaving you to write only the grading logic.

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.