Model provider boundary

Providers supply untrusted proposals. They never execute or judge evidence, and the deterministic verifier does not depend on provider selection.

Runtime model identity

Every response records both the model requested by Exhibit A and the identity confirmed by the serving backend. A requested name is not proof of what ran. When trustworthy telemetry is unavailable, adapters must record an explicit reason such as unknown_no_telemetry or unknown_unverified_backend; they must not omit the field or copy the requested name into it. This rule applies equally to hosted APIs, local servers, routers, and CLI-backed providers.

Each provider response is appended to the Case’s proposal_runs evidence trail. It records the operation, provider, requested model, explicitly confirmed-or-unknown runtime identity, token usage, cost and latency when available, the count of untrusted returned tool calls, and a SHA-256 digest of the structured response that produced the proposal. Responses are consumed exactly once so refinement records cannot inherit stale proposal telemetry.

Transport-specific containment

Provider adapters share a response schema, not an assumption that every transport has the same security boundary.

Provider output remains untrusted after either transport. Candidate path validation, scoped test commands, sandboxed execution, and the deterministic verdict gate remain downstream requirements.

Implemented adapters

Configuration

exhibit-a repro --provider-config providers.json ... selects the proposer through a strict role assignment. Model-backed roles are allowlisted; verifier is deliberately invalid because the deterministic judge is not a provider role. This first config surface wires only proposer; other fallible model-assisted roles will be added when their CLI workflows consume the same configuration rather than accepting inert settings.

{
  "providers": {
    "local": {
      "type": "ollama",
      "model": "qwen3:8b",
      "base_url": "http://127.0.0.1:11434/v1",
      "roles": ["proposer"]
    }
  },
  "roles": {"proposer": "local"}
}

For a hosted or TLS-terminated OpenAI-compatible endpoint, keep the credential out of the JSON file and name its environment variable instead:

{
  "providers": {
    "hosted": {
      "type": "openai_compatible",
      "model": "deployment-alias",
      "base_url": "https://models.example.com/v1",
      "api_key_env": "HOSTED_MODEL_API_KEY",
      "roles": ["proposer"]
    }
  },
  "roles": {"proposer": "hosted"}
}

Anthropic uses the same proposer-only role boundary and an environment-named credential:

{
  "providers": {
    "claude": {
      "type": "anthropic",
      "model": "claude-sonnet-5",
      "api_key_env": "ANTHROPIC_API_KEY",
      "max_tokens": 4096,
      "roles": ["proposer"]
    }
  },
  "roles": {"proposer": "claude"}
}