Parameter Resolution
The workflow engine resolves task inputs, experiment configuration, snapshots, user overrides, and output persistence at different lifecycle stages.
The Frequency Parameter Policy specifies the agreed design for exploration and calibrated frequencies, derived drive frequencies, YAML export, and update UX. Its implementation gaps are tracked separately from the current lifecycle described here.
Parameter model contracts
BaseTask defines three symmetric declaration/runtime pairs:
| Collection | Model | Meaning |
|---|---|---|
input_spec | InputParameterSpec | Resolution and override policy declared by the task class |
input_parameters | InputParameterModel | Values resolved for one task instance |
run_spec | RunParameterSpec | Experiment configuration specified by the task class |
run_parameters | RunParameterModel | Effective experiment configuration for one run |
output_spec | OutputParameterSpec | Calibration outputs declared by the task class |
output_parameters | OutputParameterModel | Calibration values produced by one task instance |
Every calibration input spec explicitly states its resolution and override policies through a named constructor:
input_spec = {
"qubit_frequency": InputParameterSpec.required_database(
unit="GHz",
),
"readout_amplitude": InputParameterSpec.database_or_default(
default=DEFAULT_READOUT_AMPLITUDE,
unit="a.u.",
),
}resolution has three explicit values:
database_required: use the database value and fail when it is absent.database_or_default: prefer the database value and otherwise usedefault.default_only: do not read this parameter from the database.
The constructors default to allowing user overrides; pass user_override="forbidden" when a task must prohibit them. A permitted user override has higher precedence than the selected baseline. The constructor's default is spec-time fallback data; the effective runtime value is stored separately in input_parameters as an InputParameterModel.
For coupling tasks, InputParameterSpec.parameter_name selects the database key and InputParameterSpec.qid_role selects control, target, or coupling data. Qubit tasks read from the selected qubit record.
Normal workflow execution
Task construction resolves Run parameters in this order, from highest to lowest precedence:
- Explicit
task_details[task_name].run_parameters - Task-specific entries in
CalibConfig.task_run_parameters - Legacy per-task entries in
CalibConfig.default_run_parameters - Shared entries in
CalibConfig.default_run_parameters run_specspecified on the task class
Use default_run_parameters only for values intentionally shared by every declaring task. A shared value overrides task-specific defaults, including Qubex optimized defaults: qubit spectroscopy normally uses a 1024 ns interval and resonator spectroscopy uses 0 ns. Use task_run_parameters when a template should preserve or configure one named task. Nested entries in default_run_parameters remain readable only for existing flow documents.
readout_duration is session-scoped in Qubex. QDash passes the shared value when constructing the Experiment, records the effective value on each task that uses the session readout pulse, and rejects a per-task value that differs from the session. Qubex spectroscopy is an intentional exception: qubit/control spectroscopy uses its dedicated 1024 ns pulse and resonator spectroscopy uses its dedicated 8192 ns pulse, so those tasks do not expose the shared readout_duration parameter.
QubexTask.preprocess() then resolves each declared Input parameter:
- Follow the input spec's resolution policy.
- Produce an effective
InputParameterModelfor the task instance. - Reapply an explicit user override when
user_override="allowed". - Reject an override when
user_override="forbidden".
The effective Input and Run parameter models are recorded in task result history for provenance and later snapshot re-execution.
Execution-level parameter policy
The operation itself determines the baseline; no additional source selector is needed:
| Operation | Calibration input baseline | Run parameter baseline | Output persistence default |
|---|---|---|---|
| Normal workflow | Current DB, following task input specs | Task/workflow specs | Enabled |
| Tasks quick run | Current DB, following task input specs | Task specs and submitted overrides | Disabled |
| Task re-execution | Source task snapshot | Source task snapshot | Existing re-execution setting |
This gives re-execution one stable meaning: reproduce the recorded execution conditions, optionally with explicit parameter overrides. Running the same task against current calibration state is a new Tasks quick run, not a re-execution mode.
user_override and persist_output_parameters remain independent policies. A permitted override has the highest precedence; choosing to persist new outputs does not change how inputs were selected.
A re-execution must not silently fall back to current database values. Such fallback would make it non-reproducible. Missing required snapshot data should fail with an actionable error; the operator can then start a new Tasks quick run against current state.
Model boundaries
Task classes declare immutable intent with InputParameterSpec, RunParameterSpec, and OutputParameterSpec. Each task instance receives the matching mutable runtime model: InputParameterModel, RunParameterModel, and OutputParameterModel. All three specs use default for their initial value and expose create_model() for the conversion.
The three runtime models may share persisted calibration metadata internally, but their distinct types keep input, run, and output roles visible at API boundaries and in type checking.
Snapshot re-execution
Re-execution behavior
For a single-task re-execution, CalibService passes source_task_id to SnapshotParameterLoader, which loads that exact task result. task_name and qid then validate that the selected history record matches the requested task. This avoids resolving an already identified task indirectly through its execution.
TaskExecutor applies parameters in this sequence:
- Construct the task from class and workflow defaults.
- Require a matching source snapshot and validate its declared Input coverage.
- Replace Input and Run collections with the snapshot and merged user overrides.
- Mark calibration Inputs as resolved, so
preprocess()cannot refresh them from the database. - Reapply only explicit user overrides.
- Run the experiment and record the resulting effective parameters.
The second override application preserves values computed during preprocessing while guaranteeing that explicit user input remains authoritative. The snapshot marker prevents preprocessing from replacing calibration inputs with current database values.
Calibration inputs are resolved before task preprocessing and passed as InputParameterModel values:
Execution request
-> CalibrationInputResolver(operation context)
-> Apply permitted user overrides
-> Task.preprocess(resolved inputs)
-> Task.run()Task preprocessing may compute derived runtime values, but it must not independently change the selected point-in-time source. Database access for declared calibration inputs belongs to CalibrationInputResolver.
Effective-input validation
Numeric constraints such as greater_than and less_than belong to InputParameterSpec. The executor validates them after database or snapshot resolution and after user overrides, but before run().
Concrete task preprocess() and run() methods must treat Input and Run parameters as read-only. They must not repair an invalid value, replace it with a default, or otherwise change the effective parameter collections. A missing database value may select the specified default only when the input spec uses database_or_default(); a present but invalid database, snapshot, or override value fails execution.
For a single-task re-execution, a missing source_task_id, a mismatch between the selected result and the requested task, or incomplete declared snapshot inputs causes execution to fail instead of silently changing its baseline.
Single-task result re-execution
POST /task-results/{task_id}/re-execute invokes the system single-task-executor with the source execution and source task IDs. It accepts task-level overrides in this shape:
{
"parameter_overrides": {
"input": {"qubit_frequency": 5.1},
"run": {"shots": 2000}
}
}The source task ID both selects the recorded parameter snapshot and links the child result to its parent. The source execution ID remains execution context; it is not used to re-identify the single source task.
This endpoint is available for API clients that require exact snapshot re-execution. The Tasks UI instead offers Run again, which uses the current task definition and prefills compatible values from the selected historical result.
Tasks page quick run
POST /tasks/{task_name}/execute also uses single-task-executor, but sets source_execution_id to None.
input_parameter_overridesbecomesparameter_overrides.input.run_parameter_overridesbecomes an entry intask_run_parametersfor the selected task.- A
SnapshotParameterLoaderis still created when Input overrides exist so they can be reapplied after preprocessing. - When
source_task_idis supplied by Run again, it records provenance without restoring the source snapshot; the submitted form values and current task definition remain authoritative. - With no Input override, Qubex preprocessing loads current database values normally.
The Tasks page Reload action is client-side preparation only. It reads the selected qubit or coupling record and fills the form. The server treats every non-empty submitted field as an explicit override; it does not distinguish a typed value from a value inserted by Reload.
Output persistence
persist_output_parameters controls authoritative write-back. When false, task history, execution state, figures, raw data, and in-memory output processing still occur, but BackendSaver skips calibration database and backend parameter writes.
When persistence is enabled:
- Qubit outputs update the qubit calibration repository.
- Coupling outputs update the coupling calibration repository.
- Successful qubit outputs are synchronized through the backend parameter updater.
- Failed validation skips backend parameter updates unless
force_update_paramsis true.
The API field update_params is passed to single-task-executor as force_update_params and also controls GitHub integration for that run. It does not replace persist_output_parameters; persistence must be enabled before output values can be written.
Implementation files
src/qdash/workflow/calibtasks/base.pysrc/qdash/workflow/calibtasks/qubex/base.pysrc/qdash/workflow/engine/orchestrator.pysrc/qdash/workflow/engine/task/snapshot_loader.pysrc/qdash/workflow/engine/task/executor.pysrc/qdash/workflow/engine/task/backend_saver.pysrc/qdash/workflow/service/calib_service.pysrc/qdash/workflow/service/single_task_flow.py