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.
Files changed (98) hide show
  1. shotgate-0.2.2/.github/workflows/dockerhub-mirror.yml +91 -0
  2. {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/pypi-smoke.yml +10 -1
  3. {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/release.yml +17 -36
  4. {shotgate-0.2.1 → shotgate-0.2.2}/CHANGELOG.md +29 -4
  5. {shotgate-0.2.1 → shotgate-0.2.2}/CONTRIBUTING.md +2 -2
  6. {shotgate-0.2.1 → shotgate-0.2.2}/Containerfile +1 -1
  7. {shotgate-0.2.1 → shotgate-0.2.2}/Makefile +2 -2
  8. {shotgate-0.2.1 → shotgate-0.2.2}/PKG-INFO +6 -2
  9. {shotgate-0.2.1 → shotgate-0.2.2}/README.md +5 -1
  10. {shotgate-0.2.1 → shotgate-0.2.2}/SECURITY.md +1 -1
  11. {shotgate-0.2.1 → shotgate-0.2.2}/docs/README.md +3 -3
  12. {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0001-record-architecture-decisions.md +2 -2
  13. {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0002-statistical-validation-core.md +3 -3
  14. shotgate-0.2.2/docs/adr/0004-rename-qforge-to-shotgate.md +35 -0
  15. shotgate-0.2.2/docs/adr/0005-license-apache-2-0.md +48 -0
  16. shotgate-0.2.2/docs/adr/0006-hardware-oracle-policy.md +48 -0
  17. {shotgate-0.2.1 → shotgate-0.2.2}/docs/architecture.md +14 -12
  18. {shotgate-0.2.1 → shotgate-0.2.2}/docs/assertions.md +14 -11
  19. {shotgate-0.2.1 → shotgate-0.2.2}/docs/diagrams/architecture.mmd +4 -4
  20. {shotgate-0.2.1 → shotgate-0.2.2}/docs/diagrams/pipeline.mmd +1 -1
  21. {shotgate-0.2.1 → shotgate-0.2.2}/docs/hardware-validation.md +20 -17
  22. {shotgate-0.2.1 → shotgate-0.2.2}/docs/motivation.md +12 -12
  23. {shotgate-0.2.1 → shotgate-0.2.2}/docs/pipeline.md +4 -4
  24. {shotgate-0.2.1 → shotgate-0.2.2}/docs/workflow-spec.md +8 -8
  25. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state/workflow.yaml +1 -1
  26. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware/workflow.yaml +2 -2
  27. {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/README.md +2 -2
  28. {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/cloud-init/user-data +1 -1
  29. {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/create-runner-vm.sh +3 -3
  30. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/README.md +7 -5
  31. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/examples/basic/main.tf +1 -1
  32. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/main.tf +2 -2
  33. {shotgate-0.2.1 → shotgate-0.2.2}/pyproject.toml +2 -2
  34. {shotgate-0.2.1 → shotgate-0.2.2}/scripts/demo.sh +2 -2
  35. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/__init__.py +1 -1
  36. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/base.py +1 -1
  37. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/config.py +2 -2
  38. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/report.py +3 -3
  39. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/telemetry.py +2 -2
  40. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/metrics.py +1 -1
  41. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_ibm_backend.py +3 -3
  42. {shotgate-0.2.1 → shotgate-0.2.2}/.containerignore +0 -0
  43. {shotgate-0.2.1 → shotgate-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  44. {shotgate-0.2.1 → shotgate-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  45. {shotgate-0.2.1 → shotgate-0.2.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  46. {shotgate-0.2.1 → shotgate-0.2.2}/.github/dependabot.yml +0 -0
  47. {shotgate-0.2.1 → shotgate-0.2.2}/.github/scripts/pin-device.py +0 -0
  48. {shotgate-0.2.1 → shotgate-0.2.2}/.github/scripts/pull-base-image.sh +0 -0
  49. {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/ci.yml +0 -0
  50. {shotgate-0.2.1 → shotgate-0.2.2}/.github/workflows/hardware-validation.yml +0 -0
  51. {shotgate-0.2.1 → shotgate-0.2.2}/.gitignore +0 -0
  52. {shotgate-0.2.1 → shotgate-0.2.2}/.gitlab-ci.yml +0 -0
  53. {shotgate-0.2.1 → shotgate-0.2.2}/CODEOWNERS +0 -0
  54. {shotgate-0.2.1 → shotgate-0.2.2}/CODE_OF_CONDUCT.md +0 -0
  55. {shotgate-0.2.1 → shotgate-0.2.2}/GOVERNANCE.md +0 -0
  56. {shotgate-0.2.1 → shotgate-0.2.2}/Jenkinsfile +0 -0
  57. {shotgate-0.2.1 → shotgate-0.2.2}/LICENSE +0 -0
  58. {shotgate-0.2.1 → shotgate-0.2.2}/MAINTAINERS.md +0 -0
  59. {shotgate-0.2.1 → shotgate-0.2.2}/NOTICE +0 -0
  60. {shotgate-0.2.1 → shotgate-0.2.2}/docs/adr/0003-container-and-vm-isolation.md +0 -0
  61. {shotgate-0.2.1 → shotgate-0.2.2}/docs/assets/bell-state-demo.svg +0 -0
  62. {shotgate-0.2.1 → shotgate-0.2.2}/docs/assets/shotgate-logomark.svg +0 -0
  63. {shotgate-0.2.1 → shotgate-0.2.2}/docs/getting-started.md +0 -0
  64. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state/bell.qasm +0 -0
  65. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware/bell.qasm +0 -0
  66. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware-oracle-coverage/bell.qasm +0 -0
  67. {shotgate-0.2.1 → shotgate-0.2.2}/examples/bell-state-hardware-oracle-coverage/workflow.yaml +0 -0
  68. {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state/ghz.qasm +0 -0
  69. {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state/workflow.yaml +0 -0
  70. {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state-hardware/ghz.qasm +0 -0
  71. {shotgate-0.2.1 → shotgate-0.2.2}/examples/ghz-state-hardware/workflow.yaml +0 -0
  72. {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q/grover.qasm +0 -0
  73. {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q/workflow.yaml +0 -0
  74. {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q-hardware/grover.qasm +0 -0
  75. {shotgate-0.2.1 → shotgate-0.2.2}/examples/grover-2q-hardware/workflow.yaml +0 -0
  76. {shotgate-0.2.1 → shotgate-0.2.2}/infra/qemu/cloud-init/meta-data +0 -0
  77. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/outputs.tf +0 -0
  78. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/variables.tf +0 -0
  79. {shotgate-0.2.1 → shotgate-0.2.2}/infra/terraform/versions.tf +0 -0
  80. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/__main__.py +0 -0
  81. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/__init__.py +0 -0
  82. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/ibm_runtime.py +0 -0
  83. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/local_aer.py +0 -0
  84. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/backends/registry.py +0 -0
  85. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/circuits/__init__.py +0 -0
  86. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/circuits/loader.py +0 -0
  87. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/cli.py +0 -0
  88. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/pytest_plugin.py +0 -0
  89. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/runner.py +0 -0
  90. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/__init__.py +0 -0
  91. {shotgate-0.2.1 → shotgate-0.2.2}/src/shotgate/validation/assertions.py +0 -0
  92. {shotgate-0.2.1 → shotgate-0.2.2}/tests/conftest.py +0 -0
  93. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_assertions.py +0 -0
  94. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_config.py +0 -0
  95. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_metrics.py +0 -0
  96. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_pytest_plugin.py +0 -0
  97. {shotgate-0.2.1 → shotgate-0.2.2}/tests/test_report.py +0 -0
  98. {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
- pip install -q --no-cache-dir "$SPEC" pytest
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
- # Optional Docker Hub mirror. GHCR is the canonical registry; this only runs
108
- # when both DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets exist,
109
- # so the release never fails for the absence of a Docker Hub account. To
110
- # enable: create a Docker Hub repo and an access token, then add those two
111
- # secrets. The Hub namespace defaults to the GitHub owner; override with the
112
- # DOCKERHUB_NAMESPACE secret if your Hub org differs.
113
- - name: Mirror images to Docker Hub (optional)
114
- if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
115
- env:
116
- VERSION: ${{ steps.meta.outputs.version }}
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 "Everything below is downloadable straight from GitHub. No PyPI or Docker Hub account is needed."
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** (wheel + sdist attached to this release):"
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** (published to GitHub Container Registry):"
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} # add -ibm for the QPU-capable variant"
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
- *Nothing yet.*
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 …`; building from source is the contributor fallback.
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 `:…-ibm` image tag).
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.1...HEAD
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 they have design implications).
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 no host installs)
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 runtime image built and run with Podman (rootless-friendly).
1
+ # shotgate runtime image, built and run with Podman (rootless-friendly).
2
2
  #
3
3
  # Multi-stage:
4
4
  # builder -> installs shotgate + the Aer simulator into an isolated venv
@@ -1,4 +1,4 @@
1
- # shotgate Podman/QEMU task runner.
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 while the image
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.1
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` ✅ passed
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` ✅ passed
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 not
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 to validate the gates on a real IBM QPU. |
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) quantum workflow as code.
18
- - [KVM/QEMU runner](../infra/qemu/README.md) ephemeral VM isolation tier.
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 why statistical oracles, why containers,
10
- why a Terraform module instead of a provider need to be legible to contributors and
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 total variation distance, Hellinger fidelity, chi-square
11
- goodness-of-fit but they live in research prototypes, not in production CI tooling.
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 in CI/CD pipelines".
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: `v0.1` (alpha). This document describes the design, its rationale, and
4
- > the extension points that keep shotgate "minimal but scalable".
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 built on deterministic equality has no
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 not equality. The χ² survival function is implemented from first principles ([`metrics.py`](../src/shotgate/validation/metrics.py)). |
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 no quantum SDK required"]
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 SDKs imported lazily"]
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 [ADR-0003](adr/0003-container-and-vm-isolation.md).
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 | Do this | Touches the core? |
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 (v0.1)
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).