shotgate 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shotgate-0.2.2/.github/workflows/dockerhub-mirror.yml +91 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/pypi-smoke.yml +10 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/release.yml +17 -36
- {shotgate-0.2.1 → shotgate-0.2.2}/CHANGELOG.md +29 -4
- {shotgate-0.2.1 → shotgate-0.2.2}/CONTRIBUTING.md +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/Containerfile +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/Makefile +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/PKG-INFO +6 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/README.md +5 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/SECURITY.md +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/README.md +3 -3
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0001-record-architecture-decisions.md +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0002-statistical-validation-core.md +3 -3
- shotgate-0.2.2/docs/adr/0004-rename-qforge-to-shotgate.md +35 -0
- shotgate-0.2.2/docs/adr/0005-license-apache-2-0.md +48 -0
- shotgate-0.2.2/docs/adr/0006-hardware-oracle-policy.md +48 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/architecture.md +14 -12
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/assertions.md +14 -11
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/diagrams/architecture.mmd +4 -4
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/diagrams/pipeline.mmd +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/hardware-validation.md +20 -17
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/motivation.md +12 -12
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/pipeline.md +4 -4
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/workflow-spec.md +8 -8
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state/workflow.yaml +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware/workflow.yaml +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/README.md +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/cloud-init/user-data +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/create-runner-vm.sh +3 -3
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/README.md +7 -5
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/examples/basic/main.tf +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/main.tf +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/pyproject.toml +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/scripts/demo.sh +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/__init__.py +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/base.py +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/config.py +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/report.py +3 -3
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/telemetry.py +2 -2
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/metrics.py +1 -1
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_ibm_backend.py +3 -3
- {shotgate-0.2.1 → shotgate-0.2.2}/.containerignore +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/dependabot.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/scripts/pin-device.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/scripts/pull-base-image.sh +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/ci.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/hardware-validation.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.gitignore +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/.gitlab-ci.yml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/CODEOWNERS +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/CODE_OF_CONDUCT.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/GOVERNANCE.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/Jenkinsfile +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/LICENSE +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/MAINTAINERS.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/NOTICE +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0003-container-and-vm-isolation.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/assets/bell-state-demo.svg +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/assets/shotgate-logomark.svg +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/docs/getting-started.md +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state/bell.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware/bell.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware-oracle-coverage/bell.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware-oracle-coverage/workflow.yaml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state/ghz.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state/workflow.yaml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state-hardware/ghz.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state-hardware/workflow.yaml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q/grover.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q/workflow.yaml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q-hardware/grover.qasm +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q-hardware/workflow.yaml +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/cloud-init/meta-data +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/outputs.tf +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/variables.tf +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/versions.tf +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/__main__.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/__init__.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/ibm_runtime.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/local_aer.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/registry.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/circuits/__init__.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/circuits/loader.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/cli.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/pytest_plugin.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/runner.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/__init__.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/assertions.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/conftest.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_assertions.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_config.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_metrics.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_pytest_plugin.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_report.py +0 -0
- {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_runner_integration.py +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
name: dockerhub-mirror
|
|
2
|
+
|
|
3
|
+
# Mirror an already-published GHCR image set to Docker Hub, then sync this repo's
|
|
4
|
+
# README and tagline onto the Docker Hub repository page so the listing matches the
|
|
5
|
+
# source. GHCR stays the canonical registry; Docker Hub is a convenience mirror for
|
|
6
|
+
# discovery and the familiar `docker pull`. Runs on release (via workflow_call from
|
|
7
|
+
# release.yml) and on demand (workflow_dispatch) to re-mirror or back-fill any version.
|
|
8
|
+
#
|
|
9
|
+
# Credentials live in the `dockerhub` environment: DOCKERHUB_USERNAME and
|
|
10
|
+
# DOCKERHUB_TOKEN (a Docker Hub access token), and optionally DOCKERHUB_NAMESPACE
|
|
11
|
+
# to push under a Hub organization instead of the username. Absent credentials
|
|
12
|
+
# (a fork), the mirror step is skipped rather than failing.
|
|
13
|
+
#
|
|
14
|
+
# The README sync uses peter-evans/dockerhub-description, which drives the Docker
|
|
15
|
+
# Hub HTTP API. That API accepts a personal access token only if it is scoped
|
|
16
|
+
# read/write/delete; if DOCKERHUB_TOKEN is scoped narrower, set
|
|
17
|
+
# DOCKERHUB_DESCRIPTION_PASSWORD in the `dockerhub` environment (the account
|
|
18
|
+
# password or a read/write/delete token) and it is used in preference. The sync
|
|
19
|
+
# step is non-fatal (`continue-on-error`): a description failure never blocks the
|
|
20
|
+
# image mirror or the release.
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
inputs:
|
|
25
|
+
version:
|
|
26
|
+
description: "Version to mirror from GHCR to Docker Hub (e.g. 0.2.1)"
|
|
27
|
+
required: true
|
|
28
|
+
workflow_call:
|
|
29
|
+
inputs:
|
|
30
|
+
version:
|
|
31
|
+
required: true
|
|
32
|
+
type: string
|
|
33
|
+
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
packages: read
|
|
37
|
+
|
|
38
|
+
jobs:
|
|
39
|
+
mirror:
|
|
40
|
+
name: mirror GHCR -> Docker Hub
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment: dockerhub
|
|
43
|
+
env:
|
|
44
|
+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
45
|
+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
46
|
+
DOCKERHUB_NAMESPACE: ${{ secrets.DOCKERHUB_NAMESPACE }}
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v6
|
|
49
|
+
|
|
50
|
+
- name: Compute Docker Hub repository
|
|
51
|
+
id: hub
|
|
52
|
+
if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
|
|
53
|
+
run: |
|
|
54
|
+
# Namespace defaults to the Hub username; DOCKERHUB_NAMESPACE overrides
|
|
55
|
+
# it to push under a Hub organization. Same rule as the mirror step.
|
|
56
|
+
NS="${DOCKERHUB_NAMESPACE:-$DOCKERHUB_USERNAME}"
|
|
57
|
+
echo "repository=${NS,,}/shotgate" >> "$GITHUB_OUTPUT"
|
|
58
|
+
|
|
59
|
+
- name: Mirror the version, latest, and -ibm tags
|
|
60
|
+
if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
|
|
61
|
+
env:
|
|
62
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
63
|
+
ACTOR: ${{ github.actor }}
|
|
64
|
+
REPO: ${{ github.repository }}
|
|
65
|
+
VERSION: ${{ inputs.version }}
|
|
66
|
+
HUB_REPO: ${{ steps.hub.outputs.repository }}
|
|
67
|
+
run: |
|
|
68
|
+
V="${VERSION#v}" # tolerate a leading 'v'
|
|
69
|
+
GHCR="ghcr.io/${REPO,,}"
|
|
70
|
+
DH="docker.io/${HUB_REPO}"
|
|
71
|
+
echo "$GH_TOKEN" | podman login ghcr.io -u "$ACTOR" --password-stdin
|
|
72
|
+
echo "$DOCKERHUB_TOKEN" | podman login docker.io -u "$DOCKERHUB_USERNAME" --password-stdin
|
|
73
|
+
for tag in "$V" latest "${V}-ibm" latest-ibm; do
|
|
74
|
+
podman pull "${GHCR}:${tag}"
|
|
75
|
+
podman tag "${GHCR}:${tag}" "${DH}:${tag}"
|
|
76
|
+
podman push "${DH}:${tag}"
|
|
77
|
+
done
|
|
78
|
+
echo "Mirrored ${GHCR}:{${V},latest,${V}-ibm,latest-ibm} -> ${DH}"
|
|
79
|
+
|
|
80
|
+
# Keep the Docker Hub repository page in sync with the source: the repo
|
|
81
|
+
# README becomes the full description, the project tagline the short one.
|
|
82
|
+
- name: Sync Docker Hub README and short description
|
|
83
|
+
if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
|
|
84
|
+
continue-on-error: true
|
|
85
|
+
uses: peter-evans/dockerhub-description@v5
|
|
86
|
+
with:
|
|
87
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
88
|
+
password: ${{ secrets.DOCKERHUB_DESCRIPTION_PASSWORD || secrets.DOCKERHUB_TOKEN }}
|
|
89
|
+
repository: ${{ steps.hub.outputs.repository }}
|
|
90
|
+
short-description: "Container-native CI/CD quality gates for quantum circuits"
|
|
91
|
+
readme-filepath: ./README.md
|
|
@@ -40,7 +40,16 @@ jobs:
|
|
|
40
40
|
docker.io/library/python:3.12-slim bash -euc '
|
|
41
41
|
SPEC="shotgate[aer]"
|
|
42
42
|
[ -n "$REQUESTED_VERSION" ] && SPEC="shotgate[aer]==$REQUESTED_VERSION"
|
|
43
|
-
|
|
43
|
+
# A freshly released version can lag for several minutes in the PyPI
|
|
44
|
+
# /simple/ index that pip reads, even after the files are live and the
|
|
45
|
+
# per-release JSON shows them (observed ~10 min on 2026-06-11). Retry
|
|
46
|
+
# generously so dispatching right after a release tolerates the lag.
|
|
47
|
+
for attempt in $(seq 1 20); do
|
|
48
|
+
pip install -q --no-cache-dir "$SPEC" pytest && break
|
|
49
|
+
echo "install attempt ${attempt}/20 failed (index may be propagating); retrying in 45s" >&2
|
|
50
|
+
sleep 45
|
|
51
|
+
done
|
|
52
|
+
pip show shotgate >/dev/null # fail the step if all attempts failed
|
|
44
53
|
echo "--- installed:"
|
|
45
54
|
pip show shotgate | sed -n "1,2p"
|
|
46
55
|
echo "--- CLI gate (exit 0 expected):"
|
|
@@ -44,12 +44,6 @@ jobs:
|
|
|
44
44
|
name: publish container image to GHCR
|
|
45
45
|
runs-on: ubuntu-latest
|
|
46
46
|
needs: build-dist
|
|
47
|
-
# Mapped at job level so the optional Docker Hub step can guard on env (the
|
|
48
|
-
# secrets context is not available inside a step-level if:).
|
|
49
|
-
env:
|
|
50
|
-
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
51
|
-
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
52
|
-
DOCKERHUB_NAMESPACE: ${{ secrets.DOCKERHUB_NAMESPACE }}
|
|
53
47
|
steps:
|
|
54
48
|
- uses: actions/checkout@v6
|
|
55
49
|
|
|
@@ -104,31 +98,16 @@ jobs:
|
|
|
104
98
|
podman push "${IMG}:${VERSION}-ibm"
|
|
105
99
|
podman push "${IMG}:latest-ibm"
|
|
106
100
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
SHA: ${{ steps.meta.outputs.sha }}
|
|
118
|
-
DH_USER: ${{ env.DOCKERHUB_USERNAME }}
|
|
119
|
-
DH_TOKEN: ${{ env.DOCKERHUB_TOKEN }}
|
|
120
|
-
DH_NS: ${{ env.DOCKERHUB_NAMESPACE }}
|
|
121
|
-
GHCR_IMG: ${{ steps.meta.outputs.image }}
|
|
122
|
-
OWNER: ${{ github.repository_owner }}
|
|
123
|
-
run: |
|
|
124
|
-
NS="${DH_NS:-$OWNER}"
|
|
125
|
-
DH="docker.io/${NS,,}/shotgate"
|
|
126
|
-
echo "$DH_TOKEN" | podman login docker.io -u "$DH_USER" --password-stdin
|
|
127
|
-
# Retag the images already built above (no rebuild) and push the same set.
|
|
128
|
-
for tag in "$VERSION" "$SHA" latest "${VERSION}-ibm" latest-ibm; do
|
|
129
|
-
podman tag "${GHCR_IMG}:${tag}" "${DH}:${tag}"
|
|
130
|
-
podman push "${DH}:${tag}"
|
|
131
|
-
done
|
|
101
|
+
# Mirror the just-published GHCR images to Docker Hub (optional convenience
|
|
102
|
+
# registry). Delegated to the reusable dockerhub-mirror workflow, which is also
|
|
103
|
+
# dispatchable on its own to re-mirror any version. Skipped automatically when
|
|
104
|
+
# the dockerhub environment has no credentials.
|
|
105
|
+
mirror-dockerhub:
|
|
106
|
+
needs: publish-image
|
|
107
|
+
uses: ./.github/workflows/dockerhub-mirror.yml
|
|
108
|
+
with:
|
|
109
|
+
version: ${{ github.ref_name }}
|
|
110
|
+
secrets: inherit
|
|
132
111
|
|
|
133
112
|
publish-pypi:
|
|
134
113
|
name: publish to PyPI (trusted publishing)
|
|
@@ -173,18 +152,20 @@ jobs:
|
|
|
173
152
|
{
|
|
174
153
|
echo "## Install ${GITHUB_REF_NAME}"
|
|
175
154
|
echo
|
|
176
|
-
echo "
|
|
155
|
+
echo "The package is on **PyPI** and the image on the **GitHub Container Registry (GHCR)** for convenience (the usual one-liners below). If you would rather not use a package index at all, the wheel attached to this release and the GHCR image are both pullable straight from GitHub, no extra account needed."
|
|
177
156
|
echo
|
|
178
|
-
echo "**Python package** (
|
|
157
|
+
echo "**Python package** (PyPI, or the attached wheel):"
|
|
179
158
|
echo '```bash'
|
|
159
|
+
echo "pip install 'shotgate[aer]==${V}'"
|
|
160
|
+
echo "# or straight from this release, no index:"
|
|
180
161
|
echo "pip install https://github.com/${GITHUB_REPOSITORY}/releases/download/${GITHUB_REF_NAME}/shotgate-${V}-py3-none-any.whl"
|
|
181
|
-
echo "# or, from any index that mirrors it: pip install 'shotgate[aer]==${V}'"
|
|
182
162
|
echo '```'
|
|
183
163
|
echo
|
|
184
|
-
echo "**Container** (
|
|
164
|
+
echo "**Container** (GHCR, canonical; add the \`-ibm\` tag suffix for the QPU-capable variant):"
|
|
185
165
|
echo '```bash'
|
|
186
|
-
echo "podman pull ghcr.io/${GITHUB_REPOSITORY}:${V}
|
|
166
|
+
echo "podman pull ghcr.io/${GITHUB_REPOSITORY}:${V}"
|
|
187
167
|
echo "podman run --rm -v \"\$PWD:/work:Z\" -w /work ghcr.io/${GITHUB_REPOSITORY}:${V} run examples/bell-state/workflow.yaml"
|
|
168
|
+
echo "# also mirrored to Docker Hub: docker.io/${GITHUB_REPOSITORY}:${V}"
|
|
188
169
|
echo '```'
|
|
189
170
|
echo
|
|
190
171
|
echo "---"
|
|
@@ -6,7 +6,31 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## [0.2.2] - 2026-06-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Three Architecture Decision Records.** ADR-0004 (rename `qforge` to `shotgate`,
|
|
14
|
+
including the breaking `apiVersion` change), ADR-0005 (license MIT to
|
|
15
|
+
AGPL-3.0-or-later to Apache-2.0, and the DCO inbound policy), and ADR-0006
|
|
16
|
+
(`chi_square` simulator-only, noise-aware hardware thresholds), completing the
|
|
17
|
+
ADR series against the GOVERNANCE.md criteria.
|
|
18
|
+
- **Docker Hub repository page sync.** After mirroring images, the `dockerhub-mirror`
|
|
19
|
+
workflow now pushes the repository README and the project tagline to the Docker Hub
|
|
20
|
+
repository description, so the Hub listing tracks the source. The step is non-fatal:
|
|
21
|
+
a description-API failure never blocks the image mirror or the release.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Docker Hub mirroring is a reusable, dispatchable workflow.** The mirror moved from an
|
|
26
|
+
inline release job to `dockerhub-mirror.yml` (`workflow_call` from the release plus
|
|
27
|
+
`workflow_dispatch` to back-fill any version), with credentials read from the
|
|
28
|
+
`dockerhub` environment (`DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`, optional
|
|
29
|
+
`DOCKERHUB_NAMESPACE`) instead of repository secrets.
|
|
30
|
+
- **Reporter copy uses ASCII separators.** The Markdown summary title format is now
|
|
31
|
+
``## shotgate: `name` (status)``, the no-assertion row prints `n/a`, and the plain
|
|
32
|
+
console fallback separates label and message with a colon; previously all three used
|
|
33
|
+
an em-dash. JUnit and JSON payloads are unchanged.
|
|
10
34
|
|
|
11
35
|
## [0.2.1] - 2026-06-11
|
|
12
36
|
|
|
@@ -102,7 +126,7 @@ All notable changes to this project are documented here. The format is based on
|
|
|
102
126
|
moved to the new name. The canonical home is `github.com/coldqubit/shotgate` and the
|
|
103
127
|
published image is `ghcr.io/coldqubit/shotgate`.
|
|
104
128
|
- **Pull-first usage.** Documentation now leads with `podman run
|
|
105
|
-
ghcr.io/coldqubit/shotgate
|
|
129
|
+
ghcr.io/coldqubit/shotgate ...`; building from source is the contributor fallback.
|
|
106
130
|
- **Honest extras.** `braket` and `mitigation` are marked *planned* and removed from the
|
|
107
131
|
installable `all` extra, since no Braket backend or Mitiq integration ships yet.
|
|
108
132
|
`--backend braket` now fails fast at schema validation with a clear message.
|
|
@@ -123,7 +147,7 @@ All notable changes to this project are documented here. The format is based on
|
|
|
123
147
|
- **Hardened IBM/QPU backend**: robust counts extraction from named classical registers
|
|
124
148
|
(with a clear error on unexpected result shapes), corrected Runtime channel default,
|
|
125
149
|
a token-gated smoke test, and an `[ibm]`-baked image variant (build arg
|
|
126
|
-
`SHOTGATE_EXTRAS="aer,ibm"`, published as the
|
|
150
|
+
`SHOTGATE_EXTRAS="aer,ibm"`, published as the `:...-ibm` image tag).
|
|
127
151
|
- **Noise-tolerant example** (`examples/bell-state-hardware/`) with relaxed thresholds
|
|
128
152
|
for real-device runs, plus a documented simulator-vs-QPU threshold split.
|
|
129
153
|
- **`docs/hardware-validation.md`**: a step-by-step plan and acceptance matrix for
|
|
@@ -169,7 +193,8 @@ All notable changes to this project are documented here. The format is based on
|
|
|
169
193
|
(with the math), getting-started guide, and ADRs.
|
|
170
194
|
- **Examples**: Bell state, 3-qubit GHZ, and 2-qubit Grover workflows.
|
|
171
195
|
|
|
172
|
-
[Unreleased]: https://github.com/coldqubit/shotgate/compare/v0.2.
|
|
196
|
+
[Unreleased]: https://github.com/coldqubit/shotgate/compare/v0.2.2...HEAD
|
|
197
|
+
[0.2.2]: https://github.com/coldqubit/shotgate/compare/v0.2.1...v0.2.2
|
|
173
198
|
[0.2.1]: https://github.com/coldqubit/shotgate/compare/v0.2.0...v0.2.1
|
|
174
199
|
[0.2.0]: https://github.com/coldqubit/shotgate/compare/v0.1.1...v0.2.0
|
|
175
200
|
[0.1.1]: https://github.com/coldqubit/shotgate/compare/v0.1.0...v0.1.1
|
|
@@ -7,11 +7,11 @@ favors small, well-tested, well-reasoned changes.
|
|
|
7
7
|
|
|
8
8
|
- Be kind; follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
9
9
|
- Discuss non-trivial changes in an issue first (especially new assertion oracles or
|
|
10
|
-
backends
|
|
10
|
+
backends, which have design implications).
|
|
11
11
|
- Keep the **validation core dependency-free** (standard library + pydantic). Quantum
|
|
12
12
|
SDKs are optional extras, imported lazily inside backends only.
|
|
13
13
|
|
|
14
|
-
## Development workflow (containers only
|
|
14
|
+
## Development workflow (containers only, no host installs)
|
|
15
15
|
|
|
16
16
|
Everything runs through Podman via the `Makefile`:
|
|
17
17
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# shotgate
|
|
1
|
+
# shotgate: Podman/QEMU task runner.
|
|
2
2
|
#
|
|
3
3
|
# Everything runs in containers or VMs; nothing is installed on the host.
|
|
4
4
|
# Override any variable on the command line, e.g. make run WORKFLOW=examples/ghz-state/workflow.yaml
|
|
@@ -14,7 +14,7 @@ MOUNT := -v "$(CURDIR):/work:Z" -w /work
|
|
|
14
14
|
RUN := $(PODMAN) run --rm $(MOUNT)
|
|
15
15
|
|
|
16
16
|
# Map the invoking host user through the container's user namespace so that
|
|
17
|
-
# report files written to the bind mount are owned by you
|
|
17
|
+
# report files written to the bind mount are owned by you, while the image
|
|
18
18
|
# keeps its non-root default user. The standard Podman idiom for this case.
|
|
19
19
|
USERMAP := --userns=keep-id --user $(shell id -u):$(shell id -g)
|
|
20
20
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shotgate
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Container-native CI/CD quality gates for quantum circuits: statistically validate probabilistic output across simulators and QPUs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/coldqubit/shotgate
|
|
6
6
|
Project-URL: Documentation, https://github.com/coldqubit/shotgate/tree/main/docs
|
|
@@ -138,7 +138,7 @@ $ shotgate run examples/bell-state/workflow.yaml
|
|
|
138
138
|
The same run drops into a pipeline through the Markdown reporter (`--markdown`), rendered
|
|
139
139
|
as a step summary or a PR comment:
|
|
140
140
|
|
|
141
|
-
> ## shotgate: `bell-state`
|
|
141
|
+
> ## shotgate: `bell-state` (✅ passed)
|
|
142
142
|
>
|
|
143
143
|
> - Jobs: **1**, failed: **0**
|
|
144
144
|
> - Assertions: **5**, failed: **0**
|
|
@@ -180,6 +180,10 @@ podman run --rm -e SHOTGATE_IBM_TOKEN \
|
|
|
180
180
|
run examples/bell-state-hardware/workflow.yaml --backend ibm
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
+
The same images are mirrored to Docker Hub if you prefer it: swap the registry prefix for
|
|
184
|
+
`docker.io/coldqubit/shotgate` (tags `latest`, `latest-ibm`, and each version). GHCR is the
|
|
185
|
+
canonical registry; the Docker Hub copy is a convenience mirror.
|
|
186
|
+
|
|
183
187
|
### Path B: pip (CLI and pytest plugin)
|
|
184
188
|
|
|
185
189
|
Install the package with the backend extra you need (`aer` for the local simulator, `ibm` for
|
|
@@ -91,7 +91,7 @@ $ shotgate run examples/bell-state/workflow.yaml
|
|
|
91
91
|
The same run drops into a pipeline through the Markdown reporter (`--markdown`), rendered
|
|
92
92
|
as a step summary or a PR comment:
|
|
93
93
|
|
|
94
|
-
> ## shotgate: `bell-state`
|
|
94
|
+
> ## shotgate: `bell-state` (✅ passed)
|
|
95
95
|
>
|
|
96
96
|
> - Jobs: **1**, failed: **0**
|
|
97
97
|
> - Assertions: **5**, failed: **0**
|
|
@@ -133,6 +133,10 @@ podman run --rm -e SHOTGATE_IBM_TOKEN \
|
|
|
133
133
|
run examples/bell-state-hardware/workflow.yaml --backend ibm
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
The same images are mirrored to Docker Hub if you prefer it: swap the registry prefix for
|
|
137
|
+
`docker.io/coldqubit/shotgate` (tags `latest`, `latest-ibm`, and each version). GHCR is the
|
|
138
|
+
canonical registry; the Docker Hub copy is a convenience mirror.
|
|
139
|
+
|
|
136
140
|
### Path B: pip (CLI and pytest plugin)
|
|
137
141
|
|
|
138
142
|
Install the package with the backend extra you need (`aer` for the local simulator, `ibm` for
|
|
@@ -15,7 +15,7 @@ acknowledge within 72 hours and to provide a remediation timeline after triage.
|
|
|
15
15
|
|
|
16
16
|
## Security model (what to attack / what we defend)
|
|
17
17
|
|
|
18
|
-
- **Untrusted circuits.** Circuits are OpenQASM parsed by qiskit's QASM reader
|
|
18
|
+
- **Untrusted circuits.** Circuits are OpenQASM parsed by qiskit's QASM reader, not
|
|
19
19
|
`eval`'d Python. For circuits from untrusted sources, use the **VM isolation tier**
|
|
20
20
|
(`infra/qemu`), which runs the workflow in an ephemeral KVM micro-VM.
|
|
21
21
|
- **Secrets.** Provider tokens are read from environment variables / container `-e`
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
| [Pipeline Schema](pipeline.md) | How shotgate gates a CI/CD pipeline; stage contract; exit codes. |
|
|
8
8
|
| [Workflow Specification](workflow-spec.md) | The `shotgate.dev/v1alpha1` YAML schema, field by field. |
|
|
9
9
|
| [Assertion Catalog](assertions.md) | The statistical oracles, with the math and threshold guidance. |
|
|
10
|
-
| [Hardware Validation Plan](hardware-validation.md) | v0.2 runbook
|
|
10
|
+
| [Hardware Validation Plan](hardware-validation.md) | v0.2 milestone: runbook and measured `ibm_fez` baseline (2026-06-11). |
|
|
11
11
|
| [Motivation](motivation.md) | The market/community gap shotgate fills, with sources. |
|
|
12
12
|
| [ADRs](adr/) | Architecture Decision Records and their rationale. |
|
|
13
13
|
| [Diagrams](diagrams/) | Mermaid sources for the architecture and pipeline diagrams. |
|
|
14
14
|
|
|
15
15
|
Infrastructure docs live next to the code they describe:
|
|
16
16
|
|
|
17
|
-
- [Terraform module](../infra/terraform/README.md)
|
|
18
|
-
- [KVM/QEMU runner](../infra/qemu/README.md)
|
|
17
|
+
- [Terraform module](../infra/terraform/README.md): quantum workflow as code.
|
|
18
|
+
- [KVM/QEMU runner](../infra/qemu/README.md): ephemeral VM isolation tier.
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
## Context
|
|
7
7
|
|
|
8
8
|
shotgate sits at an immature intersection (quantum × DevOps) where the "right" design
|
|
9
|
-
is not yet settled by convention. Decisions
|
|
10
|
-
why a Terraform module instead of a provider
|
|
9
|
+
is not yet settled by convention. Decisions (why statistical oracles, why containers,
|
|
10
|
+
why a Terraform module instead of a provider) need to be legible to contributors and
|
|
11
11
|
to our future selves.
|
|
12
12
|
|
|
13
13
|
## Decision
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
Quantum programs are probabilistic; exact-equality assertions are either flaky or
|
|
9
9
|
vacuous. The academic literature (χ²-as-oracle, QUTest, QuCheck, QUT) establishes the
|
|
10
|
-
correct techniques
|
|
11
|
-
goodness-of-fit
|
|
10
|
+
correct techniques (total variation distance, Hellinger fidelity, chi-square
|
|
11
|
+
goodness-of-fit), but they live in research prototypes, not in production CI tooling.
|
|
12
12
|
Meanwhile DevOps writers repeatedly note that quantum CI "lacks robust test
|
|
13
|
-
frameworks" and needs "specialized validation
|
|
13
|
+
frameworks" and needs "specialized validation... in CI/CD pipelines".
|
|
14
14
|
|
|
15
15
|
We must also decide the dependency footprint. Pulling SciPy (for `chi2.sf`) and numpy
|
|
16
16
|
into the core would make the metrics layer heavy and couple it to a scientific stack.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ADR-0004: Rename qforge to shotgate (and the workflow apiVersion with it)
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-01
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The project started on 2026-06-01 under the name `qforge`. Before any release was
|
|
9
|
+
tagged, two problems surfaced: the name was already taken on the package indexes
|
|
10
|
+
(PyPI and crates.io), and it raised a trademark concern on the VS Code marketplace.
|
|
11
|
+
The name is load-bearing in more places than the repository title: the Python
|
|
12
|
+
package, the command-line interface (CLI) program, the container image path, the
|
|
13
|
+
environment variables, and the workflow schema's `apiVersion` literal
|
|
14
|
+
(`qforge.dev/v1alpha1`) all embed it.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
Rename the project to **shotgate** everywhere the old name appeared: package
|
|
19
|
+
`src/shotgate`, CLI program `shotgate`, image `ghcr.io/coldqubit/shotgate`,
|
|
20
|
+
environment variable prefix `SHOTGATE_*`, and the workflow schema pin
|
|
21
|
+
`apiVersion: shotgate.dev/v1alpha1`. The schema pin is a pydantic `Literal`, so a
|
|
22
|
+
workflow declaring the old `qforge.dev/v1alpha1` fails schema validation rather
|
|
23
|
+
than being silently accepted.
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
|
|
27
|
+
- **+** The name is unique on PyPI and the container registries, so the package and
|
|
28
|
+
image are installable under their own name with no squatting or trademark risk.
|
|
29
|
+
- **+** It describes the product (gating on measurement shots) instead of a generic
|
|
30
|
+
"forge" metaphor.
|
|
31
|
+
- **−** The `apiVersion` change is breaking for any pre-rename workflow file. The
|
|
32
|
+
impact was zero in practice: the rename landed on 2026-06-01 and the first tagged
|
|
33
|
+
release (`v0.1.0`) followed on 2026-06-10, so no published package, image, or tag
|
|
34
|
+
ever carried the old name and no external consumer existed to break.
|
|
35
|
+
- The old name survives only in git history and in the `CHANGELOG.md` v0.1.0 notes.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ADR-0005: License is Apache-2.0 (after MIT and an AGPL interlude)
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-08
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The project's license changed twice before the first release settled it:
|
|
9
|
+
|
|
10
|
+
1. **MIT** at inception (2026-06-01).
|
|
11
|
+
2. **AGPL-3.0-or-later** later the same day, chosen for the network copyleft:
|
|
12
|
+
distributing the tool, or running a modified version as a hosted service, would
|
|
13
|
+
have required releasing source.
|
|
14
|
+
3. **Apache-2.0** on 2026-06-08, two days before the first tagged release.
|
|
15
|
+
|
|
16
|
+
The week under AGPL exposed a conflict with the adoption model. shotgate's target
|
|
17
|
+
user is a corporate continuous integration (CI) pipeline, and the quantum ecosystem
|
|
18
|
+
it builds on (Qiskit, Qiskit Aer, Cirq, Mitiq) is Apache-2.0 licensed; a
|
|
19
|
+
network-copyleft gate inside that stack invites legal review friction that a CI
|
|
20
|
+
utility cannot justify, and the "hosted modified service" trigger is ambiguous for a
|
|
21
|
+
tool whose whole purpose is to run inside other people's pipelines.
|
|
22
|
+
|
|
23
|
+
Relicensing was legally clean at that date: every line of source had a single
|
|
24
|
+
copyright holder (the maintainer), since the only third-party commits were
|
|
25
|
+
Dependabot version-range bumps and none had landed before the relicense commit.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
License the project **Apache-2.0**: verbatim license text in `LICENSE`, the
|
|
30
|
+
two-line SPDX header (`Apache-2.0`, `Copyright (C) 2026 coldqubit`) in all `src/`
|
|
31
|
+
files (17 files at the time), the `Apache-2.0` expression and classifier in
|
|
32
|
+
`pyproject.toml`, and the OCI license label in the `Containerfile`. Inbound
|
|
33
|
+
contributions are accepted under a Developer Certificate of Origin (DCO) sign-off
|
|
34
|
+
carrying the same Apache-2.0 terms (Apache-2.0 section 5), with no separate
|
|
35
|
+
contributor license agreement.
|
|
36
|
+
|
|
37
|
+
## Consequences
|
|
38
|
+
|
|
39
|
+
- **+** Permissive adoption: use, modification, and redistribution for any purpose,
|
|
40
|
+
including commercial and closed-source, retaining the license and attribution
|
|
41
|
+
notices (Apache-2.0 sections 4(a) through 4(d)).
|
|
42
|
+
- **+** An explicit patent grant (section 3), which MIT lacked.
|
|
43
|
+
- **+** License-compatible with the Apache-2.0 quantum SDK stack it imports.
|
|
44
|
+
- **−** The AGPL network-copyleft promise is removed, not replaced: a vendor can
|
|
45
|
+
ship a modified closed-source shotgate as a service.
|
|
46
|
+
- **−** Practically irreversible: once external DCO-signed contributions land,
|
|
47
|
+
relicensing again would require every contributor's consent. This ADR therefore
|
|
48
|
+
also fixes the inbound policy (DCO, same license in and out).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ADR-0006: chi_square is simulator-only; hardware gates use distance, fidelity, and structural oracles
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-11
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The v0.2 milestone ran the example gates on a real quantum processing unit (QPU)
|
|
9
|
+
for the first time (`ibm_fez`, 156-qubit Heron r2, 4096 shots per job). Two facts
|
|
10
|
+
came out of those runs:
|
|
11
|
+
|
|
12
|
+
1. Simulator-tight thresholds (total variation distance (TVD) ≤ 0.03, Hellinger
|
|
13
|
+
fidelity ≥ 0.99, leakage ≤ 0.005) fail on a healthy device: the measured Bell
|
|
14
|
+
TVD was 0.1284 and the GHZ-3 TVD 0.1536.
|
|
15
|
+
2. `chi_square` fails on hardware for a mechanical reason, not a tuning one. The
|
|
16
|
+
non-gating diagnostic Bell job (`d8l592rqv2lc738621eg`) returned statistic
|
|
17
|
+
1.5e17, dof 3, p-value 0.0000, while the four distance/structural oracles passed
|
|
18
|
+
on the same counts (TVD 0.1350, fidelity 0.8650, leakage 0.1350, P(`00`) 0.4343).
|
|
19
|
+
The ideal expected distribution `{00: 0.5, 11: 0.5}` assigns probability 0 to
|
|
20
|
+
the error states `01`/`10`; the implementation floors expected counts at
|
|
21
|
+
`_MIN_EXPECTED = 1e-12` to keep the statistic finite, so each of the 553 leaked
|
|
22
|
+
shots contributes on the order of `observed^2 / 1e-12`. Against an ideal support,
|
|
23
|
+
any device leakage forces rejection regardless of thresholds.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
1. **Hardware gates use the distance, fidelity, marginal, and structural oracles**
|
|
28
|
+
(`distribution_tvd`, `hellinger_fidelity`, `state_probability`,
|
|
29
|
+
`allowed_states`) with noise-aware thresholds (Bell: TVD ≤ 0.15, fidelity
|
|
30
|
+
≥ 0.85, leakage ≤ 0.15; full table in `docs/hardware-validation.md` section 4),
|
|
31
|
+
encoded in the `examples/*-hardware` workflows.
|
|
32
|
+
2. **`chi_square` stays simulator-only** until a gate can declare a noise-aware
|
|
33
|
+
expected distribution (a device error model with nonzero mass on the error
|
|
34
|
+
states), which is out of scope for v0.2.
|
|
35
|
+
3. A **non-gating oracle-coverage diagnostic**
|
|
36
|
+
(`examples/bell-state-hardware-oracle-coverage`) keeps measuring all five
|
|
37
|
+
oracles on hardware so the exclusion stays evidence-based run over run.
|
|
38
|
+
|
|
39
|
+
## Consequences
|
|
40
|
+
|
|
41
|
+
- **+** The hardware gates pass a healthy device with measured margins of 0.0216
|
|
42
|
+
(Bell TVD, the tightest) to 0.1357 (Grover P(`11`)) while still bounding leakage
|
|
43
|
+
and fidelity, so they catch a broken circuit or mis-mapped qubits.
|
|
44
|
+
- **−** The formal hypothesis test is unavailable on hardware, so hardware gating
|
|
45
|
+
loses the goodness-of-fit (GoF) guarantee and relies on distance bounds instead.
|
|
46
|
+
- Revisit when an error-model expected distribution exists (candidate for the v0.3
|
|
47
|
+
error-mitigation work); the diagnostic's measurements will show when `chi_square`
|
|
48
|
+
becomes viable on devices.
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# Solution Architecture
|
|
2
2
|
|
|
3
|
-
> Status: `
|
|
4
|
-
> the extension points that keep shotgate
|
|
3
|
+
> Status: `0.2.x` (alpha; oracles validated on `ibm_fez`, 2026-06-11). This document
|
|
4
|
+
> describes the design, its rationale, and the extension points that keep shotgate
|
|
5
|
+
> "minimal but scalable".
|
|
5
6
|
|
|
6
7
|
## 1. Problem statement
|
|
7
8
|
|
|
8
9
|
Quantum programs are probabilistic. The same circuit, executed twice, returns
|
|
9
|
-
different shot counts. Classical CI/CD
|
|
10
|
+
different shot counts. Classical CI/CD, built on deterministic equality, has no
|
|
10
11
|
native way to gate on this. Three concrete gaps follow:
|
|
11
12
|
|
|
12
13
|
1. **No statistical quality gate.** Teams hand-roll ad-hoc `assert counts["00"] > N`
|
|
13
14
|
checks that are either flaky or meaningless.
|
|
14
15
|
2. **No "workflow as code".** There is no declarative, reviewable artifact that says
|
|
15
16
|
*"this circuit, on this backend, must satisfy these statistical properties"*.
|
|
16
|
-
3. **No IaC / isolation story.** Terraform/Helm can't describe quantum workloads, and
|
|
17
|
+
3. **No Infrastructure-as-Code (IaC) / isolation story.** Terraform/Helm can't describe quantum workloads, and
|
|
17
18
|
running untrusted circuits in CI lacks an isolation model.
|
|
18
19
|
|
|
19
20
|
shotgate addresses all three with one composable tool.
|
|
@@ -22,7 +23,7 @@ shotgate addresses all three with one composable tool.
|
|
|
22
23
|
|
|
23
24
|
| Principle | Consequence in the codebase |
|
|
24
25
|
| --- | --- |
|
|
25
|
-
| **Statistically correct by construction** | Oracles are χ², TVD, Hellinger fidelity
|
|
26
|
+
| **Statistically correct by construction** | Oracles are χ², total variation distance (TVD), Hellinger fidelity, not equality. The χ² survival function is implemented from first principles ([`metrics.py`](../src/shotgate/validation/metrics.py)). |
|
|
26
27
|
| **Core is dependency-light** | The validation core imports only the standard library + pydantic. No quantum SDK is needed to parse a workflow or compute a metric. |
|
|
27
28
|
| **Backends are lazy & pluggable** | Heavy SDKs (qiskit, braket) are imported only when a backend actually runs. New providers register a `"module:Class"` string. |
|
|
28
29
|
| **Everything runs in a container** | The unit of execution is the shotgate image. The host needs only Podman (and optionally QEMU). |
|
|
@@ -33,13 +34,13 @@ shotgate addresses all three with one composable tool.
|
|
|
33
34
|
|
|
34
35
|
```mermaid
|
|
35
36
|
flowchart TB
|
|
36
|
-
subgraph CoreLayer["Validation core
|
|
37
|
+
subgraph CoreLayer["Validation core: no quantum SDK required"]
|
|
37
38
|
config["config.py<br/>Workflow schema (pydantic)"]
|
|
38
39
|
assertions["validation/assertions.py<br/>Oracle models + evaluate()"]
|
|
39
40
|
metrics["validation/metrics.py<br/>TVD · Hellinger · χ² (incomplete gamma)"]
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
subgraph ExecLayer["Execution layer
|
|
43
|
+
subgraph ExecLayer["Execution layer: SDKs imported lazily"]
|
|
43
44
|
loader["circuits/loader.py<br/>OpenQASM → circuit"]
|
|
44
45
|
registry["backends/registry.py<br/>provider → Backend"]
|
|
45
46
|
base["backends/base.py<br/>Backend ABC + BackendResult"]
|
|
@@ -65,7 +66,7 @@ flowchart TB
|
|
|
65
66
|
|
|
66
67
|
**Dependency direction is strictly downward.** `report` and `runner` depend on the
|
|
67
68
|
core; the core never imports the execution layer. This is what lets the same wheel
|
|
68
|
-
run a 30 MB metrics-only container and a full QPU job.
|
|
69
|
+
run a 30 MB metrics-only container and a full quantum processing unit (QPU) job.
|
|
69
70
|
|
|
70
71
|
## 4. Execution data flow
|
|
71
72
|
|
|
@@ -116,11 +117,12 @@ flowchart LR
|
|
|
116
117
|
| 2 | Rootless Podman, non-root image user | Shared/hardened CI runners. |
|
|
117
118
|
| 3 | Ephemeral Fedora micro-VM (`infra/qemu`) | Untrusted circuits; VM-grade stage isolation. |
|
|
118
119
|
|
|
119
|
-
See
|
|
120
|
+
See the Architecture Decision Record (ADR)
|
|
121
|
+
[ADR-0003](adr/0003-container-and-vm-isolation.md).
|
|
120
122
|
|
|
121
123
|
## 6. Security model
|
|
122
124
|
|
|
123
|
-
- **No executable circuits.** Circuits are OpenQASM, parsed by qiskit's QASM reader
|
|
125
|
+
- **No executable circuits.** Circuits are OpenQASM, parsed by qiskit's QASM reader,
|
|
124
126
|
not `eval`'d Python.
|
|
125
127
|
- **Strict schema.** Unknown fields are rejected, preventing silent misconfiguration.
|
|
126
128
|
- **Secrets never touch disk.** Cloud tokens come from env vars / container `-e`; the
|
|
@@ -130,7 +132,7 @@ See [ADR-0003](adr/0003-container-and-vm-isolation.md).
|
|
|
130
132
|
|
|
131
133
|
## 7. Extension points (how it scales)
|
|
132
134
|
|
|
133
|
-
| To add
|
|
135
|
+
| To add... | Do this | Touches the core? |
|
|
134
136
|
| --- | --- | --- |
|
|
135
137
|
| A new **assertion oracle** | Add a pydantic model with a unique `type` literal + `evaluate()`, append to the `Assertion` union and `ASSERTION_TYPES` | No core math beyond a new `metrics` fn |
|
|
136
138
|
| A new **backend** | Implement `Backend`, register `"module:Class"` in the registry, add an optional extra | No |
|
|
@@ -140,7 +142,7 @@ See [ADR-0003](adr/0003-container-and-vm-isolation.md).
|
|
|
140
142
|
Because the contract between layers is small (a `BackendResult` of counts, an
|
|
141
143
|
`AssertionResult` of pass/fail+metrics), each layer evolves independently.
|
|
142
144
|
|
|
143
|
-
## 8. Non-goals (
|
|
145
|
+
## 8. Non-goals (as of 0.2.x)
|
|
144
146
|
|
|
145
147
|
- Circuit *synthesis* or optimization (that's the SDKs' job).
|
|
146
148
|
- Full quantum-state tomography assertions (planned; counts-based oracles first).
|