Skip to content

Management API

Use /management/v1 on the private control listener to administer Teams, keys, roles, budgets, application content, deployments, namespaces and evaluation. It is not mounted on the inference listener. A live mcm_… token carries explicit permissions/scopes attenuated by the current human owner. The Console instead signs an actual user session internally. Mixed credential families fail. Management tokens cannot perform inference or create further tokens.

First management request

Prerequisites: a signed-in human with the required management permissions. Under account Access, create a scoped expiring token and store the one-time secret. Working directory: any client directory. Set MC_MANAGEMENT_TOKEN to that secret and MC_CONTROL_URL to your trusted private control origin, for example http://127.0.0.1:4001 for a local installation.

curl --fail-with-body \
  --header "Authorization: Bearer $MC_MANAGEMENT_TOKEN" \
  "$MC_CONTROL_URL/management/v1/teams"

Expected result is a data array of authorized Teams. A revoked token returns an authentication failure. The same token cannot invoke a public model. Never expose the control listener or token to an untrusted browser/backend client merely to enable inference.

Export audit events

GET /management/v1/audit-events returns one page of the Organization's management audit events as NDJSON (application/x-ndjson), one event per line in seq order, each with its prevHash and hash. It needs audit.read at Organization scope.

Query Default Meaning
after 0 Return events with a greater seq.
limit 1000 Events per page, 1 to 1,000.
until the newest seq when the first page is served The last seq this export covers. Pass the first page's value to every later page.

Response headers: x-mission-control-until (the export's until) and, while more events remain up to it, x-mission-control-next-after (the next page's after). Every page records audit.export. Any other query key, or a limit above 1,000, answers 400. Without audit.read it answers 403. See Export and verify the audit record for a complete export loop and verification.

Saved model guardrail evidence

Model Request metadata can include policyEvidence.modelGuardrails. It records the effective policy at invocation time, independently of content capture and OTLP. Read it through the existing authorized Request APIs; this is not a policy-management API or a way to grant access. The surrounding policyEvidence.configurationHash identifies the loaded configuration.

{
  "version": 1,
  "virtualModel": "support-sensitive",
  "applicableStages": ["input", "output"],
  "sources": [
    {
      "kind": "policy",
      "policyId": "baseline",
      "scopes": ["organization"],
      "inputRuleIds": ["baseline-secrets"],
      "outputRuleIds": []
    },
    {
      "kind": "policy",
      "policyId": "sensitive-data",
      "scopes": ["virtual_model"],
      "inputRuleIds": ["sensitive-contact"],
      "outputRuleIds": ["sensitive-cards"]
    }
  ],
  "maxBufferedOutputBytes": 1048576,
  "outputDelivery": "buffered"
}

sources follows effective execution ordering. An inline source instead has kind: "organization-inline" and the rule-ID arrays, with no policyId or scopes. A policy assigned at both scopes appears once with scopes: ["organization", "virtual_model"]. applicableStages identifies supported operation boundaries, not completed checks. outputDelivery is buffered or live for generation and not_applicable for retrieval; it describes streaming behavior even for a JSON request. The effective input/output rule counts are each bounded to 100 and the extension to 64 KiB.

Absence means the policy was not recorded, including legacy records or a refusal before resolution. sources: [] means an explicitly recorded configuration with no guardrail rules. Clients must tolerate missing/unknown versions and preserve valid base evidence rather than interpreting them as an empty policy. No patterns, classifier settings, descriptions or inspected text appear here. The existing ruleId and ruleAction identify the strongest match; map its ID to a source without re-resolving current YAML. See guardrail setup.

Management DTO conventions

All management bodies are strict JSON, at most 64 KiB. GET query keys are strict and duplicate decoded names fail. Console signatures bind the exact serialized pathname + query, including order and encoding; do not sort or reconstruct a signed query. Reads stop at the raw-byte limit before signature validation; malformed UTF-8 is rejected. Private Console deployment bodies are separately bounded to 256 KiB; the runtime envelope is 4 MiB. Responses are {data:...} except successful204 operations; errors are safe {error:{code,message}}. Creation is201 where noted. cache-control:no-store applies, including one-time secrets. Common status codes:400 invalid request,401 authentication,403 scope/permission,404 missing resource,409 revision/resource conflict,503 unavailable. Error bodies do not echo credentials, private arguments, or numeric ancestor budget constraints.

Use the revision of the resource/subresource being edited. Key lifecycle uses statusRevision; key policy uses policyRevision; namespace binding uses namespaceRevision. These counters are independent. Identical-target status/namespace retries are idempotent after current authority checks. Team, role, Agent draft, deployment, and budget revisions retain their actual shared-service CAS. Terminal revocations do not reactivate an identity.

Implemented resources:

