The calibration lifecycle
The audited life of a qcal_ artifact — the four one-way states, the per-segment activation gate, the refit scheduler's five triggers, the recalibration log, and read-time invalidation.
Last updated
On this page
A fit produces a qcal_ artifact, but fitting is only the first event in its life. The artifact
moves through a small, one-way lifecycle; exactly one of its states can license a claim;
promotion is gated; a background scheduler keeps it fresh; and every move — including every
refusal — is written to an append-only log. This page is the lifecycle end to end.
The four states
An artifact is content-addressed: its id is qcal_<compactTS>_<sha8> where the hash is taken
over the fit’s own content, so a refit over the same window lands on the same id and a
different fit is always a new id. The store keeps one <id>.json per artifact plus a
manifest.json naming the active and shadow slots and every versions[]. Each artifact
carries a status, and the status moves one way:
candidate → shadow → active → retired
| State | What it is | Can it serve a claim? |
|---|---|---|
candidate |
A fresh fit, inert. Writing one changes nothing else, which is what makes quality calibrate safe to run on a schedule. |
No |
shadow |
Fitted and evaluated, but never served — the slot the scheduler populates so a fresh fit can be watched against production. | No |
active |
The one status that can license a probability claim. Activating an artifact retires the incumbent atomically. | Yes |
retired |
Terminal. A successor is a new content-addressed id, never the same bytes wearing a fresh status. | No |
Forward skips are legal — candidate → active is the ordinary operator action, and
candidate → retired lets you discard superseded evidence — but nothing ever moves backward.
The illegal moves are refused by name, each naming the from-state, the to-state, and the legal
moves that remain:
active → shadow— demoting a serving artifact would leave the store with no active artifact and no record that one was removed. The honest move is to activate its successor, which retires it by name.active → active— “already active” is information, not a no-op; silently succeeding would let an operator believe a promotion happened and write an audit row saying so.retired → anything— a retired artifact is history.
The activation gate
Activation is the one transition that is gated. The rule is per segment: an artifact may be activated if at least one measured segment passed its own B.5 gate. Segments that failed serve nothing — but they do not block the ones that passed.
The strict reading — “every measured segment must pass” — is only ever a disclosure, and the
reason is arithmetic. Requiring N independent segments to each clear a per-segment
false-refusal rate β makes the artifact-level refusal rate 1 − (1 − β)^N: about 14% over three
segments and 99.4% over a hundred. A real fleet is many segments by construction, so a strict
gate would refuse a perfectly calibrated deployment essentially always. Nothing is weakened by
the per-segment reading, because a segment that failed its own gate licenses nothing whether or
not the artifact around it is active — the per-segment gate is where the safety actually lives.
So only two things are refused at activation: an artifact that measured nothing (a fit over an all-synthetic or all-thin window), and one whose every measured segment failed. Both refusals carry the full reliability report.
The refit scheduler
Calibrations go stale as traffic shifts, so a background loop — strictly off the request path, paced by a ticker that checks roughly every five minutes — decides when to refit. Five events can trigger a refit, and each one travels into the log as a named trigger from a closed vocabulary:
| Trigger | Fires when |
|---|---|
drift_finding |
A registered drift detector reports that a segment’s population moved. |
policy_epoch_change |
The policy fingerprint moved — the artifact was fit under an epoch no longer in force. |
staleness |
The artifact passed the freshness horizon (30 days). |
operator_request |
A human asked, with a reason that becomes the log entry’s detail. |
scheduled_interval |
The configured refit_interval elapsed. (Omit it, or set 0s, and there is no periodic refit — the other four triggers are unaffected.) |
Each trigger fires as an edge, not a level: once per (artifact, epoch), never once per
tick. A policy-epoch change is a persistent condition; firing on the level would write one log
entry per tick forever, and a log that grows without limit is an outage. A refit that failed is
retried by the scheduled interval, which is the mechanism that already means “try again later.”
The scheduler’s default promotion mode is shadow. An automatic refit lands its fresh fit in
the shadow slot — fitted, evaluated, never served — and stops. Promoting itself into the
serving slot would be a routing change nobody approved. An operator promotes from shadow to
active with tokentriage quality activate, and that activation is audited.
The recalibration log
Every lifecycle event appends one entry to recalibration.jsonl in the store — a fit,
activate, shadow, retire, or invalidate — and so does every refusal of those, each
classified applied or refused. A refusal is an entry, not an absence: a scheduler that
logged its successes and swallowed its refusals would produce a log in which “the epoch moved and
we refitted” and “the epoch moved and the refit failed for eleven days” are the same document,
just with fewer lines.
The log is strictly append-only (entries are never rewritten in place) and every entry carries a
monotonic Seq seeded from disk, so a restart is invisible and a gap at the head of a read is
the honest disclosure that older entries rotated away. Each entry also requires a detail — a
refused activation with no text is a refusal nobody can act on — so the trail records not just
what happened but why.
Invalidation happens at read time
When a drift finding, the staleness horizon, or a policy-epoch change invalidates a calibration, the artifact’s bytes are never rewritten. They can’t be: the id is a content hash of the fit, so editing an entry and re-saving under the same id is exactly the corruption the store refuses.
Instead the invalidation is applied per segment, at read time. The affected segment’s entry
is flipped to an insufficient refusal — drift_invalidated for a detector finding,
calibration_stale for a stale or epoch-mismatched fit — carrying its own need, have, and
hint, the same emitted-not-omitted shape as every other refusal. The durable record of the
decision is the recalibration log; the flip is computed when the artifact is read. So the system
says “I cannot prove that any more” about the exact thing you asked about, without ever
pretending the original measurement did not happen.
Two calibrations, one chassis
One naming caveat is load-bearing. This page is about tokentriage quality calibrate —
probability calibration, stored under <state_dir>/calibrations/quality/, producing qcal_
artifacts. It is not the pre-existing tokentriage calibrate, which is routing-threshold
calibration — it decides what share of traffic goes to the frontier tier, stores its artifacts
under ~/.tokentriage/calibrations, and is documented in
Routing.
You can watch the whole lifecycle in the Calibration studio — its lifecycle board orders cards candidate → shadow → active → retired, and gated activation shows the reliability diff as its confirm surface. See Dashboard.