Calibration Pipeline Spec
A calibration pipeline spec is a JSON description of a calibration run: the targets, an ordered list of steps, and run parameters. It lets an agent or a client start a calibration without writing a Python flow, while the steps it can use are exactly the Step classes the templates are built from.
{
"name": "coarse-then-coherence",
"targets": { "qids": ["0", "1"] },
"steps": [
{ "type": "OneQubitCheck", "mode": "scheduled" },
{ "type": "FilterByStatus" },
{ "type": "CustomOneQubit", "step_name": "coherence", "tasks": ["CheckT1", "CheckT2Echo"] }
],
"default_run_parameters": { "shots": 1024 }
}The spec cannot reference code. Each type maps to one Step class, task names must be enabled for the backend and match the step's task type, and anything else is rejected before dispatch.
Endpoints
| Endpoint | Permission | Purpose |
|---|---|---|
GET /calibration-pipelines/catalog | viewer | Step types with their dependencies and default tasks, the backend's tasks by type, and the JSON schema of the spec |
POST /calibration-pipelines/validate | viewer | Dry run: every problem with a path into the spec, plus the resolved steps and tasks when it would run |
POST /calibration-pipelines/execute | editor | Validate, then run the spec as one execution through the worker's system-calibration-pipeline deployment |
validate never fails with a 422 for a bad spec. It returns valid: false and a problems list such as targets.qids[1]: qubit '99' is not on chip 64Qv3, so a caller can correct the spec. execute returns the same list in the 422 detail when the spec is not valid, and 503 when the worker has not registered the deployment yet.
Preflight checks
Beyond the spec's shape, validate reports what a task would reject before it measures anything, so a run does not fail at step 1 with empty results. The checks read the task metadata the API already has (TaskInfo: declared inputs, run parameters, and outputs), the target qubits' calibration data, and the run parameters recorded on the task that produced a persisted parameter. They do not import workflow code.
| Check | Problem path | What it catches |
|---|---|---|
| Run parameter names | task_run_parameters.<task>.<name> | A name the task does not declare |
| Run parameter types | task_run_parameters.<task>.<name>, default_run_parameters.<name> | A value whose type the declaration cannot take, such as a sweep for shots; a shared value is checked against every pipeline task that declares it, and one that no task declares is reported because it would be ignored |
| Session readout duration | default_run_parameters.readout_duration, task_run_parameters.<task>.readout_duration | A non-positive shared value, or a per-task value that differs from the session (readout_duration is session-scoped in Qubex; the session uses the shared value, or 1024 ns when none is set) |
| Required inputs | steps[i].tasks[j] | A database_required input of a qubit task that no earlier task in the pipeline writes and that a target qubit lacks in its calibration data, such as CheckT1 without Rabi parameters |
| Rabi readout duration | steps[i].tasks[j] | Rabi parameters a task restores that were measured under a different readout duration than this pipeline's session, when no earlier CheckRabi in the pipeline refreshes them |
Coupling steps are not checked for required inputs; their inputs span two qubits and a coupling. For mux_ids targets the checks cover the MUX's four qubits minus exclude_qids. When the backend's task catalog cannot be read, the spec is reported invalid at backend_name rather than passed unchecked.
Steps
| Type | Kind | Runs | Needs before it |
|---|---|---|---|
ConfigureAll | calibration | ConfigureAll for the targets' MUXes | nothing |
BringUp | calibration | BRINGUP_TASKS or tasks | nothing |
OneQubitCheck | calibration | CHECK_1Q_TASKS or tasks | nothing |
OneQubitFineTune | calibration | FULL_1Q_TASKS_AFTER_CHECK or tasks | nothing |
CustomOneQubit | calibration | tasks (qubit tasks) | nothing |
FilterByStatus | transform | keeps qubits whose latest one-qubit step succeeded | OneQubitCheck or OneQubitFineTune |
FilterByMetric | transform | keeps qubits with metric >= threshold | OneQubitCheck or OneQubitFineTune |
GenerateCRSchedule | transform | plans parallel CR groups | candidate qubits |
TwoQubitCalibration | calibration | FULL_2Q_TASKS or tasks | candidate qubits |
CustomTwoQubit | calibration | tasks (coupling tasks) | candidate qubits |
One-qubit steps take mode (synchronized, scheduled, simultaneous_spectroscopy, serial). Custom steps take a step_name that must be unique within the pipeline; it is the step's name in execution history. Targets are either qids or mux_ids with optional exclude_qids.
The step order is checked the same way Pipeline._validate checks it in the worker: a step that lists several requirements needs at least one of them.
Following a run
CalibService.run gives every calibration step its own execution; transform steps create none. The executions of one run share note.flow_run_id, carry note.step_index, and, for a spec run, note.pipeline with the planned steps. GET /executions/{id} on any of them returns a pipeline field that covers the whole run: the plan with each step's execution id, status (pending, skipped for transforms, or the execution's own), task counts, and the figures of its finished tasks. pipeline.status is running while any step runs or the run still holds the project lock between steps, and otherwise the outcome of the last step.
Readers that want the whole run use that field rather than the first execution's status, which completes when step 1 does. QDashClient.waitForExecution does so by default (wholePipeline: false waits for the single execution), so pi-qdash's qdash_wait_execution returns when the run ends, and the chat's execution card shows every step and figure as the run advances.
Where the pieces live
| File | Role |
|---|---|
src/qdash/datamodel/calibration_pipeline.py | The spec models, the step catalog (STEP_CATALOG), the default task lists, and the step-order check. Free of workflow imports so the API can validate without a Prefect runtime |
src/qdash/workflow/service/pipeline_flow.py | The calibration-pipeline system flow: builds Step and Target objects from a spec and calls CalibService.run |
src/qdash/workflow/register_system_flows.py | Registers system-calibration-pipeline on worker startup |
src/qdash/api/services/calibration_pipeline_service.py | Chip, backend, task-type, and run-parameter checks; the catalog |
src/qdash/api/services/calibration_pipeline_preflight.py | The preflight checks: run-parameter types, the session readout duration, required inputs, and the Rabi readout duration |
src/qdash/api/services/flow_service/ | execute_calibration_pipeline in service.py; lock claim, Prefect run and execution row in execution.py |
src/qdash/api/routers/calibration_pipeline.py | The three endpoints |
STEP_CATALOG restates each Step class's requires and provides; tests/qdash/workflow/service/test_pipeline_flow.py asserts the two agree, so adding a Step means adding a spec model, a catalog entry, a build_step branch, and an entry in that test's EVERY_STEP list. The default task lists moved to the datamodel module for the same reason; qdash.workflow.service.tasks re-exports them.
Agent use
The Copilot agent composes a spec from the catalog and the flow templates, validates it, and shows the resolved steps in an approval card before execute runs. The spec is the only thing the agent writes; the procedure it encodes still comes from the templates and Step classes, and the deterministic gates in the engine judge every task result as they do for a saved flow.