Method Path under /management/v1 Body/query
GET / POST /teams POST {name,slug?}
GET /teams/{id} —
PUT /teams/{id}/status {expectedRevision,status:'active' | 'suspended'};204
GET / POST /teams/{id}/members POST {userId,roleId,sourceId}; direct membership source only
DELETE /memberships/{id}/sources/{sourceId} {};204
GET / PUT /teams/{id}/allowance PUT {expectedRevision,models?,tools?,deployments?}
GET / POST /service-accounts GET teamId; POST {teamId,name}
PUT /service-accounts/{id}/status {expectedRevision,status:'active' | 'suspended' | 'revoked'}
GET / POST /keys GET teamId; POST key creation below
POST /service-keys Same key fields, service:{id} or {name} instead of owner
GET /keys/{id} Current manageable ordinary key metadata
POST /keys/policy-review {teamId,owner,policy,operation,resourceId?}; read-only reviewOnly:true
PUT /keys/{id}/policy {expectedRevision,policy}; full policy or patch
POST /keys/{id}/rotate {overlapSeconds};201 one-time credential
POST /keys/{id}/block, /keys/{id}/unblock, /keys/{id}/revoke {expectedRevision} from statusRevision
GET / POST /roles POST {name,scopeKind,permissions}
GET / PUT /roles/{id} PUT {expectedRevision,permissions}; presets immutable
GET / POST /role-assignments GET organizationMemberId,teamId?; POST {roleId,userId,organizationMemberId,scope}
DELETE /role-assignments/{id} {};204
GET / PUT /budgets GET target=<URL-encoded JSON BudgetTarget>; PUT shared budget input
GET / POST /agents GET teamId; POST {teamId,name,description?}
GET / PATCH /agents/{id} PATCH {expectedRevision,name?,description?,status?}
GET / PUT /agents/{id}/draft PUT {expectedRevision,definition,connectionDefaults?}
GET /agents/{id}/snapshots, /agents/{id}/snapshots/{snapshotId} Immutable saved draft metadata/content
GET /agents/{id}/versions, /agents/{id}/versions/{versionId} Immutable published version metadata/content
POST /agents/{id}/preview {snapshotId}; private caller-owned Preview
GET / POST /deployments GET agentId; POST {agentId,snapshotId,expectedRevision,deployment,reviewFingerprint?}
POST /deployments/preflight {agentId,snapshotId,deployment}; review, no guarantee of remaining funds
GET /deployments/{id} Current authorized deployment metadata
PUT /deployments/{id}/status {expectedRevision,status:'active' | 'paused' | 'archived'}
POST /deployments/{id}/rollback {expectedRevision,versionId}
PUT /deployments/{id}/access {expectedRevision,audience?,apiCallers?,channels?,connectionBindings?}
PUT /deployments/{id}/execution-policy {expectedRevision,expectedPolicyRevision,policy}
PUT /deployments/{id}/execution-budget {expectedRevision,budget}; budget excludes target/kind
GET / POST /tokens POST {name,permissions,scopes,expiresAt}; actual human session required
DELETE /tokens/{id} {};204
GET / POST /namespaces GET teamId; POST {teamId,name,kind:'shared' | 'customer',assertionMode,provisioningMode?,required?}
GET /namespaces/{id} Authorized ordinary shared/customer namespace
POST / DELETE /namespace-grants, /namespace-grants/{id} POST {namespaceId,grantee:{kind:'key',keyId} | {kind:'user',userId} | {kind:'service_account',serviceAccountId}}
PUT /keys/{id}/customer-namespace {expectedRevision,namespaceId:string | null}
PUT /keys/{id}/observation-namespace {expectedRevision,namespaceId}
POST /customers {teamId,namespaceId,subject,assertion:'backend' | 'verified_person'}
GET / POST /evaluation/datasets GET teamId,includeArchived?; POST {teamId,name,description?};201
GET /evaluation/datasets/{id} teamId
GET / POST /evaluation/datasets/{id}/items GET teamId,version?,includeArchived?,limit?; POST {teamId,items,note?,expectedRevision?}
GET /evaluation/datasets/{id}/export teamId,version?; a mission-control.dataset/v1 document
POST /evaluation/datasets/{id}/import {teamId,expectedRevision,document}; the document is that same format
GET / POST /evaluation/datasets/{id}/slices GET teamId; POST {teamId,name,filter,requestKey};201
GET / POST /evaluation/datasets/{id}/slices/{sliceId}/versions GET teamId; POST {teamId,name,filter,expectedRevision,requestKey};201
GET /evaluation/datasets/{id}/slices/{sliceId}/preview Required teamId,sliceVersionId,datasetVersion; side-effect-free complete preview
GET /evaluation/subjects teamId, search?, offset?, limit? (up to 50), selectedSnapshotId?; paginated readable Team snapshots with retained older selection
GET / POST /evaluation/comparison-groups GET teamId, limit?, offset?; POST strict comparison draft plus required preflight; 201
POST /evaluation/comparison-groups/preflight Strict comparison draft including requestKey; read-only proposed-arm readiness
GET /evaluation/comparison-groups/{id} teamId; immutable arm/run identities, pinned primary metric and current summaries
GET / POST /evaluation/templates GET teamId, limit?, offset?; POST {teamId,draft,requestKey}; 201
GET / POST /evaluation/templates/{id}/versions GET teamId; POST {teamId,draft,expectedRevision,requestKey}; 201
POST /evaluation/templates/{id}/preflight {teamId,versionId,requestKey}; resolves the exact saved version read-only
POST /evaluation/templates/{id}/launch {teamId,versionId,requestKey,preflight}; 201 group with template-version provenance
GET / POST /evaluation/experiments GET teamId,agentId?,datasetId?,definitionHash?,includeArchived?,limit?; POST {teamId,name,note?,datasetId,subject,toolPolicy?,repetitions?,evaluatorIds?,selection?,preflight?};201
POST /evaluation/experiments/preflight Start input plus optional selection:{datasetVersion,items:[{itemId,revisionId}]};200; metadata-only checks and deterministic intent/configuration identity, checked time, estimates, exact original selection
POST /evaluation/experiments/smoke Selected-case start input plus requestKey and preflight:{intentHash,configurationFingerprint};201; exactly one selected original case and one repetition; visible governed smoke experiment
POST /evaluation/experiments/{id}/retry teamId, itemIds (1–1,000 original experiment-item IDs), requestKey, expectedManifestHash; 201; replay-safe paid linked child with exact historical revisions and repetitions
GET /evaluation/experiments/{id} teamId; legacy generation status remains unchanged; manifest is stored for new runs and projected with explicit unavailable facts for historical runs
GET /evaluation/experiments/{id}/summary teamId,gradingPassId?,interpretationId?; stage-aware counts and overall status, selected-pass judgments (original by default), immutable manifest hash, attempt cost {knownMicros,coverage,basis}, safe bounded failure records, failed-attempt duration, durable worker activity, and summary-specific revision/updatedAt; no answer bodies, provider text, prompts, tool arguments/results, secrets, or read-time backfill
GET / POST /evaluation/experiments/{id}/interpretations GET teamId; POST {teamId,passId,evaluatorId,evaluatorVersion,name,categories}; 201 immutable legacy categorical interpretation, explicitly selected by ID
GET / POST /evaluation/experiments/{id}/grading-passes GET teamId; POST {teamId,itemIds,evaluatorVersions,requestKey,reason}; 201 persistent grader-only pass over retained saved outputs
GET /evaluation/experiments/{id}/items teamId,limit?; outputs need evaluation.content.read
GET /evaluation/experiments/{id}/compare teamId,baseline,definitionId?,sourceKind?,candidatePassId?,baselinePassId?,candidateInterpretationId?,baselineInterpretationId?, primaryDefinitionId?, evaluatorId?, evaluatorVersion?, independent candidate/baseline evaluator ID/version overrides; response includes comparability, summary, unmatched revisions and exact-series aggregates
GET /evaluation/experiments/{id}/export teamId,baseline?,gradingPassId?,baselinePassId?,interpretationId?,baselineInterpretationId? and comparison metric selectors; a mission-control.experiment/v1 document with the items, their live scores, the summary and, with a baseline, the compare aggregates. There is no experiment import
POST /evaluation/bundles {teamId,totalBytes,sha256,requestKey};201 private upload session
GET /evaluation/bundles/{id} teamId; creator-scoped status and safe receipt, no source content
PUT /evaluation/bundles/{id}/chunks/{index} {teamId,contentBase64}; at most 40 KiB decoded chunk bytes
POST /evaluation/bundles/{id}/preview {teamId,bindings}; protected read-only reconciliation preview
POST /evaluation/bundles/{id}/commit {teamId,bindings,previewHash,requestKey,resolutions?};201 atomic suite publication
POST /evaluation/evaluators/setup {teamId,definition:{existingId} or {newDefinition},evaluator:{name,description?,kind,config},requestKey};201 complete first-version receipt
GET / POST /evaluation/evaluators GET teamId,includeArchived?; POST {teamId,name,description?,definitionId};201
GET / POST /evaluation/evaluators/{id}/versions GET teamId; POST {teamId,modelResourceId,prompt,expectedRevision};201; a saved version is immutable
GET /evaluation/evaluators/{id}/export teamId; a mission-control.evaluator/v1 document with every version; each prompt needs evaluation.content.read
POST /evaluation/evaluators/import {teamId,document};201; the document binds to an active score definition of the same name and data type in that Team, and its versions replay from 1
GET /evaluation/definitions teamId,includeArchived?; a score cannot be posted without a definition ID
GET / POST /evaluation/scores GET score filters below; POST {teamId,subject,definitionId,value,comment?,reference?};201
DELETE /evaluation/scores/{id} {teamId,mode?:'retract'\|'delete'};204
GET / PUT /evaluation/gates GET teamId,agentId; PUT gate settings below

Key creation uses {teamId,name,owner,policy?,expiresAt?,metadata?,initialCaps?}. Owner is actual {kind:'user',userId,organizationMemberId,teamMembershipId} or {kind:'service_account',serviceAccountId}. Creation plus caps is atomic through KeyProvisioningService. Each initial cap is {metric,window,unit,limitValue,expectedRevision:0} with no target/kind; duplicate dimensions fail without releasing a secret. Budget inputs are {target,kind:'allocation'|'operating',metric,window,unit,limitValue,expectedRevision}. Metrics: spend/requests/tokens/concurrency; windows:minute/daily/monthly/lifetime/concurrent; units:usd_micros/ingress/upstream_attempt/token. Supported combinations are validated by the shared service. null means no cap; zero is a hard stop. The wire format is unchanged: limitValue for usd_micros is an integer of USD micros here and in the YAML reference, while the Console enters and shows the same cap in dollars.

Company deployment connection references are exact configured Connector IDs, derived by shared managedConnectorCapabilities. Per-user Connectors allow only mode:'person'; ordinary enabled Connectors allow mode:'company',connectionRef:<connectorId>. No fixed-person fallback or invented grant ID is accepted. Managed execution identities/namespaces cannot be exported or edited through ordinary key/service/namespace routes.

Evaluation permissions are evaluation.read for every listing, evaluation.content.read for dataset item bodies, experiment item outputs, judge prompts and score comments, evaluation.manage for datasets, evaluators and gates, evaluation.execute plus the subject's own right (agent.test for a snapshot, deployment.execute for a published version) to start an experiment, and evaluation.review plus evaluation.read (an experiment item) or observability.read (a Trace, span or Session) to post a score. Deleting a score takes one of two permissions: mode:'retract' (the default) needs evaluation.review and removes only one's own score, and mode:'delete' needs evaluation.manage and removes any score in the Team. Score filters are teamId and any of from,to,definitionId,sourceKind,agentId,subjectKind,experimentId,evaluatorId,authorUserId,limit. A posted score is recorded as source_kind:'api' and origin_kind:'api' and attributed to the token's human owner; the optional reference becomes its origin_ref, one live score is held per reference and author, and reposting the same reference answers 409 resource_conflict. Gate settings are {teamId,agentId,expectedRevision,datasetId,definitionId,sourceKind?,mode?,minimumMean?,label?,minimumShare?,evaluatorId?,minimumAgreement?,minimumCoverage?,gradingPassId?,interpretationId?}; there is one gate per Agent and expectedRevision is 0 for the first write. Compare answers carry IDs, never absolute URLs: build the Console link yourself from your own Console origin plus /{organizationSlug}/evaluate/experiments/{id}/compare?teamId={teamId}&baseline={baselineId}.

Configure a customer namespace

Prerequisites: an ordinary service key and actual namespaces.manage plus budgets.manage in its Team, via an appropriately scoped management token. Working directory is irrelevant for HTTP calls; examples use fictional IDs.

  1. POST /management/v1/namespaces with {"teamId":"team_example","name":"Customers","kind":"customer","assertionMode":"backend","provisioningMode":"registered_only","required":true}. Save returned namespace ID.
  2. POST /management/v1/namespace-grants with {"namespaceId":"namespace_example","grantee":{"kind":"key","keyId":"key_example"}}.
  3. Read the key's current namespaceRevision and PUT /management/v1/keys/key_example/customer-namespace with {"expectedRevision":1,"namespaceId":"namespace_example"}. Use the actual returned revision, not the example 1.
  4. POST /management/v1/customers with {"teamId":"team_example","namespaceId":"namespace_example","subject":"customer_alice","assertion":"backend"}.
  5. Send x-mission-control-customer-subject: customer_alice on direct OpenAI/MCP calls, or body customer:{subject:'customer_alice'} on public deployment invocation. Existing tokens/keys are rechecked on each use; no gateway restart is needed for these control-plane changes.

Expected result: authoritative funding includes the trusted namespace/assertion/subject while retaining all key/service/Team/Organization constraints. Verify a registered subject succeeds and a missing/unknown one fails before upstream I/O. On 409, refresh namespaceRevision and review the current binding; do not blindly overwrite. On 403, review current grants, assertion mode and actual caller. For trusted backend first-use provisioning choose that explicit policy at creation; verified-person namespaces never use backend first-use.

