Metaharness
Browse documentation
On this page

The b10x adapter

Pinned to 0.22.0 of b10x-harness. Adapter class: direct_provider — the loop is this family's own. Under --decisions observe, the evaluation arm, this adapter observes it and decides nothing (invariant 9). Under frame or ask the loop hands each write and each run to metaharness (atlas ADR 0104, design amendment a61; see the caller seam). A run against another b10x-harness version is unverified rather than refused.

metaharness run b10x --decisions observe -p "…" --model-endpoint https://gateway.example --model-wire anthropic-messages
metaharness run b10x --decisions frame --frame step.frame.json -p "…" --model-endpoint https://gateway.example
metaharness capabilities b10x     # the declaration below; no model, no credential
metaharness conformance b10x --contract  # 11 deterministic checks, no model or credential
metaharness doctor b10x           # resolves the binary the way the spawn will, on the child's PATH

What a run does

  1. Constructs the child's environment rather than inheriting one (H3): PATH is $HOME/.local/bin:/usr/local/bin:/usr/bin:/bin, and XDG_CONFIG_HOME points at scratch so an operator profile cannot arrive ambiently. Toolchain and credential variables arrive only when declared. A key named with --api-key-secret is never one of them: the loop reads it from the operator's secret store, and only the two variables its store client finds the session bus by, DBUS_SESSION_BUS_ADDRESS and XDG_RUNTIME_DIR, are forwarded, each when present (design amendment a50). Whether the store opens in that environment is unverified until a run shows it.
  2. Resolves b10x-harness on that PATH at plan time, so the launch record names the file that ran.
  3. Spawns b10x-harness run --execution-path metaharness --json … with the flags below. The flag gives the inner agent attributable context about the outer observer and grants no authority. Stdout is the record.
  4. Reads the loop's events through the seam into metaharness.event/1.

What the vendor adapters spend two thousand lines on does not apply here, and not because this adapter is unfinished:

what a vendor needswhy b10x-harness does not
a scratch home, so ambient config cannot leak inscratch XDG_CONFIG_HOME isolates its optional profile file
a hook channelan observe run's decisions are in-process, and this adapter makes none; a frame or ask run is asked on the loop's own record and answers on its stdin
transcript retrieval--json on stdout is the record
credential isolationit reads no credential it was not pointed at, by construction: a file, a variable, or a name in the operator's secret store (--api-key-secret, since 0.17.1)

Declared capabilities

TierStatus
registrationunverified
callunverified
turnunverified
killunverified

Every tier stays unverified, on purpose. There is no registration seam and no hook. The caller seam decides only the calls the loop asks about, and its reads run unasked, so declaring call delivered would let an expectation be satisfied for calls no decision touched.

CommandSupport
halt, interrupt, tool.decidehonoured
steer, frame.set, permission.set, message.injectrefused — UNSUPPORTED_CONTROL
Decision modeStatus
observe, ask, framedelivered

The CLI's cross-adapter default is frame, and a frame-mode b10x run without a frame is refused before the spawn: with nothing to decide from, every write would be refused. Pass --decisions observe for the arm that decides nothing, or name a frame.

The caller seam: frame and ask

Since harness 0.20.0 the loop can hand its approvals to the process that launched it (run --approve caller, atlas ADR 0104). A frame or ask run launches it that way, with the ceiling at the loop's floor:

b10x-harness run … --json --approve-up-to low --approve caller …
  1. The loop announces a call (tool-requested). The adapter holds the announcement until the record says whether the loop asks about it.
  2. Above the ceiling the loop writes approval-required and blocks on stdin. The call reaches the run as a tool.requested on the control_request seam, and the frame, the embedder's tool.decide or metaharness's deadline answers it as on any other adapter.
  3. The answer is one line on the loop's stdin: {"call_id":…,"decision":"allow"} or {"call_id":…,"decision":"deny","reason":…}. abstain and replace have no word in that grammar and are written as a deny that says why; tool.decided still records which of the four metaharness decided.
  4. The loop's approval-resolved is checked against the answer. Agreement is bookkeeping; a disagreement is a warning{code: "approval-disagreed"}.

What it covers, and what it does not:

CallRisk in the catalogueUnder the caller seam
file_write, file_editmediumasked; metaharness decides
runhighasked; metaharness decides
file_read, dir_list, search, findlowruns unasked, recorded on seam none

low is the lowest ceiling 0.20.0 offers, so the reads cannot be decided here. A frame that does not admit file.read, dir.list and search, in its operation set and in every subject rule, is refused at start rather than recorded as enforced. A subject rule is judged on the flat entry's own arguments, so file_write to note.md names file:note.md.

The loop has no timeout of its own while it waits, so the run answers inside the default 30000 ms hook budget: an unanswered call is denied by metaharness's deadline at 25000 ms.

Rendering: file.read → file_read, file.write → file_write, file.edit → file_edit, dir.list → dir_list, search → search, shell → run; mcp.call, skill.load, subagent.spawn, task.todo and web.read render to nothing.

