AI harness

The invocation contour

One transport for every model call, enforced in CI; an ordered tool pipeline with declared risk and a fail-closed approval stage; a resolved-config endpoint

One transport

Every LLM call goes through a single transport, directly or through the metering decorator composed over it. The transport owns the request, the timeout, retry with error classification, and token-usage accounting.

It carries tool definitions, tool choice including required, streaming and usage reporting - so no call site has a structural reason to build its own request.

Enforced in CI. A source-scanning boundary test fails the build on any direct call site outside a two-entry allowlist: the transport itself, and the provider connection test, which probes an unsaved configuration and must not be metered.

Retry is opt-in and bounded, defaulting to a single attempt. Falling back to a different provider mid-request is not part of it.

Metering composes over the transport, not inside it

Credit metering takes the call as a callback: it opens a reservation, runs what it is given, and settles against reported usage. It is transport-agnostic by construction.

The separation is load-bearing. Retry and error classification sit in the transport, below the reservation - so a network retry does not extend the lifetime of a held credit hold.

Billing is idempotent per attempt. Each attempt derives a fresh request id. Against the credit engine's replay rule, a request id held constant across attempts does not double-bill - it unbills, because writes after the first are treated as replays.

A tenant running its own provider key is not metered by the platform.

The tool pipeline

Stages run in order, at one insertion point per side of dispatch, not per branch.

  • Guardrails before approval. Deterministic checks run first; a call that cannot be allowed never reaches a human.
  • Fail-closed approval. A write or external call is denied when there is no approver, when the approver returns unavailable, and when the approver throws. A digital employee runs unattended, so an unanswerable approval is a denial by construction.
  • Approval is argument-bound. The record is durable and single-use, and authorises those arguments - not that tool in general.
  • A denial is its own event, with its own status and a reason code separating a guardrail block from a refused approval.

Risk is declared

Risk is a property of a registered tool, not a judgment made at the moment of use. Every MCP tool declares one; chat-only surfaces are covered by a static table beside the registry.

  • A completeness test covers every tool name offerable in chat, so a new tool cannot ship unclassified.
  • An unknown name escalates. A dynamically activated tenant skill is not statically enumerable; an unresolvable name maps to external and routes through a human decision instead of defaulting to safe or being hard-denied.

Guardrails, both sides of dispatch

Pre-dispatch guardrails can block a call before it runs. The shipped rule refuses a call that would send personal data to a third-party channel - a messenger notification, a proxied external tool.

Post-dispatch guardrails redact arguments and output on the way to the audit trail, so the record of an action is not itself a store of personal data.

Two pipelines, not one: sending data outward and recording it internally are different risks with different verdicts.

Resolved configuration

An admin-gated endpoint returns the state a live turn for a given agent would resolve to: model, tool scope, prompt hash - after provider configuration, per-agent overrides, domain scoping and prompt assembly have been applied.

Where an agent builds a curated tool list instead of taking the domain-filtered registry scope, the response carries an explicit caveat field marking the part of the list it does not cover.