Key policy example

Create a service key only with keys.service.manage in its Team. Actual resource IDs come from the catalog. The operation registry is model.generate, model.embed, model.rerank, tool.invoke, deployment.invoke, telemetry.ingest.

{
  "teamId": "team_example",
  "name": "support-worker",
  "service": { "name": "Support worker" },
  "policy": {
    "operations": ["model.generate", "telemetry.ingest"],
    "models": { "mode": "selected", "ids": ["model:support"] },
    "tools": { "mode": "none" },
    "deployments": { "mode": "none" }
  },
  "initialCaps": [
    {
      "metric": "spend",
      "window": "monthly",
      "unit": "usd_micros",
      "limitValue": 10000000,
      "expectedRevision": 0
    }
  ]
}

Send to POST /management/v1/service-keys. Store the one-time credential from the 201 receipt. Creation and initial caps are atomic: invalid dimensions or conflicting caps release no secret. A selection may be none, selected or inherit_team; only tools may include followingToolSetIds in selected mode. Omitted patch fields remain unchanged; selected empty IDs explicitly remove selection.

For budget targets use {kind:"organization"}, {kind:"team",teamId}, {kind:"team_member",teamId,teamMembershipId}, {kind:"service_account",teamId,serviceAccountId}, {kind:"key",teamId,keyId}, or {kind:"consumer",teamId,namespaceId,subject,assertion}. Allocation ceilings apply only to Teams and require Organization budgets.allocate. Operating caps apply to the validated target. Costs use integer USD micros.

Save and publish a managed application

Prerequisites: current Team agent.edit, deployment.publish and appropriate deployment access permissions; an allowed priced generation model/tool and publisher budget. All IDs are fictional: replace them with actual reads/receipts. No public request may supply execution keys, payer or runtime transport credentials.

  1. Create the content record with POST /management/v1/agents and {"teamId":"team_example","name":"Support assistant"}. Read its draft revision.
  2. Save the strict definition and account defaults with PUT /management/v1/agents/agent_example/draft:
{
  "expectedRevision": 1,
  "definition": {
    "schemaVersion": 1,
    "instructions": "Help with support requests.",
    "modelResourceId": "model:support",
    "toolResourceIds": ["tool:crm/lookup-customer"],
    "connectorRequirements": [{ "connectorId": "crm", "mode": "company" }]
  },
  "connectionDefaults": [
    { "connectorId": "crm", "mode": "company", "connectionRef": "crm" }
  ]
}

Instructions are nonempty and at most 20,000 characters/UTF-8 bytes, with prohibited control characters rejected. At most 64 unique tool IDs and one requirement for each selected Connector are accepted. Model IDs use model:<id> and tool IDs tool:<connector>/<tool>. Read the returned immutable snapshot ID. A saved draft does not alter Production.

connectionDefaults contains non-secret company account references or {connectorId,mode:"person"}. A reference must be offered by that exact Connector. Saved definition reads include these defaults alongside definition; the runtime definition schema and content hash exclude them. An account-only edit creates another snapshot with the same content hash. Omission retains compatible saved defaults; a sole company account may be resolved automatically. An ambiguous unsupplied choice remains unresolved and blocks Preview/publication with connection_choice_required. An invalid supplied reference is rejected with connection_binding_unavailable.

  1. Submit the proposal to POST /management/v1/deployments/preflight:
{
  "agentId": "agent_example",
  "snapshotId": "snapshot_example",
  "deployment": {
    "name": "Support production",
    "policyMode": "automatic",
    "monthlySpendLimitUsdMicros": 25000000,
    "channels": { "api": true, "chat": true },
    "audience": [
      { "kind": "user", "userId": "user_example", "organizationMemberId": "member_example" }
    ],
    "apiCallers": [
      { "kind": "service_account", "serviceAccountId": "service_example" }
    ]
  }
}

The review returns ready, definitionHash, executionPolicy, concrete connectionBindings, policyMode, reviewFingerprint, gate, warnings and productionConnectionsTested:false. It makes no business call and does not reserve future funds. Automatic policy derives only the saved model, declared tools and necessary operations. Team/resource policy continues to apply. New deployments default to automatic mode when no explicit policy is submitted. Supplying executionPolicy selects custom mode; combining it with policyMode:"automatic" is rejected. An existing custom policy stays in force until an access manager deliberately changes it.

Saved defaults supply the first publication's accounts. Updates retain current accounts when defaults have not changed. A conflicting draft/live account change returns connection_update_conflict; explicitly supply the reviewed deployment.connectionBindings to resolve it. Personal accounts cannot be combined with effective service-account API grants, including grants retained from a prior version.

monthlySpendLimitUsdMicros is optional. Omission retains an existing cap; null clears only the monthly operating spend cap; 0 blocks readiness. A positive limit is applied atomically with publication, never in a later unprotected step. Other dimensions remain available through /execution-budget.

  1. Publish the same proposal to POST /management/v1/deployments, adding top-level expectedRevision:0 for a new deployment and the reviewFingerprint returned by preflight. For an existing deployment include deployment.id and its current revision. Any proposal change requires a fresh review. A fingerprint mismatch returns publication_review_stale; refresh and review current state. A repeated successful submission with the same fingerprint and snapshot is reconciled to the existing deployment rather than creating another version. Publication reauthorizes and reruns required readiness checks even when a management client omits the optional fingerprint.

Publication returns a 201 receipt; a separate production smoke test exercises the deployed path. A paused destination stays paused after publication. An instructions-only update that retains the existing policy and accounts needs deployment.publish; first publication and actual access changes also need deployment.access.manage. Restoring a version checks current accounts, funding and evaluation requirements, retains live grants/caps and custom restrictions, and derives restored access in automatic mode. A resulting policy change requires access-management permission.

Audience entries are exact {kind:"user",userId,organizationMemberId}, {kind:"team",teamId} or {kind:"directory_group",directoryGroupId}. API callers use the actual key-owner DTO, including both membership IDs for a user or the service-account ID. Unavailable former owners remain identifiable for explicit removal; a replacement membership is a different grant. Omitting an access patch field preserves it; sending [] explicitly clears that list.

Management role scopes and token scopes use {kind:"organization"} or {kind:"team",teamId}. Resource expiry values such as expiresAt use epoch milliseconds. Preset roles are immutable, and ownership cannot be granted through an ordinary custom-role or directory mapping.

Budget dimension matrix

Metric Window Unit
spend daily, monthly, lifetime usd_micros
requests minute ingress or upstream_attempt
tokens minute token
concurrency concurrent ingress or upstream_attempt

Only these combinations are accepted. Ingress and upstream-attempt counts describe different dimensions; they do not add another billed provider cost. null removes one local cap; zero denies that dimension. Allocation is restricted to Team targets under Organization allocation authority.

Verification and recovery

Read back the exact resource, then exercise one allowed and one denied operation. Control-plane changes take effect on subsequent checks without restart. Secret rotation requires clients to reload the replacement credential. On 409, reload the relevant revision and review the changed proposal. On 403, inspect actual token scopes and current human authority; a token never gains the owner's broader omitted permissions. Role assignments, Team membership sources, directory mappings and custom role definitions also return 403 when the role carries a permission the token does not; see Grants stay within your own authority. Error bodies do not expose private arguments or numeric ancestor budget details.

Namespace setup currently uses this API; there is no separate Console namespace screen. Ordinary routes cannot reveal/edit reserved managed execution keys, service accounts or namespaces.

See permissions, virtual keys, deployment invocation and budgets.

Evaluation readiness and paid smoke

The preflight response contains checks (id, status:ready|blocked|unknown, severity:required|advisory, safe code, nullable permitted remediationHref, checkedAt), verdict, intentHash, configurationFingerprint, checkedAt, datasetVersion, selection, generationEstimate, gradingEstimate, plannedInvocationCount and estimateLimitations. It does not create a timestamped audit manifest. Each persisted experiment retains its actual immutable manifestHash separately. Estimates use estimatedMicros, assumptions and unknowns, never recorded-cost knownMicros.

Working directory: any client directory with curl and jq. Use the private MC_CONTROL_URL and authorized MC_MANAGEMENT_TOKEN from the first-request example. Replace the fictional IDs with readable resources. A smoke requires its own displayed one-case preflight; the full-run hash is not the selected-case hash.

cat > intended.json <<'JSON'
{"teamId":"team_support","name":"Support check","datasetId":"dataset_support","subject":{"kind":"agent_snapshot","agentId":"agent_support","snapshotId":"snapshot_support"},"toolPolicy":"read_only","repetitions":3,"evaluatorIds":[]}
JSON
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' --data-binary @intended.json "$MC_CONTROL_URL/management/v1/evaluation/experiments/preflight" > readiness.json
jq --slurpfile checked readiness.json '.repetitions=1 | .selection={datasetVersion:$checked[0].data.datasetVersion,items:[$checked[0].data.selection[0]]}' intended.json > selected.json
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' --data-binary @selected.json "$MC_CONTROL_URL/management/v1/evaluation/experiments/preflight" > smoke-readiness.json
jq --slurpfile checked smoke-readiness.json '. + {requestKey:"support-smoke-20260913-1",preflight:($checked[0].data | {intentHash,configurationFingerprint})}' selected.json > smoke.json
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' --data-binary @smoke.json "$MC_CONTROL_URL/management/v1/evaluation/experiments/smoke"

