Promotion gates
Evaluation measures six immutable gate facts. The registry applies your project's promotion policy to those facts when you create a serving bundle. Keeping those jobs separate matters: changing a policy never changes an eval report, its fingerprint, or what the model actually measured.
The platform checks every model's quality before it can be promoted. For a sequence model that includes the platform's internal quality safeguards: gate G1 compares every gating label with an internal reference model scored on the same test targets.
Report fact vs promotion verdict
Two fields answer different questions:
gates_passedis the eval-report fact. It isfalsewhen any mandatory gate failed, and policy or acknowledgement never changes it.promotionis the registry's current, server-resolved decision. Itsverdictispromotable,needs_acknowledgement, orblocked, with the affected gate ids split intoadvisoryandblockinglists.
Read promotion from model detail or list responses when deciding what the
next bundle request may do. Do not derive it from gates_passed, the catalog,
or a client-side copy of policy rules. A successful acknowledgement belongs to
the immutable bundle it created; it does not rewrite the report fact. A later
bundle request therefore evaluates the latest report and policy again.
The full machine-readable catalog is available from
GET /gate-definitions.
Use promotion guidance in the console
The Models list shows the registry decision without fetching an eval report for every row:
- Ready to promote means the current server verdict is
promotable. - N warnings means the verdict is
needs_acknowledgement; the number is the server's advisory-gate count. - Blocked means at least one failure is blocking under the active policy, or the decision could not be safely recognized.
The Models list has a labelled Gate results filter with three options:
All (the default), No failed gates and Has failed gates. The
filter works on the stored evaluation report, not on your promotion policy, so
it is not the same as the row chips: Has failed gates includes models with
warnings and blocked models, and No failed gates omits models with
acknowledgeable warnings even when policy would allow a reasoned promotion.
Neither option hides promoted models. The selection is kept in the page
address (?gates=), so a filtered view can be linked. A one-line summary
above the table counts the loaded models by ready, warnings and blocked. A
model that is promoted, archived or not yet evaluated is counted on its own, so
the counts add up to the total. The line says "on this page" whenever the list
spans more than one page. When no model matches a filter, the empty state
offers "Show all models".
On model detail, each gate card leads with its plain-language name. The G-code is secondary metadata. The card keeps the immutable report evidence together: what the check measures, why it matters, what to do next, observed values, the required threshold, and the report's diagnostics. The console does not show the comparison with the internal quality safeguards: G1 says whether each label passed and what to do next, without the safeguards' estimates. The report still records that comparison for the API.
For a quality warning, choose Acknowledge & create bundle, review every
warning, select each gate, and enter a non-empty decision reason. Confirm stays
disabled until the acknowledgement exactly covers the server's current
advisory set. The created bundle shows the recorded gate name, actor, reason,
policy mode, and gate.override provenance. If evaluation or policy changes
while the modal is open, the console clears the old review and asks you to
review the refreshed warning set instead of retrying it blindly.
A blocking card has no acknowledgement control. Correct the integrity issue, or, for a quality gate made blocking by policy, ask an owner or admin to review the policy. The server remains the enforcement boundary in both cases.
Each gate card states the condition of its report result:
- Passed — no corrective action is needed for that gate.
- Failed — the card shows the catalog's next step and the report's diagnostics.
- Waived for an existing bundle — a bundle that pins this exact report recorded an acknowledgement. The card names the bundle, actor, policy and reason. The measured failure is unchanged, and a new bundle needs its own acknowledgement. If the current policy now makes that gate blocking, the card says so instead: another bundle cannot acknowledge it.
- Not applicable — the gate had nothing to check and does not block promotion.
- Not evaluated — the report has no recognized result for the gate. It counts as failed, and bundle creation refuses the report.
Once a model has a bundle, the decision panel is titled Requirements for a new bundle and sits beside the bundle already created, so a recorded acknowledgement is never read as a pending requirement. The Models list names the gates behind a warning or block, and the gate-failure history names each failed gate with its recorded value and threshold and links to that gate's section of the model.
Gate catalog
| Gate | Name | Category | Default severity | Promotion meaning |
|---|---|---|---|---|
| G1 | Label quality vs baselines | quality | advisory | A measured quality regression may be explicitly accepted. |
| G2 | Score calibration | quality | advisory | A known calibration tradeoff may be explicitly accepted. |
| G3 | Count invariance | quality | advisory | A known representation tradeoff may be explicitly accepted. |
| G4 | Train/serve tokenizer parity | integrity | blocking | Train/serve skew invalidates the reported metrics for the deployed artifact. |
| G5 | Label-leakage audit | integrity | blocking | Leakage makes reported quality fictitious, not merely risky. |
| G6 | Drift baseline | integrity | blocking | A missing or malformed drift reference is a pipeline defect. |
G1: Label quality vs baselines
For each gating label, G1 makes two checks, and the label passes only when both do. G1 is advisory: a failure is shown with its evidence, and you choose whether to improve the model or promote it with an acknowledgement and a reason.
- Quality (the release minimum). The label must beat the release
minimum: the previous release's value minus
0.005, or, on a first release, the no-skill reference (chance level for a binary AUC). - Internal reference check. The model must hold its own against an
internal reference model, scored on the same held-out customers, on the
label's primary metric (AUC, macro-F1 or R²). The label passes when the
model is within
0.01of the reference (delta >= -0.01, where delta is the model minus the reference) and the lower bound of the paired 95% interval of the difference is at least-0.03. Your own uploaded comparators are shown for context and never count as the reference.
The report records, per label, a competitiveness entry with the reference's
estimate, the delta and its interval, both margins and the result, and a
single typed reason when the label fails:
| Reason | Meaning | What to do |
|---|---|---|
insufficient_support | Fewer than 50 test targets, or a 95% interval wider than 0.30. | Collect more labeled outcomes or use a longer window. |
baseline_unavailable | The internal reference check could not run for the label, so the model was not compared. | Evaluate on data where the internal reference check can run, or acknowledge G1 with a reason. |
not_competitive | The model scored more than 0.01 below the internal reference model on the same customers, or it is within 0.01 but the paired interval reaches below -0.03. The second case is inconclusive: the test split is too small to establish competitiveness. | Train again with a study, or fix the data. When only the interval failed, evaluate on a larger test split or a longer window. To promote the model as it is, acknowledge G1 with a reason that records the trade-off. |
The interval check is a non-inferiority test: with few test targets the
interval widens and the label can fail even when its point estimate is close.
The gate-failure history in the API (GET /monitors/gate-failures) records a
not_competitive label against the bar it
missed: its estimate against the reference's estimate minus 0.01, or, when only
the interval failed, the interval's lower bound (delta_ci95_lower) against
-0.03. When the paired interval is narrow (a half-width of about 0.02 or less), a label within 0.01 also clears the interval check; the interval decides only for small or noisy test splits.
Models evaluated before the competitiveness check. A sequence model whose
evaluation report has no competitiveness record shows a G1 finding with the
reason evaluated_before_competitiveness_check: the model was never compared
with the internal reference model. Under the default policy nothing is blocked. Acknowledge the finding with a
reason to promote the model now, or let the release re-evaluation fill in the
comparison: wherever the platform owner has enabled the re-evaluation job, the release that
introduces the check re-evaluates earlier models automatically, promoted models
included. A re-evaluation can publish a
gate.failed webhook for a model that has been serving for a long time; its
bundle, slot and serving are never changed. Only a project's own strict
policy or a G1 override makes the finding blocking. Event models and entity
baseline models are not affected.
The model page's quality scoreboard offers Re-evaluate for such a model. It re-runs a signals evaluation of record with its own options and seed; any other record (the default evaluation) is re-run with seed 0 and prediction evidence turned on, which the button says. It is not offered for an observation-time ablation record, which only that ablation can renew.
The console names the reference that controlled each label. The previous release's value comes from the newest promoted bundle for the same entity type that did not acknowledge a G1 failure. Without one, the release minimum is the no-skill reference (the trivial predictor's score), and the console's detailed G1 table reads "No previous release". The competitiveness check reads the internal reference check of the same report. The console shows it as "Internal check" with its state only: met, not met (with one sentence, "An internal reference model scored higher than this model by more than the declared margin."), not settled, or could not run, and what to do next. It never shows the reference's name, id or estimate, or any number derived from them such as the difference or its interval; the acknowledgement dialog explains a G1 finding in words (the label did not meet the quality bar, or the bar could not be checked), so you can still make an informed choice. The report and the API keep every number.
Inspect the per-label estimates and confidence intervals before acknowledging a G1 failure. Acknowledging G1 does not lower the bar for the next model. See The G1 anti-ratchet.
G2: Score calibration
G2 measures expected calibration error for every gating classification label.
The threshold is 0.05: a predicted probability such as 0.8 should describe
an event that occurs about 80 percent of the time. Review the worst label's
reliability diagnostics, then recalibrate, retrain, or explicitly accept the
tradeoff.
A head that barely moved from a constant prediction can still pass ECE, so
each classification label also reports log_loss_gain: the log loss of a
constant predictor (the test positive rate, or the class frequencies) minus
the log loss of the served scores, in nats per target. A gain near zero means
the head adds little over the base rate. The gain is informational and never
changes the gate's result.
G3: Count invariance
G3 probes whether embeddings represent behavioral content or merely activity
volume. When the sampled entities have different event counts, the ridge probe
must have R-squared below 0.30, and the exact-count share among 10 nearest
neighbors must be below 0.55.
G3 is advisory by design. A representation that encodes event volume or event velocity, such as one built from event timing, is expected to exceed this probe's limits and is not a defect: a model-free matrix of simple event statistics already exceeds them. Every G3 result carries this note, and the model page shows it. To promote such a model, acknowledge G3 with a reason that records the representation choice. Nothing in training or model selection optimizes for G3.
Equal-count sample: when all sampled entities have the same event count,
the report records degenerate_single_count=true. The evaluator defines
R-squared as zero and compares the neighbor share with the count-shuffled
baseline: nn_share <= nn_share_shuffled + 1e-9. Both shares are one for an
equal-count sample, so this diagnostic can pass. The usual 0.55 neighbor-share
threshold remains in the report's metrics but does not apply to this case.
An equal-count sample cannot establish invariance across different event counts. The console and downloaded model card retain the report's decision and explain this limitation; a passing badge does not remove the caveat. Inspect pooling, sampling, and the count-correlation diagnostics before accepting a failure.
G4: Train/serve tokenizer parity
G4 sends held-out entities through the dataset-builder and scoring paths.
Tokens must be byte-identical and the maximum embedding delta must be at most
1e-4. A failure means the artifact evaluated is not the artifact that would
serve, so it cannot be acknowledged. Fix the tokenizer, collation, encoder,
dtype, or artifact pins and evaluate again.
G5: Label-leakage audit
G5 independently recomputes supervision masks and requires zero post-snapshot or unavailable-target violations. A failure invalidates the quality result and cannot be acknowledged. Repair timestamps, maturation rules, joins, or masks; then rebuild the dataset and evaluate again.
G6: Drift baseline
G6 requires a complete, schema-valid reference distribution for serving drift monitoring. It fails only when the evaluation artifact pipeline is incomplete or broken. Repair the pipeline and evaluate again; there is no acknowledgement path for this gate.
Project policy
Every project has one resolved promotion policy:
warnis the default when no policy has been stored. G1-G3 are advisory: a failure is shown with its evidence, and you choose whether to promote with an acknowledgement and a reason. G4-G6 are blocking.
The two other settings are optional project rules that an org admin chooses; they are never a default, and the console names the rule wherever it applies:
strictmakes every failed mandatory gate blocking and disables gate acknowledgement.gate_overridescan raise G1, G2, or G3 from advisory to blocking. Overrides can never lower severity, and G4-G6 can never become advisory.
Project viewers can read the policy. An org admin or admin-scoped API key can
replace it with PUT /promotion-policy.
For example, this keeps warning mode but makes calibration failures blocking:
{
"mode": "warn",
"gate_overrides": {
"G2": "blocking"
}
}
Every successful policy replacement records one transactional
promotion_policy.update audit event with the before and after values.
In the console, open Settings → Promotion policy. The page starts with the
saved policy: the mode, the quality checks raised to blocking, and when and by
whom it was last saved. Owners and admins can switch between warn and
strict and can raise G1-G3 individually to blocking in the Gate
behaviour list, where each check shows what happens when it fails. Under
strict every quality check blocks; the individual raises stay saved and
apply again under warn. G4-G6 appear locked with their catalog
justification and are never sent in the update body. Members and viewers see
the same active policy in a guided read-only view that points to the Members
page for the required role.
Acknowledge an advisory failure
Under warn, a model whose only failures are advisory has promotion verdict
needs_acknowledgement. A bundle request must include the exact set of
currently failing advisory gates and a non-empty reason:
{
"acknowledge_gates": {
"gates": ["G2"],
"reason": "Known calibration tradeoff; ranking lift is required for this trial."
}
}
The registry re-reads the model's latest verified eval report inside the
request. It rejects a stale set, a partial set, extra or duplicate gates, a
passing or unknown gate, a blank reason, and every attempt to acknowledge a
blocking gate. Under strict, any otherwise well-formed acknowledgement is
refused with reason policy_strict.
On success, the bundle still carries the eval report's G1-G6 block verbatim.
Its additional promotion block records the policy mode, reason, actor, and
acknowledged gate facts. The same database transaction records one
gate.override audit event per acknowledged gate and one bundle.create
event. An idempotent request replay does not duplicate either the audit events
or the bundle.created webhook.
If advisory gates fail and no acknowledgement is supplied, bundle creation is
refused with acknowledgement_required. Any blocking failure refuses creation
regardless of the rest of the request.
Acknowledge at a shared-checkpoint import
Importing a shared checkpoint (POST /models:import-shared) creates the local
model's bundle, so the same rules apply. Its body takes the same optional
acknowledge_gates member:
{
"gates": ["G1", "G3"],
"reason": "Reviewed the local evaluation; the lift is enough for this source."
}
The import preview (POST /models:preview-shared-import) never grants an
acknowledgement. When the only reasons it lists are acknowledgement_required,
it stays ready: false and names each failing advisory gate. That state can be
imported with an acknowledgement that covers exactly those gates. With
expected_preview_fingerprint, stale_preview is checked after request
validation and before the acknowledgement's cover, so a changed preview is
refused first. A missing, extra or duplicate gate or a blank reason is refused
before anything changes. On success the bundle carries the promotion block,
and each acknowledged gate records gate.override, exactly as above.
Repeating an import whose bundle already exists returns that bundle and records
no second gate.override or bundle.create. Without
expected_preview_fingerprint, the repeat does not check its acknowledgement
again; with it, the acknowledgement must still cover the current preview.
An acknowledgement never imports a blocking gate: G4-G6, and G1-G3 when the
project policy makes them blocking (strict, or a raise-only override; the
refusal names that rule). Nor does it import a malformed gate result or a
binding, provenance or evaluation reason such as local_artifact_mismatch or
evaluation_mismatch. Automatic preparation never acknowledges for you: a
candidate with failed advisory gates waits for an explicit import.
Integrity failures are never waivable
G4-G6 protect the validity of the artifact and its measurement, rather than a business judgment about model quality. No role, policy, request field, or acknowledgement can make them advisory. A strict policy or a raise-only override can also treat a quality failure as blocking.
Gate acknowledgement is separate from the fingerprint verification performed when a bundle is activated on a slot. A promotion acknowledgement never bypasses a missing or altered artifact, and it is not an activation fingerprint override.
The G1 anti-ratchet
An acknowledged G1 regression may be served, but it never becomes the quality baseline the next model must beat. Baseline lookup walks promoted bundles newest first and selects the latest bundle that did not acknowledge G1. For example, after clean model A and acknowledged-G1 model B are promoted, model C is compared with A.
Only an acknowledged quality regression is excluded. A bundle stays the baseline when its acknowledged G1 accepted no regression: every failing label still beat the previous estimate minus 0.005 (it failed only the support floor or the competitiveness check against the internal reference model), or the model was evaluated before the competitiveness check and its recorded G1 passed. So acknowledging G1 never lowers the bar relative to the reference the model was evaluated against. A model evaluated earlier, against an older reference, and bundled later still becomes the reference.
The registry still verifies every pinned input on a newer candidate before it can exclude that candidate for policy reasons. A broken bundle is never silently skipped in favor of an older, weaker baseline. This rule excludes acknowledged G1 regressions; it does not pin the baseline to the best model ever observed.
Field descriptions and their ablation evidence
A model can read offline field descriptions through the description channel:
it trains on a dataset built with tokenizer_format: descriptions.v1 (or
semantic.v2) and a pinned description registry, with the matching
overrides.tokenizer_format on its run. The channel is off by default and
optional per project. For a model with a gating label, a study shows whether
the channel hurts; when there is no study, or it does not pass, bundling shows
you that as a bundle finding and you decide (in strict
mode, an admin's choice, you cannot acknowledge it).
- Build the same dataset twice from one request on the same snapshot: once
without the channel (the off build) and once with
tokenizer_formatanddescription_registryadded (the on build). Forsemantic.v2, also addsemantic_inputs. Everything else must match: snapshot, contract, labels, window and split. - Train runs on each build with the same tier, overrides and trainer
settings, one per seed on each side. Only the on runs set
overrides.tokenizer_format. Use several seeds (three or more for a recommendation). - When the runs are evaluated, record the study with
POST /models/{model_id}/description-ablations, naming any on model. You do not name seeds or pairs: the server finds the off build and every run of both builds, failed ones included, and refuses any difference beyond the channel, naming the keys that differ.
Each seed of the study is one row. The earliest run of a seed that succeeded
and was evaluated counts; a later run of the same seed counts only when every
earlier one failed. A seed whose run failed, or whose model was never
evaluated, stays in the study as a failed row. While a run or an evaluation
that could still count is in progress, recording is refused with
ablation_runs_pending; try again when it finishes.
The evidence reports two verdicts:
gate_passis the study's verdict. Every row is complete, every pair and every gating label satisfieson >= off - 0.002, and every on run has G1passand G2–G6passornot_applicable. A study that does not pass is the bundle findingdesc_channel_study_failed.recommend_enableis advice. It also needs at least three pairs and a positive mean improvement on every gating label. It never blocks.
When to turn the channel on
The recommendation and the cost budget below are guidance for turning the
channel on for a project; gate_pass is shown to you at bundle time as a
finding you acknowledge. Turn the channel on
only when the project's study reports recommend_enable and the channel's
measured cost on the project stays within these limits, each the cost with
descriptions divided by the cost without them:
| Measure | Limit |
|---|---|
| Training time per step | 2.0× |
| Realtime scoring p95 per micro-batch | 1.25× |
| Batch scoring wall time | 1.5× |
| Dataset bytes | 4× |
| Realtime and batch scoring memory | 1.1× |
The overhead benchmark (tools/describe/bench_overhead.py) measures these
ratios. For now, a registry with more than about 50 distinct descriptions is
expected to exceed the dataset-bytes and realtime limits.
POST /models/{model_id}/bundles then checks the model:
- Its own G1–G6 are handled like any other model's. A failing quality gate
(G1–G3 under
warn, unless your project policy raises it) is shown and can be acknowledged; G4–G6 always block. For example, a representation that carries event counts or timing is expected to fail G3, and you can acknowledge it. - A model trained without any gating label, such as a self-supervised segmentation model or a projection that is never used for decisions, needs nothing more: no study is required.
- For a model with a gating label, the registry works out the current result
of every study of its recipe from the runs, builds and reports
themselves. The recipe is the contract, leakage policy, description
registry (and semantic tokenization), model config, run labels and
training settings. What it finds is shown to you as a
bundle finding, and you decide: no study, a study that
does not pass, or a study that cannot be resolved. A study you have not
recorded yet no longer stops the bundle; every study's fingerprint, builds
and verdict are pinned in the
bundle.createaudit event.
A later retrain of the same recipe on a new dataset build is covered by the recipe's studies. A changed recipe, including other training settings or another compute class, needs its own study. A recorded study is a snapshot: when a study gains runs, record it again; earlier snapshots stay as history.
Bundle findings
A bundle finding is evidence you decide on: the request is refused once with
everything still to acknowledge, and the bundle is created when you send the
finding codes in acknowledge_findings with a reason. The refusal names every
advisory gate and finding at once, so you never discover them one at a time:
{
"acknowledge_findings": {
"findings": [
{
"finding": "desc_channel_study_failed",
"fingerprint": "3f1c0a9e5b7d2c4f6a8e0b1d3c5f7a9e2b4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a"
}
],
"reason": "The regression is within what this use case tolerates."
}
}
Each finding appears in the refusal as {"type": "bundle_finding", "finding": <code>, "fingerprint": <digest>, "facts": {...}}, after the reasons the same
condition used to carry and before acknowledgement_required. A finding's
legacy reasons (for example ablation_off_build_ambiguous or
ablation_resolution_over_cap for an unresolved study) come before its
bundle_finding detail, so read the finding from that detail. Acknowledge
each finding by echoing its code and the bundle_finding detail's
fingerprint (a legacy desc_channel_ablation_failed detail carries the
study's own fingerprint, which is not the one to echo): if the facts changed since
you saw them (another failing study, for example), the request is refused with
finding_facts_changed, the new fingerprint and the current facts, so you
never acknowledge facts you have not seen. On success the registry records one
bundle.finding_acknowledged audit event per finding, with the fingerprint
and facts you saw, your reason and your identity; the manifest does not change.
The codes must exactly cover the findings found now (finding_exact_cover
lists both sets). A blocking gate refuses first, with the findings listed as
information. Under strict mode, an admin's choice, no finding can be
acknowledged: the refusal names policy_strict instead of
acknowledgement_required. The promotion verdict
of a model covers the gate catalog only: findings appear in the bundle
refusal and in the readiness read below.
Check readiness before you bundle
GET /models/{model_id}/bundle-readiness is a
read-only dry run of the bundle request: it runs the same checks, including
the ones made while the bundle is assembled when no gate blocks (the model
card's sources, a shared import's pins, the semantic representation; only the
shared manifest's content validation and the manifest's own invariants are left
to the bundle request). It writes nothing and needs
only read access. It returns the verdict (ready, needs_acknowledgement or
blocked), every failing gate with its severity, metrics and diagnostics (and,
for a gate your project's policy raised, policy_strict or
policy_override), every bundle finding with the same facts and fingerprint a
refusal carries, any hard refusal (with retryable for runs still in
progress; never when a gate blocks, since a blocking gate does not resolve by
itself), and the exact acknowledge_gates and acknowledge_findings (codes
with their fingerprints) to send. ready assumes the request members it
evaluated: send observation_time.enabled: true for a model that consumes the
elapsed-silence token. Each read resolves the description-channel studies and,
like a bundle request, re-reads the model card's sources (a shared import's
checkpoint weights in full) or a semantic model's semantic inputs, so avoid
polling it. A storage outage answers 503 unavailable with Retry-After.
Pass observation_time or signals to evaluate those request members;
observation_time defaults to true for a model that consumes the
elapsed-silence token. Before a hazard bundle
(POST /models/{model_id}/bundles:hazard with hazard.enabled: true), pass
hazard=true: the answer then also lists the hazard-ablation findings below
with the fingerprints that request acknowledges, or the hazard refusal. The
answer is resolved when you ask: the bundle request checks everything again.
curl -s "$TESSERA_API_BASE/models/$MODEL_ID/bundle-readiness" \
-H "authorization: Bearer $TESSERA_READ_KEY"
| Code or reason | Kind | Meaning |
|---|---|---|
observation_time_evidence_missing (was observation_time_ablation_missing) | Finding | No observation ablation for this dataset and run. See observation time. |
observation_time_not_recommended (was observation_time_ablation_not_recommended) | Finding | The observation ablation does not recommend enablement; the facts show the arms and the failing labels. |
desc_channel_study_missing (was desc_channel_ablation_missing) | Finding | The recipe has no study. To get one, build the v1 twin of the on build on the same snapshot and train its runs. |
desc_channel_study_failed (was desc_channel_ablation_failed) | Finding | Studies of the recipe do not pass (a failed or unevaluated seed, a regression beyond -0.002, or a failing on-run gate). The facts list each study's rows with their status, reason, off and on runs, per-label deltas and on-run gates. |
desc_channel_study_unresolved | Finding | A study cannot be resolved: several v1 twin builds of one on build (ablation_off_build_ambiguous), more than 16 seeds, or a bound of the resolution (ablation_resolution_over_cap). The other studies of the recipe are still resolved; a bound on the whole recipe resolves none. The facts carry each typed reason with its on build and, for one run group, its run_group digest: at most 16 entries, with the total unresolved count and a resolution_fingerprint that binds every entry. |
hazard_ablation_missing | Finding | Hazard bundles (POST /models/{model_id}/bundles:hazard) only: no hazard-ablation evaluation graded the model's current eval report and calibration. Declare a hazard-ablation protocol and run its evaluation, or acknowledge. |
hazard_ablation_failed | Finding | Hazard bundles only: a current hazard-ablation evaluation fails. The facts list each failing evaluation and its protocol. |
hazard_ablation_incomplete | Finding | Hazard bundles only: a current hazard-ablation evaluation is incomplete. The facts list each one. |
hazard_ablation_unverifiable | Refusal | Hazard bundles only: a hazard-ablation report fails re-verification or does not bind to its protocol, model and config, or its protocol lacks the full arm and metric set or a declared seed (only pre-ruling or tampered content can). It cannot be acknowledged. Repair or re-run the evaluation. |
desc_channel_ablation_stale | No longer returned | An unrecorded study is pinned in the bundle.create audit event instead. |
ablation_runs_pending | Refusal (retryable) | A run or evaluation of a study is still in progress; the details name the run, seed and model. Retry when it finishes. |
desc_channel_ablation_invalid | Refusal | A recorded study failed its integrity check. Record the study again. |
desc_channel_run_missing | Refusal | The model has no readable training run to tie evidence to. |
ablation_pairs_invalid (malformed rows, no seeds key) | Refusal | A study's rows are malformed. With a seeds key it is the more-than-16-seeds case: a legacy reason of desc_channel_study_unresolved. |
ablation_run_mismatch, ablation_recipe_mismatch, ablation_dataset_mismatch, ablation_label_mismatch, ablation_format_mismatch | Refusal | A study's runs or builds differ beyond the description channel; the details name the keys. Rebuild or retrain the off arm to match. |
ablation_arm_not_ready, ablation_evidence_invalid | Refusal | A counted run or its evaluation is not in the state the study needs. Contact support. |
ablation_artifact_mismatch, ablation_artifact_missing, ablation_dataset_unverifiable, ablation_arm_inconsistent, desc_channel_recipe_invalid | Refusal | A run, build or report of a study no longer matches what was recorded. The details name the run, model or build; contact support. |
observation_time_dataset_disabled, observation_time_dataset_invalid | Refusal | The model's dataset is not a valid observation-time build. Rebuild it with observation time and retrain. |
observation_time_model_disabled | Refusal | The model was not trained to consume the elapsed-silence token. |
observation_time_run_missing | Refusal | The model's training run is not readable, so no evidence can be tied to it. |
observation_time_enablement_required | Refusal | A model that consumes the token is bundled only with observation_time.enabled: true. |
Recording a study can also be refused: ablation_off_build_missing (the
details carry the twin request to build, and any near miss with the keys that
differ), ablation_off_runs_missing (the twin build has no run with the twin
settings), ablation_pairs_invalid (more than 16 seeds), and the reasons
above.
GET /models/{model_id}/description-ablations lists the recorded studies of
the model's recipe, marked direct for a model the study counted and
carry_over otherwise. Shared-checkpoint imports are exempt: they are
validated by their platform release. v1 models are unchanged. Existing
bundles keep serving, because the check runs only when a bundle is created.
No bundle created before this check pins a description registry or a
non-legacy tokenizer format, so none needed an exemption: existing bundles
keep the legacy format, and the check applies to bundles created from now on.