Core Schema
The Mandrel Spec is formalized using a machine-readable YAML schema. It is a drop-in extension of the Agent Card defined by the Linux Foundation’s Agent2Agent (A2A) project.
Top-Level Fields
| Field | Required | Description |
|---|---|---|
apiVersion | ✓ | Must be mandrelproject.ai/v1. |
kind | ✓ | Must be MandrelSpec. |
metadata | ✓ | Standard K8s-style metadata (name, namespace, labels). |
x-mandrel-version | — | Internal schema versioning tag (e.g. v1). |
spec | ✓ | The core governance and behavior contract (see below). |
status | — | Promotion and signature block (appended by Metrology Lab). |
Schema Structure
The spec is divided into several logical blocks that govern different aspects of the agent’s lifecycle.
1. Governance & Safety (governance)
Defines the behavioral envelope and tiered escalation paths.
risk-profile: Posture defining the fallback behavior (e.g.,safety-first,balanced).circuit-breaker: System-level action on violation (e.g.,deny-all).intent-verification: Defense-in-depth check that an agent’s action matches the intent the user expressed.modeisstrict,advisoryordisabled;failure-actionisdeny,challenge,challenge-alwaysorlog. The agent declares the posture here; the platform supplies the mechanism — which model does the checking, at what endpoint — by bakingmandrelproject.ai/intent-classifierintoextensionsat promotion. An agent that could choose its own classifier could point it at an endpoint it controls, which is a verifier grading its own homework.enforcement-matrices: Mapping request attributes to actions.
Actions: Supported actions across the spec include autonomous, challenge (conditional HITL), challenge-always (mandatory HITL), and deny.
2. Enhanced Identity (identity)
Advanced identity requirements for Agent-to-Agent communication.
required-oidc-scopes: Granular scopes needed for authorization.attribute-whitelist: Attributes visible via token exchange.enforce-dpop: Mandatory sender-constrained tokens.
3. Hardened Network (network)
Strict network and data persistence claims.
egress-spiffe-targets: Permitted downstream agents.ingress-spiffe-allow-list: Workloads permitted to contact this agent.state-claim: Named resource for data persistence.
Naming a workload
Both lists take references in three forms.
| Form | Example | Means |
|---|---|---|
| Path-only | /ns/finance/sa/ledger | A workload in this deployment’s own trust domain |
| Absolute | spiffe://partner.example/ns/x/sa/y | A workload in a foreign trust domain |
| Tag | tag:spiffe-auth | A platform-defined selector |
Prefer the path-only form. A trust domain belongs to the SPIRE server or CA that issues SVIDs — a property of the deployment, not of the agent — so writing it into a spec ties that spec to one environment. A path-only reference is resolved against the platform’s trust domain at promotion time, which is what lets a single reviewed spec move between dev, staging and production without editing.
An absolute reference asserts that a SPIFFE federation relationship exists with that trust
domain. A lab rejects one the platform has not federated with: without federation there is no
bundle exchange, without a bundle the SVID cannot be validated, and the rule could never fire.
Rejecting at promotion turns a permanently dead rule into a named error. mandrel-cli emits
W030 on an absolute reference for the same reason.
Either path form may end in /* to match beneath it. The wildcard is segment-aligned:
/ns/finance/* matches /ns/finance/sa/ledger and does not match /ns/finance-test/sa/ledger.
4. Deterministic Runtime (runtime)
Execution constraints to prevent stochastic drift.
idempotency: Defines if actions can be safely retried.max-hops-allowed: Prevents infinite delegation loops (depth).max-fan-out: Maximum simultaneous outbound delegation calls at one hop (concurrent width).0means no delegations permitted; absent means no limit.max-delegations: Maximum total outbound delegation calls per turn (per-turn) and per session (per-session). Complementsmax-fan-outwith a volume constraint.0means no delegations permitted; absent means no limit.timeout-seconds: Hard execution deadline per turn.
5. Plugin Extensions (extensions)
Free-form configuration blocks owned by registered plugin validators. Each top-level key must be claimed by exactly one plugin via OwnsExtensions(). Unclaimed keys fail validation by default (Default-Deny). See the Extensions Reference for the full ownership model and override flags.
The mandrelproject.ai/ prefix is reserved. Keys under it are owned by the specification
and written by a metrology lab when it bakes platform configuration into a spec — currently
mandrelproject.ai/intent-classifier. A third-party validator that claims a key under this
prefix is rejected with E007: allowing it would let an agent supply its own substitute for
platform-owned configuration and have the claim legitimize it.
6. Orchestrator Mode (orchestrator)
Adding spec.orchestrator to a MandrelSpec marks the agent as an orchestrator-mode agent. The stanza is absent on specialist agents — its presence alone activates the orchestrator enforcement layers in the Collet. All sub-fields are optional; an empty orchestrator: {} declares orchestrator mode with no constraints.
This stanza closes the governance gap between the orchestrator (previously the trusted caller outside the governance boundary) and the specialist agents it governs. See the Orchestration & Delegation page for the enforcement model.
permitted-intents
The set of user intents this orchestrator is authorized to handle. The ingress Collet classifies each inbound request against this list before routing. Requests that do not match are handled per scope-rejection-policy.
| Value | Meaning |
|---|---|
| Absent | No intent restriction — orchestrator accepts all inbound tasks |
[] (empty list) | All inbound tasks rejected |
| One or more entries | Only the declared intents are accepted |
Each entry has a required name (classifier label) and an optional description (classifier training signal and documentation).
scope-rejection-policy
Controls the Collet’s response when an inbound request does not match permitted-intents.
| Value | Behaviour |
|---|---|
deny | Return PermissionDenied immediately (default when field is absent) |
escalate | Pause execution and raise a HITL challenge — caller must supply a HumanApprovalToken or redirect |
transfer | Query the Metrology Lab for a sibling orchestrator declaring this intent; redirect if found, otherwise deny. Requires Metrology Lab infrastructure. |
permitted-delegates
Declares which agents this orchestrator may route work to. Supports three binding modes:
| Mode | agents field | Behaviour |
|---|---|---|
| Capability-binding | Absent | Any promoted agent matching capability + optional namespace/risk-profile constraints |
| Name-binding | ["agent-name"] | Only the specifically named agent(s) — supports compliance audit requirements |
| Hybrid | ["a", "b"] | Any agent in the named set, provided capability and namespace also match |
The capability sub-field is required. namespace, risk-profile, and agents are optional narrowing constraints.
context-trust
Declares which information sources may inform the orchestrator’s routing decisions. Restricting this surface reduces the prompt injection attack surface for the agent selection step.
| Sub-field | Type | Default | Meaning |
|---|---|---|---|
user-query | bool | true | Raw inbound user query may be in the selection prompt |
tool-results | bool | true | Tool call results may be in the selection prompt |
agent-responses | bool | true | Prior specialist responses may be in the selection prompt |
session-history | enum | full | full | intents-only | none — what portion of accumulated session history is included |
intents-only reduces session history to a sequence of classified intent labels, excluding raw content.
planning-depth
Caps the number of sub-task decomposition levels the orchestrator may produce from a single inbound request. Distinct from runtime.max-hops-allowed, which bounds delegation chain depth at the wire level after planning is complete.
| Value | Meaning |
|---|---|
| Absent | No planning depth restriction |
1 | Orchestrator delegates directly to specialists; no intermediate planning |
>= 2 | Orchestrator may produce plans containing sub-plans up to this depth |
0 or negative | Invalid (E022) |
autonomy
Declares which orchestrator actions require human approval before proceeding. Three actions are governed independently:
| Action | What triggers it |
|---|---|
re-planning | Orchestrator changes its delegation plan mid-task after execution has begun |
scope-expansion | Orchestrator attempts to delegate to an agent not in its permitted-delegates list |
cross-namespace-delegation | Orchestrator delegates to an agent outside its declared namespace |
Each action accepts autonomous (proceed without approval), challenge (pause and require a HumanApprovalToken), or deny (never permitted regardless of approval). Absent fields default to autonomous.
Orchestrator Example
apiVersion: mandrelproject.ai/v1
kind: MandrelSpec
metadata:
name: finance-orchestrator
namespace: domain-finance
spec:
capabilities:
intents:
- name: "Get_Account_Balance"
description: "Routes balance inquiries to the finance specialist."
- name: "Execute_Payout"
description: "Routes payout requests with threshold enforcement."
governance:
risk-profile: "safety-first"
circuit-breaker: "deny-all"
runtime:
max-hops-allowed: 3
timeout-seconds: 30
max-delegations:
per-turn: 4
orchestrator:
permitted-intents:
- name: "Get_Account_Balance"
description: "Balance inquiries for verified accounts."
- name: "Execute_Payout"
description: "Fund transfers subject to threshold governance."
scope-rejection-policy: "escalate"
permitted-delegates:
- capability: "Get_Account_Balance"
namespace: "domain-finance"
- capability: "Execute_Payout"
namespace: "domain-finance"
risk-profile: "safety-first"
agents: ["finance-specialist"]
context-trust:
user-query: true
tool-results: false
agent-responses: false
session-history: "intents-only"
planning-depth: 2
autonomy:
re-planning: "challenge"
scope-expansion: "deny"
cross-namespace-delegation: "challenge"
7. Token Budget (token-budget)
Declares chip-based cost governance for the agent. Absent means no chip-budget enforcement is configured.
| Field | Required | Type | Default | Description |
|---|---|---|---|---|
primary-chips | ✓ | number (tenths) | — | Max Chips for normal execution (e.g. 500.0) |
model-id | ✓ | string | — | Model identifier; must exist in the active MandrelChipRateTable |
resolution-chips | — | number (tenths) | 500.0 | Isolated budget activated when primary-chips is exhausted |
clamp-points | — | array of enum | ["inbound","outbound"] | Active governance boundaries |
hold-strategy | — | enum | cold-max | Cost-estimation strategy for pre-flight holds |
graceful-degradation | — | object | see below | Behaviour on budget exhaustion |
clamp-points values:
| Value | Boundary | Enforcement |
|---|---|---|
inbound | Client → Agent | Macro-hold placed on the user chip balance before the task begins |
outbound | Agent → External (LLMs, MCP) | Micro-check before each sub-call; blocks when budget is insufficient |
hold-strategy values:
| Value | Behaviour |
|---|---|
cold-max | Assume 0% cache hit (conservative; recommended) |
warm-estimate | Apply Bayesian priors from Collet telemetry (requires outbound clamp-point; Phase 4+) |
graceful-degradation sub-fields:
| Field | Type | Default | Description |
|---|---|---|---|
resolution-message | string | see below | System message injected when transitioning to the Resolution Budget |
block-mcp-on-resolve | boolean | true | Block all MCP tool calls while the session is in resolving state |
Default resolution-message:
SYSTEM: Token budget exhausted. Immediately summarize findings and advise the user. Do not execute further tools.
Validation (E012): See the CLI reference for the full diagnostic code list. W012 is emitted by mandrel-cli validate --metrology-lab when model-id is not found in the active ChipRateTable.
Example:
spec:
token-budget:
primary-chips: 500.0
resolution-chips: 50.0
model-id: "claude-sonnet-4-6"
clamp-points: ["inbound", "outbound"]
hold-strategy: cold-max
graceful-degradation:
block-mcp-on-resolve: true
8. Promotion Status (status)
The status block is absent on raw developer-authored specs. It is appended by a
metrology lab promotion gate and must not be authored
manually. Its presence, with phase: approved and a valid signature, is what distinguishes a
realized spec from a raw declaration.
status:
phase: approved # pending | approved | rejected | revoked
promotedAt: "2026-05-07T09:00:00Z"
promotedBy: "metrology-lab-a1b2c3d4"
signature: "eyJhbGciOiJFZERTQSIsImtpZCI6ImExYjJjM2Q0In0..."
The signature is top-level
signature is a sibling of status, not a member of it. Nesting it inside status would make
the signature part of what it signs, and the exclusion rule that then becomes necessary is one
that two implementations get subtly different — presenting as an unverifiable signature with no
diagnostic naming the cause.
At the document root the rule is one sentence: the signature covers the entire document
except the signature field itself. That brings apiVersion, kind, metadata, spec and
all of status under coverage. status.phase matters most — it gates the first enforcement
decision, so leaving it unsigned would put an unauthenticated field in front of every
subsequent check.
Two hashes
| Claim | Covers | Answers |
|---|---|---|
doc_hash | The whole document minus signature | Integrity. This is what a verifier checks |
spec_hash | The source spec block as authored | Provenance, and promotion idempotency |
Neither substitutes for the other. doc_hash cannot serve idempotency, because a realized
document contains lab-generated status fields a source document does not have, so the
comparison would never match. spec_hash cannot serve integrity, because it covers neither the
baked ceilings nor the phase.
Both are computed over the RFC 8785 canonical form of the content, not over Go’s or Python’s default JSON encoding — those differ between languages, and a signature that verifies in one and not another is worse than no signature. The normative rules live in the metrology contract.
Expiry is carried in the JWT exp claim; it is not a separate YAML field.
Example Source Spec
apiVersion: mandrelproject.ai/v1
kind: MandrelSpec
metadata:
name: finance-agent
spec:
governance:
risk-profile: "safety-first"
enforcement-matrices:
- type: "threshold"
attribute: "transaction.value"
tiers:
- limit: 1000.00
action: "challenge"
runtime:
max-hops-allowed: 2
max-fan-out: 4
max-delegations:
per-turn: 10
per-session: 50
timeout-seconds: 30
Example Realized Spec
After promotion, the same spec gains a status block. The spec block values reflect the baked (platform-policy-applied) effective values.
apiVersion: mandrelproject.ai/v1
kind: MandrelSpec
metadata:
name: finance-agent
namespace: domain-finance
spec:
governance:
risk-profile: "safety-first"
enforcement-matrices:
- type: "threshold"
attribute: "transaction.value"
tiers:
- limit: 1000.00
action: "challenge"
runtime:
max-hops-allowed: 2 # baked: min(spec:2, platform:5) = 2
max-fan-out: 4
max-delegations:
per-turn: 10
per-session: 50
timeout-seconds: 30
status:
phase: approved
promotedAt: "2026-05-07T09:00:00Z"
promotedBy: "metrology-lab-a1b2c3d4"
signature: "eyJhbGciOiJFZERTQSIsImtpZCI6ImExYjJjM2Q0In0..."