Expected result:201 with one queued experiment named Smoke · Support check, one original dataset case and one repetition. Each attached judge runs once under normal governed execution. requestKey is 1–120 ASCII letters, digits, ., _, : or -; the operation is scoped by Organization, Team, acting membership and kind. Identical bodies replay the original result atomically; changed bodies with the same key return409 request_key_conflict. Current evaluation and subject permissions are checked on replay. Selected cases must be active and match the current original dataset version/revision; temporary duplicate datasets are unnecessary.

For a full run, POST the original input plus preflight:{intentHash,configurationFingerprint} from its full-run response to /evaluation/experiments. Launch rechecks supplied identity and live metadata. Drift returns 409 stale_preflight; a required blocked check returns 409 preflight_blocked. The gateway records a guard and repeats it before claim. Clients omitting preflight retain the existing start behavior and live execution admission. The control listener and64 KiB management body limit are unchanged; Console imports remain16 MiB. See Readiness procedure and recovery for unknown checks, capture requirements, operator handoff and funding limitations.

The experiment summary also preserves the existing run-level errorCode (or null), including pre-claim abandonment causes that create no attempt failures, and returns up to 1,000 content-free items (item ID, generation status, cost coverage, latency and trace/request IDs). Each bounded safe failure includes a server-associated experimentItemId; clients must not assign a run-wide failure to every item. Console uses the session-authorized /{organizationSlug}/evaluate/experiments/{experimentId}/summary?teamId={teamId} route with Cache-Control: private, no-store. It returns the summary directly (without the management API data envelope) and masks unreadable Teams/experiments with 404. Neither summary GET reconciles spend, records a heartbeat, nor exposes the internal worker attempt reader. See the live investigation guide.

Linked selective retry

POST /evaluation/experiments/{id}/retry requires current evaluation.read, evaluation.execute and the original subject's agent.test or deployment.execute permission, including identical replay. It uses the same private listener and 64 KiB request limit. It does not grant inference rights or reserve funding. The child and actor/Team-scoped operation result commit atomically; retry after response loss with the same request key and body returns that child. Changing the body under that key returns 409 request_key_conflict.

Working directory: any client directory with curl and jq. Use the private control URL and management token from the first-request example. Replace fictional IDs; obtain the immutable manifest hash from the original experiment GET and select failed/cancelled experiment-item IDs from its items endpoint.

export MC_EXPERIMENT_ID='experiment_support_original'
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" "$MC_CONTROL_URL/management/v1/evaluation/experiments/$MC_EXPERIMENT_ID?teamId=team_support" > original.json
jq '{teamId:"team_support",itemIds:["expitem_support_failed_rep3"],requestKey:"support-retry-20260919-1",expectedManifestHash:.data.manifestHash}' original.json > retry.json
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' --data-binary @retry.json "$MC_CONTROL_URL/management/v1/evaluation/experiments/$MC_EXPERIMENT_ID/retry"

Expected response: 201 {"data":{...}} with the child experiment, parentExperimentId and its own immutable manifestHash. Child item records expose parentItemId; their repetition numbers remain unchanged. The service preserves original inputs, scores, outputs, evaluator versions and generation counters. Dataset/evaluator current-version advances do not alter historical pins. Child conversations, attempts and spend are fresh. Current authorization, known runtime/model/tool configuration and resource availability are checked at launch and claim; later drift can fail the child before invocation. Unknown upstream provider/runtime revisions remain unknown.

Only failed/cancelled items of a terminal, unarchived, released original are eligible. Safe refusals return 409 with a stable retry_… code; completed, live and approval-blocked items, missing pins and uncertain side-effect work are refused with no child. Started all-tools work is conservatively refused. Configuration drift or insufficient legacy fingerprint evidence requires restoring the original configuration or checking readiness for a new current-configuration run. Identical committed replay still returns the stored child after source availability changes. See retry eligibility and recovery codes.

Full-dataset gates filter incompatible partial smoke/retry membership before ordering and candidate limits. They retain existing score thresholds and do not impose a new score coverage minimum.

Independent grading passes

POST /evaluation/experiments/{id}/grading-passes creates a persistent grader-only pass; GET /evaluation/experiments/{id}/grading-passes?teamId=team_support lists readable passes, original first. These remain private /management/v1 routes with the existing 64 KiB request limit. The inference listener exposes neither route. Creation requires current Team evaluation.read, evaluation.execute, and evaluation.content.read; listing requires evaluation.read.

From the repository root, using a management token that includes these permissions and the exact Team scope:

curl --fail-with-body -X POST "$MC_MANAGEMENT_URL/management/v1/evaluation/experiments/experiment_example/grading-passes" \
  -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"teamId":"team_support","itemIds":["expitem_example"],"evaluatorVersions":[{"evaluatorId":"evaluator_correctness","version":1}],"requestKey":"grading-review-001","reason":"Consistency review"}'

Expected: 201 with {data:{id,experimentId,kind:"regrade",sourceOutputHashes:[{itemId,outputHash}],evaluatorVersions,createdByUserId,reason,createdAt}}. It queues graders only. The same actor membership, Team, operation kind, request key and canonical body replay the committed receipt. Changing the body returns 409 request_key_conflict; current authorization is still required on replay. Outputs must be completed, retained and in that experiment. Missing/deleted content returns grading_output_unavailable or grading_evidence_unavailable; unavailable/paused/archived evaluators return grading_evaluator_unavailable. Claim-time lost authority becomes grading_creator_unauthorized on the execution.

Summary and export accept gradingPassId; score queries accept it alongside experimentId. Compare accepts candidatePassId and baselinePassId independently and can compare two passes of the same experiment. Export uses gradingPassId for its candidate and baselinePassId for its optional baseline. Omitted selectors resolve the original pass. An explicit selector belonging to another experiment is refused. Export includes selectedPassId and score gradingPassId; manual/API series remain separate and are not relabeled as automatic pass scores. Run costs aggregate all recorded grading passes.

Gate writes accept optional gradingPassId. An explicit pass must match the full-dataset candidate's completed output selection. Required agreement reads separate version/pass calibration; omitted selectors retain original grades and legacy latest_agreement. Creating or calibrating a newer pass does not change an unchanged gate. Explicit pins are matched before the recent-run limit. Gate/preflight verdicts carry nullable gradingPassId: null means original/legacy selection; an ID names the selected pass. The production preflight comparison link translates that ID to candidatePassId and retains the existing baseline. See Regrade saved answers for Console selection, calibration and recovery.

Exports retain pass and evaluator IDs as provenance, including the selected pass's kind, creation time and evaluator versions. Deterministic initial pass IDs contain the experiment ID and permit correlation; exports are not anonymized. Separate experiment/item ID fields, the generation starter, thread and decision IDs remain omitted. Export costs and comparison run costs include all passes; per-item cost fields remain generation-only.

Measurements, coverage and legacy interpretations

Evaluator-version writes accept an optional measurement configuration. New version writes require schema v2 (the default), a plain value interpretation, higher-is-better for numeric/boolean or unordered for categorical definitions, and an 80% coverage notice. Configuration is immutable with the saved version and included in evaluator export/import. Omitted measurement configuration in old exported evaluator documents preserves schema v1 when imported. Existing stored versions retain their legacy output schema.

{
  "teamId": "team_support",
  "modelResourceId": "model:synthetic-judge",
  "expectedRevision": 1,
  "prompt": {"system": "Judge the saved answer.", "user": "{{input}} {{output}}"},
  "measurement": {
    "outputSchemaVersion": 2,
    "valueInterpretation": {"kind": "threshold", "threshold": 0.7},
    "metricDirection": "higher_is_better",
    "minimumCoverageNotice": 0.8
  }
}

valueInterpretation is {kind:"value"} or, for numeric/boolean values, {kind:"threshold",threshold:number}. It records a scored-value interpretation; gate quality thresholds are separately configured. metricDirection is higher_is_better, lower_is_better or unordered. Categorical versions require unordered. Compare honors matching numeric directions. When both values exist but direction cannot be compared, rows retain verdict:"no_score" for compatibility and include bounded verdictReason:"directions_differ"|"unordered_direction". Genuine missing-score rows have no verdictReason. Aggregates and comparison exports include withheldVerdicts:[{reason,count}] when present; observed values remain available. The Console labels these rows Comparison withheld and explains the reason. minimumCoverageNotice is a finite share in [0,1], not a gate setting.

The gateway sends schema-v2 judges a strict nested measurement envelope. Scored value has the score definition's native number, boolean or category-label type:

{"measurement":{"disposition":"scored","value":true,"reasoning":"The saved answer matches."}}
{"measurement":{"disposition":"abstained","reasonCode":"evidence_missing","reasoning":"The evidence is unavailable."}}

not_applicable uses the same non-scored shape. No value member, including null, is allowed on abstained/not-applicable output. reasonCode matches [a-z][a-z0-9_]{0,127}. A scored measurement requires a valid value and cannot contain reasonCode; extra fields are rejected. Malformed output fails the invocation as invalid_output. Legacy schema-v1 versions continue to accept {value,reasoning}. Completed non-scored executions expose measurementDisposition and measurementReasonCode with scoreId:null; reasoning and raw output remain protected content. Execution errors and cancellation are separate states.

