Skip to content

harn adapter

harn adapter list [--json]
harn adapter show <id> [--json]
harn adapter bench [adapters...] [--require-installed] [--json]
harn adapter attest [adapters...] --yes [--subscription-only] [--timeout <ms>] [--projection] [--json]
harn adapter attestations [--json]

harn adapter is the executable source of truth for Harnery’s workflow adapters. The built-in registry intentionally contains three adapters: claude-code, codex, and cursor.

The registry describes the Harnery adapter boundary, not every feature in the vendor product. For example, Cursor Agent can stream JSON, but Harnery’s workflow adapter currently returns only a normalized final result, so streaming is declared unsupported for that adapter.

harn adapter list prints the high-signal claims. harn adapter show <id> prints every dimension, its support level, and its qualification.

Claims use four states:

State Meaning
supported The adapter implements the capability.
partial A narrower form works; read the qualification.
unsupported The adapter does not expose the capability.
unknown Harnery does not have enough evidence to claim either way.

The current dimensions cover invocation, model and effort selection, turn ceilings, final results, session ids, cost, tool evidence, policy mapping, interruption, streaming, steering, resume, images, and compaction. Context continuity is split into four claims: context telemetry, a pre-compaction signal, a post-compaction signal, and the ability to initiate native compaction.

harn adapter bench runs without credentials and makes no model calls. It uses the same invocation planners and result normalizers as production workflow runs, then reconciles their observed behavior with the registry claims. A disagreement is drift and exits non-zero.

The full verdict vocabulary is:

Verdict Meaning
supported / unsupported / partial Observation agrees with the declaration.
unknown No offline probe exists for that behavior yet.
skipped The vendor binary is unavailable in this environment.
not_applicable The dimension does not apply to the adapter.
drift Executable behavior disagrees with the declared profile.

A missing binary is a neutral skip by default because the registry can be tested on machines that do not install every vendor CLI. Pass --require-installed when the current machine is expected to host every registered adapter. --json emits the complete report for CI or dashboards.

Every row also carries a basis, which answers “how do we know” rather than “what is true”. Verdict and basis are independent (ADR 0037).

Basis Meaning
adapter Checked against Harnery’s own planner, normalizer, or fixture. Proves the adapter contract and says nothing about the installed vendor CLI.
attested Checked by observing the installed vendor CLI on this host.
declared Not checked. The value is the declaration, repeated.

Most of the bench is an adapter check. A supported session-id row proves that Harnery’s normalizer can read a session id out of the committed fixture. It does not prove the vendor still emits that shape. Without a basis column, a clean report reads as “no disagreement found” when the accurate reading is usually “no vendor behavior was observed”.

The report includes a basisSummary count per basis so a caller can tell a measured run from an unmeasured one without walking every row.

Each adapter profile can record the vendor CLI contract its declaration was last validated against. The contract dimension compares that recorded version with the version the installed binary reports:

  • supported when they match;
  • drift when they differ, because the declaration is no longer backed by an observation;
  • unknown when nothing is recorded, or the recorded value is not a version;
  • skipped when the binary is absent.

A newer vendor CLI is not automatically broken, so a mismatch is drift rather than failure. Harnery never infers that a dimension is probably still supported on an unverified version: an undeterminable fact stays unknown.

contract is the only dimension attested without a model turn, because a version string costs nothing to read.

harn adapter attest --yes records what the installed vendor CLIs actually do (ADR 0038). It runs one bounded turn per adapter through the same spawn the workflow engine uses, so what gets attested is the path production takes.

Terminal window
harn adapter attest --yes # every registered adapter
harn adapter attest --yes claude-code # one adapter
harn adapter attest --yes --subscription-only # match a subscription-only run
harn adapter attest --yes --timeout 180000 # per-adapter probe timeout
harn adapter attest --yes --projection # also prove the sandbox is enforced
harn adapter attestations # show records, no model calls

--yes is required because this spends real vendor tokens. Nothing else in the adapter command group makes a model call, and bench never does.

One turn can honestly establish four dimensions: invocation, finalResult, sessionId, and cost. Dimensions that would need a staged scenario are left out of the record entirely. If the probe turn does not complete, nothing is recorded at all, because an unreachable subject evidences nothing.

--projection adds a fifth dimension, filesystemPolicyProjection, and needs a staged scenario to mean anything (ADR 0041). A sandbox that is declared but never enforced looks exactly like an enforced one at the CLI boundary: both accept the flag and exit zero. So the probe gives a child a file to write and checks the filesystem.

It runs a control first, under a mode that permits the write. Without it, a child that simply ignored the instruction would be indistinguishable from a sandbox doing its job. If the control completes without writing, the probe reports inconclusive, skips the treatment turn, and records nothing.

This costs two extra turns per capable adapter, which is why it is opt-in. An adapter that declares no sandbox projection spends no extra turn at all.

Records live under .harnery/adapters/attestations/. They are structural throughout: version strings, dimension names, timestamps, and digests. The probe prompt is a fixed constant, so no prompt text, completion text, or host path can reach the file.

An observation is recorded against the billing mode it ran under. --subscription-only scrubs API-key variables so the child can only use its stored login, matching workflow run --subscription-only (and the repo default in config.jsonc). A child that may fall back to an API key can succeed where one restricted to its login fails, so a record made in one mode is not cited for the other.

A record stops counting when the installed binary reports a different version, when the capability declaration is edited, when it was made under a different billing mode, or when it fails its own digest. Stale and tampered records are ignored rather than trusted, and the affected bench rows fall back to adapter basis.

bench reads attestations but never writes one and never runs a turn. A live observation that disagrees with the declaration becomes drift, exactly as a fixture disagreement does.

The offline bench proves registration, binary availability, the installed vendor contract, invocation planning, model and effort mapping, turn-ceiling mapping, final result normalization, session-id extraction, cost extraction, and the absence of normalized tool evidence. It also checks pre/post-compaction claims against the hook wiring that harn init installs. Context telemetry stays unknown because no static probe can prove a runtime measurement. Policy, active interruption, streaming, steering, resume, image input, and native compaction control remain unknown until a safe behavioral probe exists.

Workflow agent() calls accept effort alongside model:

await agent("Review this patch", {
adapter: "codex",
model: "gpt-5.6-sol",
effort: "high",
});

Claude Code maps effort to --effort. Codex maps it to -c model_reasoning_effort="…". Cursor’s parameterized model ids can carry effort for some models, but Harnery deliberately does not rewrite model ids; separate Cursor effort requests fail before launch.