conformance b10x runs 11 deterministic vectors: a recorded executable/argv/environment launch, the same launch handing the loop two --context files (design amendment a35), the caller-seam launch (a61), a byte-exact replay of the retained 0.22.0 loop record, four enforcement records (unpublished tool, approval denial, budget stop and cancellation), two caller-seam exchanges (allow and deny, each asserting that the line metaharness writes is byte for byte the line the loop was fed), and the captured version banner against the pin. The records came from harness's loopback-only deterministic endpoint and are provider_emulated, never vendor_live. The two exchanges fill the fourth checklist row, hook input: the loop's approval-required is the question.

The four enforcement tiers reach the loop

On this arm the published toolset is the policy. The other three tiers are the operator's, and all four travel as flags to the loop rather than as a seam here — a driven run measured with three of them switched off would not be a comparison:

TierFlagWhat reaches the loop
publication--allow-program, --substrate, --substrate-embedded, --cgroup-root, --toolchainwhich tools exist at all: run only under confinement, and only for a named program
write scope--write-scope <glob>=allowed|partial-only|denied, --scope-announce stated|silentordered rules, first match wins; refused on any other kind (ScopeUnsupported), whose scope travels sealed in Frame.subjects
ceiling--approve-up-to high — never --yes, which approves the destructive class and does not combine with a ceilingcalls above the ceiling are asked, not assumed
content and program--hooks <file>, --driver <path>the operator's content rule, named and never discovered; and a program the confined run must be able to start — staged, mounted read-only and added to the loop's own allow-list, so a path on the allow-list the sandbox does not hold no longer dies at ENOENT
instructions--plugin-dir <dir>, repeatablethe plugin's skills/ and agents/ in the vendor's on-disk format, so both arms of a comparison are handed the same instructions at session start
context--context <file>, repeatable, every kind since design amendment a35metaharness reads each file beneath the --cwd tree, checks it (see the CLI reference), and hands the loop's own --context its copy of the bytes under the run's scratch root, so the loop reads what was digested; the loop renders the file into its standing instruction itself — a trust="operator" layer with < and & escaped, no preamble of ours — so the file carries system-prompt authority, and the source it names is that copy's path

What the seam maps — and what it refuses to claim

Loop recordEventWhy not tool.decided
hook-ran, blockedwarning{code: "hook-refused", message: <the reason>}that family says a seam adjudicated the call; this arm has none — consulting programs is what the loop does instead
hook-ran, failedwarning{code: "hook-failed"}
approval-resolved{approved: false}, observe runwarning{code: "approval-denied"}, carrying the call_id a reader joins to the tool.requestedevery DecidedBy the protocol has is a metaharness-side decider; the loop's own approver is none of them
approval-resolved, caller seamnothing when it agrees with metaharness's answer; warning{code: "approval-disagreed"} when it does notthe decision is already the run's tool.decided
a tool the run never publishedwarning{code: "unpublished-tool"}
a proceed, or an approval that said yesnothingbookkeeping; one event per approved call would put the stream back where the opaque mapping had it

The message names the call and invents no cause: the loop's record carries {call_id, approved}, and the reason lives only in the failed outcome's message. Note for a reader reconciling older records: up to 0.17.1 b10x-harness had its own events converter, which mapped the same approval to tool.decided. Both paths emitted metaharness.event/1, and one run described through the two differed there. 0.18.0 removed the converter, so this seam is the only path at the pin (0.22.0).

session.started on this arm

FieldValue
offered_tools, available_operationsread from the loop's opening record
skills, agentsread from the record, never asserted — the loop can be offered several
withheldread when the record carries it; null — the harness did not say — from a build too old to write it
cwdread from the record: the canonical root the loop's file tools act in (since 0.15.1); null when the record names none, as under --substrate <socket> — never the directory metaharness planned, which H7 compares it with
mcp_servers[], hardcoded: the loop has had an MCP client since 0.12.1, but every recorded run so far reported none, so the shape of an entry is unestablished and nothing is read
plugins, slash_commandsnull, deliberately — not established here; hermetic.installed_plugins answers the plugin question
credential_sourceread from the record, in the loop's own words, which name the credential class and the kind of source and never a path, a variable or a value: on 0.22.0 none, api-key:file, api-key:environment, api-key:secrets (the run named --api-key-secret), oauth:file, oauth:environment or provider:<name>. H4 holds a run that named a secret to api-key:secrets exactly
context_filesfilled by the run from what it handed the loop with --context, each entry "channel": "system-prompt": the loop renders the file into its standing instruction, which its wire codecs send as the system prompt; absent when none was declared

Measured

The driven evaluation, evals/aep/run-driven.sh, runs one step map on the Claude Code arm and on this one, each with --plugin-dir on the copied plugin. Paid, and never in the gate. The b10x arm on 2026-08-29, in order: 17 pass / 12 fail with drive exit 1 before the tiers reached the loop; 22 / 7 after; 29 / 1 once the census read a program refusal and the adapter stated mcp_servers: []; 30 / 1 with a row that scored whether the planning guidance arrived rather than how; 30 pass / 0 fail, EVAL_EXIT=0 once both arms took the plugin. The row that failed along the way was the-skill-was-offered, which a harness without a skills mechanism cannot satisfy; b10x-harness has one now.

Sources: crates/metaharness-b10x/src/lib.rs, launch.rs, seam.rs and vectors.rs; metaharness capabilities b10x; the commit messages of the changes named above.