Summary responses include interpretationId (null by default) and measurements, one row per selected evaluator version/pass. Each row has passId, evaluatorId, evaluatorVersion, definitionId, counts:{eligible,scored,abstained,notApplicable,error,pending,cancelled}, coverage, applicableUnjudged, runCompleted, selectedCompleted, minimumCoverageNotice and lowCoverage. Eligible is completed generation answers in that selected pass. Coverage is scored/(eligible-notApplicable); an empty applicable population returns null. Pending includes active/delayed work and missing expected judgments. Cancelled, error, abstained and unavailable/deleted score evidence remain gaps. Manual/API grades do not fill automatic gaps; unselected answers are not NA.

To save a legacy interpretation, POST this body to /evaluation/experiments/{id}/interpretations on the private management listener:

{
  "teamId":"team_support",
  "passId":"grading_initial_experiment_example",
  "evaluatorId":"evaluator_citations",
  "evaluatorVersion":1,
  "name":"Citation applicability",
  "categories":{"insufficient_evidence":"abstained","no_citations":"not_applicable"}
}

Expected result: 201 with {data:{id:"interpretation_…",passId,evaluatorId,evaluatorVersion,name,categories,createdByUserId,createdAt}}. Creation requires evaluation.manage and evaluation.read; listing requires evaluation.read. This route performs no generation or grading and uses the existing 64 KiB request limit. The version must be schema-v1 categorical, belong to the selected pass, and every mapped label must exist in its definition. At least one mapping is required. Invalid labels return a safe validation failure; another Team's identity is unavailable. No numeric sentinel is inferred, and saving a policy never selects it automatically.

Select interpretationId with gradingPassId on summary, score reads and experiment exports. Compare uses candidateInterpretationId and baselineInterpretationId beside their corresponding pass selectors. A mismatched tenant/pass/evaluator/version is rejected as unavailable or measurement_interpretation_selection_mismatch. There is no latest-policy lookup. The mapping excludes labels only within its bound evaluator/version; other judge evidence and comparison aggregates are retained. Experiment exports keep historical score records and their raw values, and separately include the selected interpretation mapping and derived measurements. The comparison includes baselineInterpretation; creator user identity is omitted from exported policies. A selected score CSV adds interpretation_id.

Gate writes optionally accept minimumCoverage in [0,1], with sourceKind:"judge" and evaluatorId. A saved interpretation additionally requires its explicit matching gradingPassId; the policy identifies its evaluator version. Both raw and interpreted gate writes require an existing, unarchived evaluator whose definition matches the gate; only the calibration lookup differs. An empty applicable population or partial-pass selection cannot qualify as evidence, even at minimum zero. Omission leaves coverage unenforced. GateVerdict and production preflight carry nullable gradingPassId and interpretationId for evidence links. Existing gates retain original/raw scores and legacy global calibration until deliberately edited. Explicit raw-pass calibration and interpreted calibration use separate stored records and report only compatible scored pairs; one cannot overwrite the other.

See Measurements and coverage for the Console workflow and failure recovery.

Console calibration navigation resolves an omitted calibrateVersion from the explicitly selected immutable pass/interpretation, not the evaluator's latest version. Explicit mismatches remain refusals. Apply calibration scope performs an authorized evaluator metadata read and redirects to the selected scope, preserving pass/policy IDs and refreshing compatible queues without any calibration write. It works without a selected queue; the separate Calibrate action retains current permission, version, pass, policy and queue validation. Unscoped legacy calibration remains available.

Assertion evaluator versions

The existing private evaluator routes support kind:"assertion" when creating an evaluator. Omitted kind remains llm_judge. Assertions require an active boolean score definition and retain kind/definition immutably. Create/import/save do not provision a Judge identity or require model allowance for assertions. LLM judges retain their existing model, prompt, identity and funding requirements.

Working directory: any client directory with curl and jq, using the private MC_CONTROL_URL and scoped MC_MANAGEMENT_TOKEN from the first-request example. Substitute a readable Team and boolean definition. Creation/save/import require evaluation.manage; reading protected configuration also requires evaluation.content.read.

curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' \
  --data '{"teamId":"team_support","name":"Article check","kind":"assertion","definitionId":"scoredef_boolean"}' \
  "$MC_CONTROL_URL/management/v1/evaluation/evaluators" > evaluator.json
MC_EVALUATOR_ID=$(jq -r '.data.id' evaluator.json)
curl --fail-with-body -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json' \
  --data '{"teamId":"team_support","expectedRevision":1,"assertion":{"semantics":"unicode-script-letters/v1","assertion":{"op":"contains","path":"/output/text","text":"Article 39"}}}' \
  "$MC_CONTROL_URL/management/v1/evaluation/evaluators/$MC_EVALUATOR_ID/versions"

Expected result: 201 with immutable version 1, kind:"assertion", schema v2, modelResourceId:null, prompt:null, promptHash:null, assertionHash and protected assertion configuration. A metadata-only read returns assertion:null,assertionHidden:true. Export retains assertion kind, hash, configuration and measurement policy; a hidden-configuration export cannot be imported. Validated old LLM imports alone may preserve schema v1; normal new version writes require schema v2.

Configuration supports equals, contains, regex, shape, count, tool_called, script_ratio, all and any, with a 64 KiB configuration bound, depth 10 and 100 assertion nodes. JSON pointers and RE2 patterns are validated at save/import. The enclosing management body is independently capped at 64 KiB. Invalid regex, pointer, shape/range, excessive size or incompatible score definition returns 400 without saving a version. For example {"op":"regex","path":"/output/text","pattern":"(?=x)"} is refused because RE2 does not support lookahead. See the assertion contract for all operators, protected explanation bounds and frozen Unicode semantics.

Attachment, preflight, sample execution and regrading need no dummy model or prompt. Preflight gradingEstimate.judgments includes assertions; plannedInvocationCount excludes their model calls and an assertion-only grading estimate is zero model cost. Execution completion has decisionId:null, traceId:null, measured latencyMs, and costMicros:0; attempt model-cost coverage is complete, including recovery after interrupted settlement. This does not price external evidence. Tool assertions and production trace assertions use complete protected new gateway captures; missing/legacy/partial evidence retains tool_evidence_unavailable or assertion_evidence_unavailable abstention. Assistant-authored tool lists are never elevated into runtime proof. Existing permission/creator checks, protected explanations, score normalization and selected-pass behavior remain in force.

Tool validators and protected evidence

Create an evaluator with kind:"tool_validator" and save a version through the same evaluator routes using {teamId,expectedRevision,validator} instead of model/prompt fields. validator contains toolResourceId, the registered sha256: schemaHash, bounded arguments, optional timeoutMs (default30000, maximum60000), and optional maxResponseBytes (maximum/default262144). The strict validator contract defines mappings, Measurement/source output, effective capture limits, registration and recovery. Arbitrary URL callbacks, action/unknown tools, required unmapped fields and schema drift return400 before version persistence/business invocation as appropriate.

An explicit save/import validates the Team allowance and updates its managed evaluation key with the selected validated tool plus retained allowed evaluator resources. Reads expose kind, toolResourceId, toolSchemaHash, validatorHash and protected validator; without evaluation content authority, configuration is null and validatorHidden:true. This does not grant a human inference permission or alter ordinary keys. Import/export preserves immutable kind/configuration; redacted configurations cannot be replayed.

Start/preflight/smoke and grading-pass requests accept optional gradingPlan:{enrichment?:{evaluatorId,version},judges:[{evaluatorId,version,evidenceFrom:"generation"|"enrichment"}]}. Regrade evaluatorVersions must match all plan stages. Plans refuse duplicate stage identities, cycles, missing enrichment and cross-Team/unknown versions. New intent includes the entire plan, while old manifest bytes/hashes and omitted-plan generation behavior remain intact. Retry children retain the original plan and use fresh outputs.

Method Private path Input and result
GET /management/v1/evaluation/executions/{id}/evidence teamId, optional result=true; returns {data:{projection,output}} after current evaluation and observability content authorization and source retention checks. result=true selects the validator result bundle; default selects the execution's input projection.

projection carries format:"mission-control.evidence/v1", exact hash, capturedAt, completeness, call-ID-correlated toolCalls, optional frozen messages with independently marked input/output completeness, source content/hashes and source references. Optional dependencies contain only server-published protected input references/hashes; reads require those parent objects, their retained sources and the invocation’s own source. Both inspection selectors validate all bound dependencies before returning judgment output, with the earliest inherited expiry. The protected execution output is read only by its published reference/hash. Evidence reads return403 without current authority and400 when pending, unavailable, expired or changed. They never fall back to a newer unreferenced application body or another Team. This route exists only on the private management listener; its response is no-store. No public inference route is added.

Execution metadata includes evidence references/hashes without bodies. Source-dependent output is hidden in synchronous summary/list reads and is available through authorized evidence inspection. Source-derived reasoning is not copied into score comments. Validator metadata additionally reports costCoverage (complete, partial, unknown) and costBasis (configured_tool_fee, unknown); legacy scalar costMicros:0 alone must not be interpreted as free when coverage is unknown. summarizeExecutions adds costCoverage when its scope contains validator work; it uses the same governed fee/adjustment projection as execution detail. Mixed totals remain partial, including conservative coverage for legacy scalar contributions. Existing non-validator-only summary shapes remain unchanged. Summary grading.waitingForEvidence is a subset of delayed, not an extra denominator. It includes collector waits and enrichment dependencies.

