Evaluation Gates
Technical reference. This page is for integrations, advanced configuration, or platform operations. For portal tasks, start with the user guide.
Evaluation gates are quality checkpoints that block release state transitions until required evaluation suites pass. They provide automated, evidence-based confidence before a release reaches human reviewers or goes live — ensuring that every behavioral change in a workload is validated against defined expectations.
What Are Evaluation Gates
An evaluation gate is a policy-enforced checkpoint in the release lifecycle. Before a release can advance to the next state, the gate checks whether all required evaluation suites have run and met their pass-rate thresholds. If any required suite is missing or below threshold, the transition is rejected with a structured error.
Default gate position: The gate fires before the review_ready → approved transition. Human reviewers only see releases that have already cleared automated evaluation.
Configurable gate position: The gate can be moved to the approved → active transition instead, so evaluation runs closer to the moment of activation.
Default (gate before approval):
draft --> review_ready --(eval gate)--> approved --> active
Alternative (gate before activation):
draft --> review_ready --> approved --(eval gate)--> activeWhen no evaluation configuration exists for a workload (no policy bundle and no workload-level
eval_config), the gate is open and transitions proceed without evaluation checks.
Evaluation Suites
An evaluation suite is a governed asset with asset_kind: "evaluation_suite". It defines the test cases and scoring rules used to measure a release.
Visibility and ownership
Evaluation selectors show suites owned by the active workspace and platform-owned global built-ins. A global built-in is shared only when the platform record has no workspace, no namespace, and the platform global-ownership marker; a tenant cannot make a suite global by omitting a workspace. Release evaluation, candidate testing, and Compare apply the same rule and pin the selected suite version before execution.
Global built-ins are read-only to tenant users. They may be selected for an evaluation or comparison, but they do not appear in mutation pickers such as Add to suite, which creates a new version. Add or edit cases only on a suite owned by your workspace.
The control plane revalidates the pinned suite and version before execution, gate decisions, evidence, and metrics. A stale or malformed reference is excluded rather than treated as global; when that reference was required, the gate remains blocked as if the required result were missing.
Suite Structure
| Field | Description |
|---|---|
asset_kind | Must be "evaluation_suite". |
slug | Human-readable identifier (e.g. "customer-support-accuracy"). |
test_cases | Ordered list of input/expected-output pairs. |
evaluator_flow_ref | Reserved for future custom scoring. Current executable match modes do not invoke it. |
default_threshold | Pass-rate floor (0.0–1.0) used when no other threshold is configured. |
Test Cases
Each test case specifies an input, an expected output object, and how to compare the actual output against the expected. A case can also link back to the governed asset it evaluates.
Evaluation suites are bounded at authoring and again before execution: a suite contains at most 256 cases, a regex case contains at most 32 expected fields, and a suite contains at most 256 regex fields in total. Regex patterns are limited to 512 characters and each expected value is a string of at most 512 characters. The runtime uses a timeout-capable engine with a 0.25-second per-match deadline and one 256-match/1-second cumulative budget for a complete candidate or comparison dispatch. Invalid, unsafe, oversized, timed-out, or over-budget regex cases fail closed; the platform never falls back to an unbounded regular-expression engine. Compare requests are also capped at 256 cohorts and reject a suite-pattern/cohort cross-product above that budget before creating or dispatching a run.
| Link Field | Description |
|---|---|
subject_kind | Optional linked asset kind: prompt_template, skill, or agent. |
subject_ref | Required when subject_kind is present. Identifies the linked asset. |
subject_version_ref | Optional version pin. Requires both subject_kind and subject_ref. |
| Match Mode | Description |
|---|---|
exact | Output must match the expected string exactly (case-sensitive). |
contains | Output must contain the expected string as a substring. |
regex | Output must match the expected regular expression. |
semantic | Reserved for compatibility. The control-plane runner rejects it until semantic scoring has a verifiable implementation. |
{
"asset_kind": "evaluation_suite",
"slug": "refund-policy-accuracy",
"test_cases": [
{
"id": "tc-001",
"input": { "user_message": "Can I return a product after 60 days?" },
"expected_output": { "text": "30-day return policy" },
"match_mode": "contains",
"subject_kind": "prompt_template",
"subject_ref": "refund-policy-prompt",
"subject_version_ref": "refund-policy-prompt@v3"
},
{
"id": "tc-002",
"input": { "user_message": "What is the refund window?" },
"expected_output": { "text": ".*30.*(day|days).*" },
"match_mode": "regex"
}
],
"default_threshold": 0.9
}The suite pass rate is the mean score across executable test cases. exact, contains, and regex produce deterministic binary scores. Do not author semantic cases yet; they validate for compatibility but fail closed at runtime.
Federated Configuration
Evaluation gate behavior is configured at two levels: the policy bundle (operator floor) and the workload (author overrides). The two levels are merged at gate-check time.
Policy Bundle Settings
A policy bundle can include an eval_policy block:
{
"eval_policy": {
"gate_position": "before_approval",
"min_pass_rate": 0.85,
"required_suite_kinds": ["accuracy", "safety"]
}
}| Field | Description |
|---|---|
gate_position | "before_approval" (default) or "before_activation". |
min_pass_rate | Minimum pass rate that applies to all suites unless overridden. |
required_suite_kinds | Suite kind tags that must be present in any evaluation run. |
Workload eval_config
A workload can override policy settings in its eval_config. Overrides can only be stricter — they cannot relax what the policy bundle requires:
{
"eval_config": {
"suites": [
{
"suite_slug": "refund-policy-accuracy",
"threshold": 0.95
},
{
"suite_slug": "tone-safety",
"threshold": 0.88
}
]
}
}Merge Rules
When both a policy bundle and a workload eval_config are present, the runtime applies the following merge rules:
| Setting | Merge Rule |
|---|---|
min_pass_rate | max(policy_threshold, workload_threshold) — the stricter value wins. |
| Required suites | Union of required_suite_kinds from policy and suite slugs from workload config. |
gate_position | Policy bundle sets position; workload cannot override gate position. |
This means a workload can require additional suites or raise thresholds, but cannot skip suites the policy mandates or lower a threshold below the policy floor.
Release Lifecycle with Evaluation Gate
The diagram below shows the default gate position (before approval) and where evaluation runs fit in the full lifecycle:
An evaluation result can be incomplete when a required test case is skipped by
a retryable release-pin conflict during a rollout window. Incomplete suites do
not count skipped cases in the pass-rate denominator, but they still fail the
release gate until the suite is re-run against a stable active release.
Recurring Quality Monitors
Recurring quality monitors are opt-in, disabled-by-default operator intents that periodically compare a pinned candidate release with a pinned baseline using an exact evaluation-suite version. They provide accountable quality review history; they do not replace Compare or the release evaluation gate. Eligible users configure and act on monitors through the Portal, CLI, or SDK. The AI Dev Kit exposes read-only monitor and review-episode inspection so an assistant can surface due work and evidence without enabling a loop or closing an episode on its own.
Keep these thresholds and verdicts separate:
| Signal | Meaning |
|---|---|
| Monitor floor | The pinned suite version’s default_threshold, combined with the monitor’s requested min_pass_rate using the stricter value. |
| Compare aggregate | Compare’s unchanged pass_threshold (default 1.0) for the raw comparison report. A monitor verdict never rewrites this status. |
| Case score | Each executable case is binary (1 or 0), not a configurable per-case cutoff. Unknown, partial, and error cases cannot be averaged into a pass. |
A complete monitor can pass its own floor while the raw Compare status remains failed. Display the monitor verdict and raw Compare status as distinct values; neither one should be presented as an execution-success or release-gate claim without the corresponding evidence.
Generalized Evaluation Sessions
Release evaluations are gate evidence for a concrete release. Generalized evaluation sessions are advisory comparison runs for asset candidates or asset versions before they become release bindings.
Use /v1/evaluation-sessions to create a session for asset_candidate or
asset_version subjects, add manual or imported candidates, and run those
candidates against an evaluation_suite. Asset-local and end-to-end session
outcomes return gate_eligible: false; they never publish a winning candidate,
create an asset version, update a release binding, or clear a release gate by
themselves. A human or governed workflow must still promote any selected
candidate through the normal asset-version and release lifecycle.
Assistant-generated candidates (source: "assistant_generated") and
Orqestra-managed LLM-as-judge runs (judge_mode: "llm_judge") are
Enterprise-gated. Community runtimes reject those paths with
enterprise_capability_required; deterministic manual/imported candidate runs
remain available.
Generated submissions are synchronous and all-or-nothing. Each generated spec
produces one candidate and carries a generation_brief; a request may contain
at most five generated specs, briefs are capped at 4,000 characters, and a
session may contain at most 50 candidates across all sources. Generated create
and append requests require a client-minted idempotency_key. Retain the exact
payload and key across a timeout, idempotency_request_in_progress, or
idempotency_outcome_unavailable; a completed retry returns the original status
and body without another dispatch. Rotate the key only after a definitive
outcome or when the payload changes.
curl -X POST "http://localhost:8000/v1/evaluation-sessions?workspace_id=$WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"subject_kind": "asset_candidate",
"subject_ref": "refund-policy-prompt",
"idempotency_key": "eval-candidate-7d973ee7",
"candidates": [{
"source": "assistant_generated",
"asset_kind": "prompt_template",
"generation_brief": "Draft a concise refund-policy prompt with a customer-name variable."
}]
}'Generated candidate summaries return their validated content and governed
provenance. Manual and imported summaries return content: null. Append
responses additionally return appended_candidates in request order while
preserving the bounded whole-session candidates snapshot.
curl -X POST "http://localhost:8000/v1/evaluation-sessions?workspace_id=$WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"subject_kind": "asset_candidate",
"subject_ref": "refund-policy-prompt",
"candidates": [
{
"source": "manual",
"asset_kind": "prompt_template",
"content": { "template": "Answer using this refund policy: {{policy}}" }
}
]
}'curl -X POST "http://localhost:8000/v1/evaluation-sessions/{session_id}/runs?workspace_id=$WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"candidate_id": "candidate-uuid",
"suite_slug": "refund-policy-accuracy",
"execution_mode": "asset_local",
"judge_mode": "deterministic"
}'Generating candidates in the portal
The asset builder’s Test mode (Evaluation Workbench) offers a Generate
candidate control alongside the existing manual and import-version
candidate flows. It is visible whenever the evaluation-assistant-candidates
capability is off — disabled with the capability’s reason — so operators on a
Community edition always see the feature and why it is unavailable, rather
than a missing button.
When the capability is enabled, clicking Generate candidate opens an
inline brief intake: a single free-text description of the desired
prompt/skill/agent, submitted as one assistant_generated draft per click.
The button’s enabled state, the served asset kinds, the per-request draft
cap, and the brief character limit are all read from the capability’s own
generation_limits — the portal never hardcodes a client-side kind list or
count. Selecting a kind the capability does not serve, or reaching the
per-request cap, disables the button with a tooltip naming the served set or
the limit.
Clicking Run evaluation submits every draft — manual, imported, and
generated — in one POST /v1/evaluation-sessions request. A generated batch
mints a client-side idempotency key and retains the exact request body for
recovery. The server’s returned content for each generated candidate folds
back into the corresponding draft (a positional match against the request’s
candidate order), and a candidate whose server-reported candidate_kind is
assistant_generated renders a small badge in the candidate table and its
result card.
The workbench is deliberately one-shot: it never auto-retries a submission.
A 409 idempotency_request_in_progress or 503 idempotency_outcome_unavailable response surfaces a recovery banner with a
Retry-After-driven countdown (falling back to 5 seconds without a usable
header); the Run evaluation button, the brief intake, and per-candidate
removal are locked until the countdown clears. Only an explicit click on
Retry resends the exact retained key and payload. Editing the candidate
list after the countdown clears — adding, removing, or describing another
candidate — abandons the recovery episode and mints a fresh idempotency key
for the next submission instead. A 422 generation_needs_clarification
response surfaces the server’s clarification question and reopens the brief
intake without storing a partial candidate, so the operator can resubmit a
more specific brief.
Triggering Evaluations
API
curl -X POST http://localhost:8000/v1/releases/{release_id}/evaluate \
-H "Content-Type: application/json" \
-d '{}'To run specific suites only:
curl -X POST http://localhost:8000/v1/releases/{release_id}/evaluate \
-H "Content-Type: application/json" \
-d '{
"suite_slugs": ["refund-policy-accuracy", "tone-safety"]
}'Response (202 Accepted):
{
"evaluation_run_id": "evr-0000-0000-0000-000000000001",
"release_id": "r1a2b3c4-0000-0000-0000-000000000001",
"status": "pending",
"suite_slugs": ["refund-policy-accuracy", "tone-safety"],
"created_at": "2026-04-11T09:00:00Z"
}EvaluationResult Status Transitions
Each triggered evaluation run creates one or more EvaluationResult records, one per suite:
pending --> running --> passed
--> failed
--> error| Status | Meaning |
|---|---|
pending | Queued, not yet started. |
running | Executing test cases. |
passed | All test cases met the configured threshold. |
failed | One or more test cases fell below threshold. |
error | Evaluation could not complete (infrastructure or flow error). |
Viewing Results
Portal
On the release detail page, select the Evaluations tab to see:
- A gate status banner indicating whether the gate is currently
passed,failed, orpending(evaluation not yet run or still running). - A Run Evaluations button that triggers a new evaluation run against all configured suites.
- A results table listing each suite with its pass rate, threshold, and overall status.
- Expandable rows per suite showing per-test-case detail: the input, expected output, actual output, match mode, and individual score.
API — List Evaluation Results
curl http://localhost:8000/v1/releases/{release_id}/evaluationsResponse (200 OK):
[
{
"evaluation_result_id": "evr-0000-0000-0000-000000000001",
"release_id": "r1a2b3c4-0000-0000-0000-000000000001",
"suite_slug": "refund-policy-accuracy",
"status": "passed",
"pass_rate": 0.97,
"threshold": 0.95,
"test_case_count": 30,
"test_case_results": [...],
"created_at": "2026-04-11T09:00:00Z",
"completed_at": "2026-04-11T09:00:45Z"
}
]API — Get a Single Evaluation Result
curl http://localhost:8000/v1/releases/{release_id}/evaluations/{evaluation_result_id}Gate Enforcement
When a transition is attempted and the gate is not satisfied, the API returns HTTP 422:
{
"error": "evaluation_gate_failed",
"message": "Release cannot transition: evaluation gate not cleared.",
"details": {
"gate_position": "before_approval",
"missing_suites": ["tone-safety"],
"failing_suites": [
{
"suite_slug": "refund-policy-accuracy",
"pass_rate": 0.81,
"threshold": 0.95,
"status": "failed"
}
]
}
}| Field | Description |
|---|---|
missing_suites | Suites that are required but have no completed evaluation result for this release. |
failing_suites | Suites that ran but did not meet their pass-rate threshold. |
To clear the gate:
- Fix the issue in the underlying flow, prompt template, or model configuration.
- Publish a new asset version and update the release binding.
- Trigger a new evaluation run (
POST /v1/releases/{id}/evaluate). - Wait for results to reach
passedstatus. - Retry the transition.
Re-running evaluations on the same release bindings (without changing anything) will produce a new result record but is unlikely to change outcomes unless there is an upstream non-determinism you want to average out.
SDK Reference
Python SDK
from orqestra_sdk import OrqestraClient
async with OrqestraClient("http://localhost:8000") as client:
# Trigger evaluation (all configured suites)
run = await client.releases.evaluate(release_id="r1a2b3c4-...")
# Trigger evaluation (specific suites)
run = await client.releases.evaluate(
release_id="r1a2b3c4-...",
suite_slugs=["refund-policy-accuracy"],
)
# List all evaluation results for a release
results = await client.releases.get_evaluations(release_id="r1a2b3c4-...")
# Get a specific evaluation result
result = await client.releases.get_evaluation(
release_id="r1a2b3c4-...",
evaluation_result_id="evr-0000-...",
)
print(result.status) # "passed" | "failed" | "error" | "pending" | "running"
print(result.pass_rate) # 0.97CLI Reference
Trigger evaluations:
orqestractl release evaluate <release-id>
# Run specific suites only
orqestractl release evaluate <release-id> --suite refund-policy-accuracy --suite tone-safetyList evaluation results:
orqestractl release evaluations <release-id>Get a specific result:
orqestractl release evaluation <release-id> evr-0000-...Create and run an advisory asset-candidate evaluation session:
orqestractl evaluation create \
--subject-kind asset_candidate \
--subject-ref refund-policy-prompt \
--candidate-json '{"source":"manual","asset_kind":"prompt_template","content":{"template":"Answer with {{policy}}"}}'
orqestractl evaluation sessions --subject-ref refund-policy-prompt
orqestractl evaluation run <session-id> \
--candidate-id <candidate-id> \
--suite refund-policy-accuracyorqestractl evaluation is advisory session tooling. It does not activate,
publish, bind, or approve release assets. Declarative sync currently keeps
evaluation sessions out of manifests; sync continues to rely on explicit
release lifecycle commands and the control-plane release gate.