Uniform Execution Contract (UEC)
You don’t need to create envelopes manually. SDK adapters (
devqubit-qiskit,devqubit-braket, etc.) automatically capture and validate ExecutionEnvelopes duringrun.wrap()executions. For runs without an adapter (manual tracking), the engine synthesizes a minimal envelope at finalization. This document describes the internal structure for contributors and advanced debugging.
Quantum experiments depend on programs, devices, execution settings, and (on hardware) calibration that drift over time. To keep runs reproducible and comparable, adapters produce a standardized ExecutionEnvelope that captures execution context and results.
A Run Record stays small and queryable.
The ExecutionEnvelope is the canonical, schema-validated “evidence pack” used by diff/verify tooling.
Envelope at a Glance
%%{init:{
"theme":"base",
"flowchart":{"curve":"basis","nodeSpacing":36,"rankSpacing":44,"htmlLabels":true},
"themeVariables":{
"fontFamily":"ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, sans-serif",
"fontSize":"14px",
"background":"#ffffff",
"textColor":"#0f172a",
"lineColor":"#94a3b8",
"clusterBkg":"#f8fafc",
"clusterBorder":"#cbd5e1",
"edgeLabelBackground":"#ffffff"
}
}}%%
flowchart TB
subgraph ENV["ExecutionEnvelope"]
direction TB
META["envelope_id<br/> created_at<br/> schema<br/>"]
subgraph SNAPSHOTS[" "]
direction TB
PROD["<b>producer</b><br/>name: devqubit<br/>adapter: qiskit<br/>sdk_version: 1.2.0"]
PROG["<b>program</b><br/>logical[]<br/>physical[]<br/>structural_hash<br/>parametric_hash"]
DEV["<b>device</b><br/>backend_name: ibm_kyoto<br/>num_qubits: 127<br/>calibration"]
EXEC["<b>execution</b><br/>shots: 1000<br/>job_ids[]<br/>submitted_at<br/>completed_at"]
RES["<b>result</b><br/>success: true<br/>status: completed<br/>items[]<br/>raw_result_ref"]
end
end
subgraph STORE["Artifact Store"]
direction TB
A1["circuit.qasm<br/><i>sha256:a1b2c3...</i>"]
A2["circuit.qpy<br/><i>sha256:d4e5f6...</i>"]
A3["backend_properties.json<br/><i>sha256:789abc...</i>"]
A4["raw_result.json<br/><i>sha256:def012...</i>"]
end
META ~~~ SNAPSHOTS
PROG -.->|logical_ref| A1
PROG -.->|physical_ref| A2
DEV -.->|raw_properties_ref| A3
RES -.->|raw_result_ref| A4
%% Linki: delikatniejsze i równe
linkStyle default stroke:#94a3b8,stroke-width:1.4
%% Klasy zamiast wielu style-line
classDef grpBlue fill:#eff6ff,stroke:#2563eb,stroke-width:1.6,color:#0f172a;
classDef grpAmber fill:#fffbeb,stroke:#d97706,stroke-width:1.6,color:#0f172a;
classDef cardBlue fill:#dbeafe,stroke:#3b82f6,stroke-width:1.4,color:#0f172a;
classDef cardGreen fill:#ecfdf5,stroke:#10b981,stroke-width:1.4,color:#064e3b;
classDef artifact fill:#fff7ed,stroke:#f59e0b,stroke-width:1.4,color:#7c2d12;
class ENV grpBlue
class STORE grpAmber
class META cardBlue
class PROD,PROG,DEV,EXEC,RES cardGreen
class A1,A2,A3,A4 artifact
Required fields: schema, envelope_id, created_at, producer, result.
Schema Contract
All envelopes are validated against devqubit.envelope/1.0 (JSON Schema Draft 2020-12). Top-level is strict (additionalProperties: false). Extra data belongs in metadata.
Conditional requirements:
If
producer.adapter != "manual":programandexecutionmust existprogram.structural_hashandprogram.parametric_hashmust exist
If
program.physicalexists and is non-empty:program.executed_structural_hashandprogram.executed_parametric_hashmust exist
Snapshot Reference
Each snapshot has its own schema identifier for independent versioning.
producer — ProducerInfo
Required: name, adapter, frontends.
Optional: engine_version, adapter_version, sdk, sdk_version.
program — ProgramSnapshot
Schema: devqubit.program_snapshot/1.0
Field |
Description |
|---|---|
|
Pre-transpilation circuit artifacts |
|
Post-transpilation circuit artifacts |
|
Hash of circuit structure (ignores parameter values) |
|
Hash of structure + bound parameter values |
|
Structural hash of transpiled circuits (when physical exists) |
|
Parametric hash of transpiled circuits |
|
Number of circuits |
|
Adapter-specific transpilation details |
device — DeviceSnapshot
Schema: devqubit.device_snapshot/1.0
Field |
Description |
|---|---|
|
Snapshot timestamp |
|
Backend identifier |
|
|
|
Provider identifier |
|
Qubit count |
|
Edge list |
|
Supported gate set |
|
Aggregated calibration metrics |
|
ArtifactRef to full raw backend properties |
execution — ExecutionSnapshot
Schema: devqubit.execution_snapshot/1.0
Field |
Description |
|---|---|
|
Submission timestamp |
|
Completion timestamp |
|
Number of shots |
|
Number of executions |
|
Provider job IDs |
|
Transpilation metadata |
|
Raw execution options |
|
SDK identifier |
result — ResultSnapshot
Schema: devqubit.result_snapshot/1.0
Required: success, status, items.
Field |
Description |
|---|---|
|
Overall execution success |
|
|
|
Structured error info ( |
|
Per-circuit results (counts, quasi_probability, or expectation) |
|
ArtifactRef to full raw SDK result |
Bitstring normalization: counts.format.bit_order is canonical cbit0_right (LSB on right). If the source SDK uses a different convention, adapters set format.transformed = true and record format.source_key_format. This prevents silent mismatches when comparing distributions across SDKs.
ArtifactRef
Content-addressed references used throughout:
Field |
Description |
|---|---|
|
Required, format: |
|
Artifact type (e.g., |
|
MIME type |
|
Logical role ( |
|
Optional small metadata (sizes, versions) |
Adapter Contract
Emit a schema-valid envelope for every successful execution.
On failure, emit an envelope with
result.success=falseand structuredresult.error.Validate envelopes in adapters (fail fast on integration bugs).
Store large payloads as artifacts via
ArtifactRefinstead of embedding inline.Exclude volatile IDs (job IDs, timestamps) from stability-critical fingerprints.
Accessing Envelope Data
import json
from devqubit.storage import create_store, create_registry
store = create_store()
registry = create_registry()
record = registry.load(run_id)
envelope_artifact = next(
(a for a in record.artifacts if a.role == "envelope"), None
)
if envelope_artifact:
envelope = json.loads(store.get_bytes(envelope_artifact.digest))
# Device info
print(f"Backend: {envelope['device']['backend_name']}")
# Results
for item in envelope["result"]["items"]:
if "counts" in item:
print(f"Counts: {item['counts']['counts']}")