A paid validator result is saved before waiting for capture; restart/collector delay never re-invokes that saved result. The five-minute tool-result ingestion window ends honestly if capture remains absent. Independent downstream jobs terminate when enrichment fails, abstains or cannot provide evidence. All stages and passes remain included in recorded run costs. Private request bodies remain64 KiB and Console file import remains16 MiB; bounded staging remains planned.

From the repository root, with a Team-scoped management token carrying the required current permissions:

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" \
  "$MC_CONTROL_URL/management/v1/evaluation/executions/evalexec_example/evidence?teamId=team_support&result=true"

Expected result is the exact retained projection/output for that execution. A hidden caller is refused before any source content is returned; a deleted source remains unavailable despite its saved hash. Source-specific corpus interpretation is provided by the registered validator, not by a Mission Control corpus parser.

Explicit comparison selection and statistics

GET /evaluation/experiments/{id}/compare accepts primaryDefinitionId (definitionId remains a compatible alias), sourceKind, evaluatorId, evaluatorVersion, and independent candidateEvaluatorId/candidateEvaluatorVersion and baselineEvaluatorId/baselineEvaluatorVersion overrides. Versions must be positive integers; conflicting definition aliases are refused. Pass and interpretation selectors remain independent. A unique series on a selected pass can be resolved without explicit grader selectors; an ambiguous series withholds directional results. Judge keys include definition, source, evaluator and version. Different definitions or manual/API provenance never merge.

Working directory: any trusted client directory. Use a current Team-scoped evaluation.read management token and actual IDs from your installation:

curl --fail-with-body --get \
  --header "Authorization: Bearer $MC_MANAGEMENT_TOKEN" \
  --data-urlencode "teamId=$MC_TEAM_ID" \
  --data-urlencode "baseline=$MC_BASELINE_ID" \
  --data-urlencode "primaryDefinitionId=$MC_DEFINITION_ID" \
  --data-urlencode "sourceKind=judge" \
  --data-urlencode "evaluatorId=$MC_EVALUATOR_ID" \
  --data-urlencode "evaluatorVersion=1" \
  --data-urlencode "candidatePassId=$MC_CANDIDATE_PASS_ID" \
  --data-urlencode "baselinePassId=$MC_BASELINE_PASS_ID" \
  "$MC_CONTROL_URL/management/v1/evaluation/experiments/$MC_EXPERIMENT_ID/compare"

The response adds selection (each side's effective metric identity), comparability (compatible, typed issues and safe configuration differences), summary, and unmatched. Rows pair by datasetItemRevisionId and repetition. Different dataset versions are allowed only within the same dataset; changed revisions are exclusions. summary.quality has equal-weight case means/ranges, each arm's run spread, candidate-minus-baseline mean/spread and interval95. At least 20 distinct paired scored cases use 2,000 deterministic case-cluster resamples; below 20 the interval is null. Incompatible selected series withhold paired numeric statistics and directional verdicts, retaining observed values. Categories have distributions and label changes without numeric averages. Per-arm aggregate means carry independent observed counts; paired counts never inflate from unmatched values.

costDelta is nullable; it requires two completed generations and complete known cost facts. Row cost retains knownMicros, coverage and basis; legacy scalar costMicros is not a completeness claim. summary.costs and top-level costs include every pass/stage, including failed work, configured-zero fees and unknown fees. Unknown provenance is not equality. Configuration never includes protected prompt/context/source bodies and comparison never restores deleted evidence.

Export accepts the same metric selectors, with gradingPassId and interpretationId naming the candidate. Every exported item now includes its immutable datasetItemRevisionId and a cost fact (knownMicros, coverage, basis) for that item's generation attempts, including failed attempts; it excludes grading. Legacy costMicros remains for compatibility and cannot prove complete or zero cost. Top-level costs and comparison full-run costs include all grading passes/stages. Its comparison includes selection, summary, safe configuration and unmatched revision/repetition metadata, without adding unmatched answer bodies or separate experiment item IDs. Exact-version/full-scope gate authority is unchanged. See Compare evaluation evidence for the denominators and Console procedure. Invalid versions return 400; foreign or unavailable pass/interpretation identities return the existing scoped refusal rather than switching selection.

Explicit dataset case fields

Dataset item writes and mission-control.dataset/v1 imports accept protected optional sourceNamespace, externalId, title, tags and strict caseSettings. Identity requires a nonempty namespace/ID pair, each at most 200 characters. Title permits 300 characters; tags permit 20 strings of 1–64 characters. caseSettings accepts only reasoningEffort (low, medium, high) and userContext (16 KiB UTF-8). Existing body and 512 KiB combined-item limits still apply. Unknown execution keys such as model/tools/credentials are refused; arbitrary metadata remains data. The private management request limit remains 64 KiB.

Example item in POST /evaluation/datasets/{id}/items:

{"teamId":"team_support","expectedRevision":1,"items":[{"sourceNamespace":"synthetic.memory","externalId":"memory-1","title":"Remember language preference","tags":["memory"],"input":{"text":"Reply using my preferred language."},"caseSettings":{"userContext":"Prefer Arabic.","reasoningEffort":"low"}}]}

Expected result: one new dataset version with an opaque logical item ID. JSON/CSV exports carry explicit fields only with evaluation.content.read. Imports reconcile by namespace/ID before creating an item, reject duplicates and conflicting identities, and retain immutable established identity through archive/deletion. Per-item failures name field when applicable, without copying protected stored values. Legacy items without formal fields and their content hashes remain unchanged. An import updating an established source identity must carry the pair; deleted identities cannot be resurrected. Blank title or tags:[] explicitly clears formal presentation; caseSettings:{} removes settings on a new revision.

Agent definitions optionally declare datasetContextBinding:true; absence stays absent in canonical saved JSON and means disabled. The field participates in the definition hash. Experiment readiness blocks unbound user context and unsupported reasoning. Runtime loads settings only from the original admitted item's protected revision, with current authority and hash verification; public AG-UI fields cannot supply them. Readiness includes caseCapabilities with declared reasoningEfforts, runtime context support and snapshot contextBindingEnabled. New manifests add safe effectiveSettings.contextBindingIdentity and caseSettingsIdentity hashes; old manifests remain byte-for-byte unchanged. Comparison configuration exposes those safe identities, not context bodies. Retries pin original settings and start a fresh conversation; regrade consumes saved outputs only. See case authoring for the procedure and supported route limits.

Versioned dataset slices

Slice writes require evaluation.manage and evaluation.read. Their requestKey accepts 1–100 printable ASCII characters, including spaces and punctuation; control characters are refused. Existing smoke/retry/regrade key rules are unchanged. GET list/version reads require evaluation.read, returning opaque IDs/version/hash with name:null, filter:null, contentHidden:true without evaluation.content.read. Preview and slice launch require current content authority before loading/evaluating protected predicates. Tenant and dataset ownership scope every lookup. The routes exist only on the private management listener and retain its 64 KiB request bound.

From any working directory, create a filter with a Team-scoped management token (fictional IDs below):

curl --fail-with-body "$MANAGEMENT_URL/management/v1/evaluation/datasets/dataset_example/slices" \
  -H "Authorization: Bearer $MANAGEMENT_TOKEN" -H 'Content-Type: application/json' \
  -d '{"teamId":"team_example","name":"Smoke","filter":{"allTags":["smoke"]},"requestKey":"slice-smoke-1"}'

Expected 201 {"data":{"sliceId":"slice_…","sliceVersionId":"sliceversion_…","version":1,"revision":1,"filterHash":"…"}}. Repeat the same key/body after response loss to receive the same receipt. Changed bodies conflict with request_key_conflict; current manage/read authority is rechecked on replay. Only a body hash and safe result are stored in the operation receipt; names and filter content stay in protected application storage. Saving a new version adds required expectedRevision and a new requestKey, returning the next revision/version. Stale revisions fail with slice_revision_conflict (409); no partial version is published.

curl --fail-with-body "$MANAGEMENT_URL/management/v1/evaluation/datasets/dataset_example/slices/slice_example/preview?teamId=team_example&sliceVersionId=sliceversion_example&datasetVersion=4" \
  -H "Authorization: Bearer $MANAGEMENT_TOKEN"

Expected 200 {"data":{"datasetId":"dataset_example","datasetVersion":4,"slice":{"sliceId":"slice_example","sliceVersionId":"sliceversion_example","version":1,"filterHash":"…","selectedCount":14,"excludedCount":127},"items":[{"itemId":"item_example","revisionId":"itemrev_example"}]}} (illustrative counts; the real array contains every selected pair). Empty previews return zero selected items. A missing dataset version query fails validation; there is no implicit current-version fallback.

filter is a strict object containing optional allTags, anyTags, itemIds, metadataEquals. Bounds are 20 tags combined (1–64 characters each), 500 item IDs (1–200 characters), 20 scalar own-key equalities (key 1–200, string value at most 4,096 characters); finite numbers, booleans and null are supported. Dimensions intersect, comparisons are type-strict, missing is distinct from null, duplicates cannot duplicate work, and expressions/nested predicates are refused. Formal empty tags override legacy metadata.tags; absent formal tags alone permit that fallback. Complete resolution refuses more than 5,000 active cases with slice_dataset_scope_limit (400), rather than returning truncated counts. Missing protected candidate content returns slice_dataset_content_unavailable (400); absent filter content returns slice_content_unavailable. Preview without content authority is 403. Foreign resources are not found (404).

Include this field in the existing preflight, launch or paid smoke body to bind that exact selection:

{"slice":{"sliceId":"slice_example","sliceVersionId":"sliceversion_example","datasetVersion":4}}

All existing subject/name/dataset/Team fields are still required. A paid smoke's existing explicit selection must belong to this resolved slice and dataset version. The manifest records safe slice identity/counts beside exact selected historical revision/repetition membership and selectionHash; readiness receipts cover both. A different slice version invalidates readiness even if its filter selects the same cases. Queued admission reads original membership, never the current filter. Retry keeps the original slice identity but can select fewer pairs. Historical manifests remain byte-for-byte unchanged. Full-dataset gates still compare actual full historical membership before SQL LIMIT. See Versioned slices for recovery and Console steps.

Comparison groups and templates

These routes remain on the private control listener, with strict JSON/query keys and the existing 64 KiB request limit. New group/template operations use 1–100 printable ASCII requestKey characters. Scope is Organization + Team + operation + actor + key. Repeating the same body/key returns the original resource; a different body returns 409 request_key_conflict. Receipts contain hashes and safe IDs, not protected drafts. Current permissions are checked again on replay, including variant edit authority; availability drift does not create duplicate partial arms.

Readiness and launch require evaluation.read, evaluation.execute, evaluation.content.read, agent.read and agent.test. Proposed model variants additionally require agent.edit and are staged without changing the agent draft/deployment. All arms must pass before the group, real immutable snapshots and experiments are published atomically. Readiness itself creates nothing. Current resource/settings/content authority is checked per arm again at execution. Stored group membership governs current evaluation.content.read and the other group execution rights before generation claims, protected input reads, Preview/admission and new turns. Revocation stops new dispatch while already-dispatched calls still settle and retain output/evidence/cost. Original group grading is also checked before claim, protected input reads and managed model/tool dispatch. That supplemental guard selects stored group membership, experiment origin and an initial grading pass; explicit regrades, sample tests and legacy standalone grading keep their existing rules. Result/cost settlement is separate and continues under existing evidence-retention rules. Variant agent.edit remains creation-only; legacy standalone generation behavior is unchanged. Unknown advisory pricing is not an authorization decision.

A draft has teamId, name, baseSnapshotId, explicit historical datasetId/datasetVersion, optional slice:{sliceId,sliceVersionId}, 2–5 arms, required gradingPlan, required primary:{definitionId,evaluatorId,version}, 1–5 repetitions, toolPolicy, and requestKey. Each arm is exactly {label,modelResourceId} or {label,snapshotId}. The grading plan preserves optional enrichment:{evaluatorId,version} and judges:[{evaluatorId,version,evidenceFrom}]; the primary version must be one selected pinned grader measuring that definition. There is no ambiguous latest-grader fallback. datasetVersion is also an optional explicit historical pin on standalone experiment start/preflight bodies; omission retains existing current-version behavior.

From any working directory, prepare a bounded synthetic comparison (replace fictional IDs with current permitted resources; keep tokens in environment variables):

cat > comparison.json <<'JSON'
{
  "teamId": "team_example", "name": "Synthetic comparison",
  "baseSnapshotId": "snapshot_example", "datasetId": "dataset_example", "datasetVersion": 1,
  "arms": [
    {"label": "A", "modelResourceId": "model:example-a"},
    {"label": "B", "snapshotId": "snapshot_other"}
  ],
  "gradingPlan": {"judges": [{"evaluatorId": "evaluator_presence", "version": 1, "evidenceFrom": "generation"}]},
  "primary": {"definitionId": "definition_presence", "evaluatorId": "evaluator_presence", "version": 1},
  "repetitions": 1, "toolPolicy": "none", "requestKey": "comparison example 1"
}
JSON
curl --fail-with-body "$MC_CONTROL_URL/management/v1/evaluation/comparison-groups/preflight"   -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json'   --data-binary @comparison.json > readiness.json
jq --slurpfile r readiness.json '. + {preflight:{intentHash:$r[0].data.intentHash,configurationFingerprint:$r[0].data.configurationFingerprint}}' comparison.json > launch.json
curl --fail-with-body "$MC_CONTROL_URL/management/v1/evaluation/comparison-groups"   -H "Authorization: Bearer $MC_MANAGEMENT_TOKEN" -H 'Content-Type: application/json'   --data-binary @launch.json

Readiness returns checks/verdict, actual proposed model/profile/settings facts, generationExecutions, all judgments, initialInvocationCount, graderAttemptLimit:5, and plannedInvocationCount. Initial counts include generation plus model/tool graders; assertions add no model/tool work. The admission count includes generation plus five possible worker attempts per model/tool grader, including enrichment, and may not exceed 1,000. A configured zero-fee tool still counts as tool work. Provider retries, agent rounds and unknown prices remain outside a guaranteed monetary ceiling. Launch returns 201 {data:{id,name,primary,arms,...}}; each arm identifies its immutable experiment and current stage-aware summary. Pinned manifests retain exact revision/repetition membership, grading dependencies, context/settings hashes and safe configuration identities.

Safe failures include comparison_invocation_limit (reduce explicit work), comparison_primary_invalid (select an actual pinned measurement), stale_preflight (check again), and preflight_blocked (resolve the reported checks). Neither a denied later arm nor a failed receipt publication exposes partial groups or snapshots. Do not turn an uncertain response into a new key unless you intend a new run.

Template POST bodies use draft without teamId/requestKey; those are the outer operation fields. Create returns {id,versionId,version:1,revision:1}. Saving a version adds expectedRevision; a stale revision returns template_revision_conflict. Writes require evaluation.manage; reads require evaluation.read and hide draft without evaluation.content.read. Launch resolves the explicitly selected version, preserving full references; later template edits cannot change historical groups. Missing selected bodies refuse a fresh launch. Exact current/selected reads do not depend on unrelated historical bodies. A template-launch receipt recovers the original group even if that template body later becomes unavailable, under current read/execute/content and variant permissions.

The scheduler gives groups one complete item quantum, persists arm order and yields an opportunity to unrelated queued work. It keeps preview deployments and completed turns. Graceful group shutdown drains a current item with lease renewal. Expired hard-crash group items become invocation_indeterminate without automatic replay; original thread/attempt/cost evidence remains. Explicit linked retries still enforce retry_side_effect_uncertain for all-tools uncertainty. Existing standalone interruption behavior is unchanged. No paid exactly-once guarantee is made.

Complete grader setup

POST /management/v1/evaluation/evaluators/setup is private control only and requires current Team evaluation.manage. It creates an evaluator with its first immutable version and either an existing active definition or a new definition in the same control transaction. Protected content is durable before publication. Identity/key-policy provisioning is part of that transaction for model/tool graders; assertions need no identity. Failed validation or publication leaves no visible partial grader, new definition or widened policy.

From the repository root on a host authorized to reach the private management listener, with MCM_TOKEN set to your management token:

curl --fail-with-body http://127.0.0.1:4001/management/v1/evaluation/evaluators/setup \
  -H "Authorization: Bearer $MCM_TOKEN" -H 'Content-Type: application/json' \
  --data '{"teamId":"team_support","requestKey":"grader-setup-001","definition":{"newDefinition":{"name":"Has answer","dataType":"boolean"}},"evaluator":{"name":"Answer present","kind":"assertion","config":{"assertion":{"semantics":"unicode-script-letters/v1","assertion":{"op":"count","path":"/output/text","min":1,"max":100000}}}}}'

Expected: 201 {"data":{"definitionId":"scoredef_…","evaluatorId":"evaluator_…","evaluatorVersion":1}}. The receipt contains IDs/version only, never private criteria. No execution or spend starts. To reuse a definition, replace newDefinition with "existingId":"scoredef_example" inside definition; do not provide both. New definitions require name and dataType; numeric definitions accept finite minValue/maxValue with min < max (defaults 0/1), categorical definitions require 1–50 unique {label,value} entries, and boolean definitions accept neither range nor categories. description is optional.

The evaluator requires name, kind and config, with optional description. Configuration is strict by kind:

  • llm_judge: {modelResourceId,prompt:{system?,user},measurement?}.
  • assertion: {assertion:{semantics:"unicode-script-letters/v1",assertion:<bounded AST>},measurement?}; its definition must be boolean.
  • tool_validator: {validator:{toolResourceId,schemaHash,arguments,timeoutMs?,maxResponseBytes?},measurement?}; only currently registered read-only tools inside the Team allowance are accepted.

measurement follows the immutable schema-v2 version contract above. Unknown/cross-kind fields, archived or foreign definitions, name conflicts, invalid prompts/assertions, denied models/tools and schema drift refuse the complete save. The management request remains limited to 64 KiB. No credentials, arbitrary callback URLs or execution results are accepted.

requestKey is 1–100 printable ASCII characters, scoped by Organization, Team, actor membership and evaluator_setup. An identical canonical body returns the same IDs; changing it returns 409 request_key_conflict. Current manage authority is required on replay. Receipt recovery does not re-read deleted private criteria or re-provision a changed model/tool. Existing two-step creation/version routes and single-evaluator imports remain supported.

The Console's Test on samples is a separate explicit action over the current saved version, with 1–5 completed answers. It stores test executions/results/costs without experiment scores, automatic coverage or gate evidence. New samples record their actual requester (and management token when present), require live evaluation read/execute/content permissions, and require both observability read permissions for source-consuming graders/tool validators. Checks precede claim, protected reads and new dispatch; settlement remains possible after revocation. An unfinished historical sample without a recorded requester fails with test_requester_unavailable; current permission loss fails with test_requester_unauthorized. Existing completed results are retained. The Console refuses stale current-version selection with evaluator_test_version_changed. This change does not add a public sample-test route.

Evaluation suite bundles

These five private control routes stage and reconcile a complete suite. All writes and previews require current Team evaluation.read, evaluation.manage and evaluation.content.read. A staging session is bound to its creator's Organization membership; status needs that creator's current evaluation.read. Tokens retain their live owner/scope restrictions. GET status and POST preview make no writes. Imported templates also check snapshot permissions and current model allowances; proposed variants require agent.edit in addition to read/test rights.

Method Path under /management/v1 Strict input and result
POST /evaluation/bundles {teamId,totalBytes,sha256,requestKey};201 upload status. totalBytes1–16 MiB, lowercase SHA-256 of exact UTF-8 file bytes.
GET /evaluation/bundles/{id} teamId; status, exact byte/hash/chunk counts, received indexes, expiry and safe committed result. No source body or labels.
PUT /evaluation/bundles/{id}/chunks/{index} {teamId,contentBase64};200 updated status. Canonical base64 of at most 40 KiB raw bytes, exact expected length/index.
POST /evaluation/bundles/{id}/preview {teamId,bindings};200 {sessionId,sourceNamespace,previewHash,entries,issues,valid,caseSettings}. Validation problems are protected field-addressed issues; authority/session lookup remains 403/404.
POST /evaluation/bundles/{id}/commit {teamId,bindings,previewHash,requestKey,resolutions?};201 atomic safe receipt. Each changed/conflicting source key requires resolutions[key]="update".

The existing 64 KiB request limit is unchanged. 40 KiB raw chunks encode to at most 54,616 base64 characters, leaving room for the JSON envelope. A 16 MiB file has at most 410 chunks. Chunks can arrive out of order; identical packets replay, different bytes at an accepted index return409 bundle_chunk_conflict. Exact lengths, every chunk hash, whole-file SHA-256, valid UTF-8/JSON and every object's content hash are checked. Staging expires after 24 hours; status becomes expired, new writes refuse 409 bundle_expired, and a new session is required. Committing an already committed session under a different operation key refuses 409 bundle_already_committed.

bindings contains optional models, tools, snapshots and objects maps (empty maps by default). Models/tools map a source resource reference to an existing permitted target ID. Snapshots map a source alias to a readable/testable saved snapshot in the Team. Objects map a source object key to a deliberately selected existing same-Team resource of the same kind. Omitted mappings use exact source references. Unused mappings, duplicate target bindings, foreign resources and changes to an established source-to-resource identity are refused. A tool mapping must still match the registered schema/classification. No credentials or arbitrary validator URL can be imported.

The file is:

{
  "format": "mission-control.evaluation-bundle/v1",
  "sourceNamespace": "example/legal-suite",
  "objects": [
    {"key":"metric","kind":"definition","contentHash":"<computed lowercase SHA-256>","value":{"name":"Has answer","dataType":"boolean"}}
  ]
}

This schema illustration needs a computed hash; use the validated synthetic fixture for an executable example. Each contentHash hashes canonical sorted JSON of {kind,value} before schema defaults or target bindings using the exported bundleContentHash helper. The whole-file hash covers the exact uploaded bytes. Non-JSON/non-finite values, lossy sparse arrays, unknown fields, duplicate keys/resource names, missing or wrong-kind references and unsupported configurations refuse the document. Maximums: 200 objects, 5,000 provided cases, 40 levels of JSON nesting and 1,000,000 JSON nodes. Source namespace/object keys are stored privately; their stable hash indexes the control association.

Supported values:

  • definition: the complete setup API's new-definition shape (name, dataType, optional description/range/categories).
  • evaluator: {definitionKey,evaluator:{name,description?,kind,config}}, using the same strict schema-v2 LLM/assertion/tool-validator configurations as complete setup. A new object gets version 1; changed criteria append a version. Existing metadata-only changes do not invent a criteria version.
  • dataset: {name,description?,items:[...]} with current explicit dataset item fields. Every case requires id or sourceNamespace/externalId; imported source labels may use {kind:"import",source?}. No foreign trace/session provenance is adopted. Supplied cases merge, omitted cases stay active/retained, unchanged content keeps its revision, and delete reservations are not resurrected. The complete existing reconciliation scope is bounded at 10,000 open identities and 5,000 active cases.
  • slice: {name,datasetKey,filter} with the bounded slice DSL. Provided case-ID aliases resolve to their established local IDs.
  • template: {name,baseSnapshot,datasetKey,sliceKey?,arms,gradingPlan,primary,toolPolicy,repetitions}. Arms are {label,modelResourceId} or {label,snapshot}. The plan uses optional enrichment:{evaluatorKey} and judges:[{evaluatorKey,evidenceFrom}], at most 10 total distinct graders. primary:{definitionKey,evaluatorKey} identifies an attached metric. Commit pins actual dataset/slice/grader versions; enrichment must be a tool validator. No agent draft or deployment changes, and no experiment starts.

Preview identifies create/adopt/unchanged/update/conflict actions and current/proposed revisions. It validates allowed resources without provisioning execution identity, paid calls or provisional control writes. Formal case settings are checked for provided cases selected by each imported template's slice. Unbound, retained and unselected case settings still need launch readiness; metadata never acts as settings. Immutable definition shape, grader kind/definition and slice parent changes are unsupported in place. Score categories used by live scores can only be extended. Existing active auto-evaluation rules use an updated grader's new current version for future samples; preview warns about those rules.

Commit recomputes the preview under the control write transaction. A changed target returns 409 bundle_preview_stale; unresolved changes return409 bundle_conflict_requires_resolution. Protected resource bodies and their recovery registry become durable together before the outer control transaction publishes all resources, version pins, source associations, audit events and receipt. A failure publishes none of the proposed suite. Background cleanup checks actual published references under the same lock order before removing abandoned staged bodies. Uploaded content expires; published resources and versions do not.

Creation/commit keys are 1–100 printable ASCII characters, scoped by Organization, Team, actor membership and operation (bundle_stage or bundle_commit). Replaying the exact successful body returns the original result; changing it returns 409 request_key_conflict. Nested slice/template operations have separate internal namespaces. Commit receipt replay requires current authority but does not need expired/erased upload bytes. A fresh upload of the same unchanged document/bindings creates no duplicate resources or versions. Receipts contain session ID plus resource identity hashes, kinds, IDs, revisions, versions and actions—no private criteria or case contents.

Upload and preview from a private-network client

Working directory: repository root. Use Node 24 and the declared pnpm version. Set MCM_TOKEN to a scoped management token; the example reaches the private listener on localhost. Replace the snapshot ID and model bindings with real permitted resources. The example stages and previews only; inspect the preview before committing.

export BUNDLE_FILE='platform/packages/control-plane/testdata/evaluation-bundle.synthetic.json'
export BUNDLE_BINDINGS='{"models":{"model:fixture-a":"model:support","model:fixture-b":"model:support"},"snapshots":{"subject":"snapshot_example"}}'
node --input-type=module <<'JS'
import fs from 'node:fs';
import crypto from 'node:crypto';
if (!process.env.MCM_TOKEN) throw new Error('Set MCM_TOKEN in this shell');
const base = 'http://127.0.0.1:4001/management/v1';
const teamId = 'team_support';
async function call(path, body, method = 'POST') {
  const response = await fetch(base + path, {method, headers: {
    authorization: `Bearer ${process.env.MCM_TOKEN}`,
    'content-type': 'application/json'
  }, body: JSON.stringify(body)});
  const value = await response.json();
  if (!response.ok) throw new Error(value.error?.code ?? `HTTP ${response.status}`);
  return value.data;
}
const bytes = fs.readFileSync(process.env.BUNDLE_FILE);
const bindings = JSON.parse(process.env.BUNDLE_BINDINGS);
const session = await call('/evaluation/bundles', {
  teamId, totalBytes: bytes.length,
  sha256: crypto.createHash('sha256').update(bytes).digest('hex'),
  requestKey: 'example-suite-upload-001'
});
for (let offset = 0, index = 0; offset < bytes.length; offset += 40960, index++) {
  await call(`/evaluation/bundles/${session.id}/chunks/${index}`, {
    teamId, contentBase64: bytes.subarray(offset, offset + 40960).toString('base64')
  }, 'PUT');
}
const preview = await call(`/evaluation/bundles/${session.id}/preview`, {teamId, bindings});
fs.writeFileSync('bundle-preview.json', JSON.stringify({sessionId: session.id, teamId, bindings, preview}, null, 2), {mode: 0o600});
console.log({sessionId: session.id, valid: preview.valid, objects: preview.entries.length, issues: preview.issues});
JS

Expected: valid:true and five entries after valid bindings; the fixture uses two synthetic cases and five objects. A missing snapshot returns a field-addressed issue and publishes nothing. Keep the upload key and exact file stable while recovering a response loss; choose a new key for an intentionally different file. To commit, POST the same teamId/bindings, the returned previewHash, a stable new commit requestKey, and explicit resolutions for changed entries. Keep the entire commit tuple unchanged for receipt recovery. The fixture contains synthetic data and establishes no production evaluation result.