All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
wai-quantum contract / does this change touch it (pull_request) Successful in 6s
wai-quantum contract / the policy is linked, and the deprecations page reads (pull_request) Successful in 5s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m25s
wai-quantum contract / API compatibility · linux (pull_request) Successful in 4m8s
wai-quantum contract / schemas and the committed tool lists · linux (pull_request) Successful in 5m53s
wai-quantum 0.8.0, wai-quantum-cli 0.2.3 and wai-quantum-qpu 0.4.0 are on crates.io, so the API is checked against them from here on. Co-Authored-By: Claude Opus 5.5 <[email protected]>
311 lines
16 KiB
YAML
311 lines
16 KiB
YAML
name: wai-quantum contract
|
|
|
|
# The version and deprecation contract (wai/wai-quantum/VERSIONING.md), on
|
|
# Linux:
|
|
#
|
|
# - api: cargo-semver-checks, built from crates.io at a pinned version,
|
|
# compares wai-quantum, wai-quantum-cli and wai-quantum-qpu with the last
|
|
# version of each on crates.io (the baselines below). It checks every change as if for a minor
|
|
# release, so that each breaking change is listed, and fails only on a
|
|
# breaking change made without a major bump. Before 1.0 the first non-zero
|
|
# component is the major (0.6.16 to 0.7.0 is a major bump). A step warns
|
|
# when crates.io holds a newer version than a baseline here, so a baseline
|
|
# does not go stale unseen.
|
|
# - schemas: the schema tests, each by name. The engine's: every schema file
|
|
# reads and names itself, every method's job, a set, a result's provenance,
|
|
# every error-correction answer, a mitigated job, every answer of the
|
|
# variational and algorithm library and every engine tool's
|
|
# answer follow their schemas with no member they do not name, and receipts issued
|
|
# before results named their engine and schema still verify, their digests
|
|
# pinned. Both MCP servers': every tool's answer follows its outputSchema,
|
|
# the committed tool lists (wai-quantum-cli/docs/local-mcp.json and
|
|
# service-mcp.json) are the live lists, the agent docs are what the binary
|
|
# generates, and initialize reports the versions. The job service's job in
|
|
# each status, a set's among them, and its receipt key set after a rotation;
|
|
# the command line's scheduler lease, its harness's report, corpus and token
|
|
# counts, and the course's progress file; the answer layer's documents, a
|
|
# correctness record, the views page's manifest, the course, its exercises
|
|
# and its checks. The provider protocol's messages and
|
|
# answers. Then a full
|
|
# JSON Schema implementation (wai/wai-quantum/ci/check_schemas.py) reads
|
|
# every schema file and both tool lists, and holds a release build's
|
|
# answers to them.
|
|
# - policy: every README of a WAI Quantum package links the policy, and the
|
|
# deprecations page's table is well formed.
|
|
#
|
|
# The receipts' legacy pins and the conformance corpora run where they always
|
|
# have (wai-energy-receipts.yml, wai-conformance.yml).
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/wai-quantum-cli/**'
|
|
- 'wai/wai-quantum-service/**'
|
|
- 'wai/wai-quantum-qpu/**'
|
|
# The schema tests read the answer layer's conformance set and the harness's corpus.
|
|
- 'wai/quantum-conformance/answers/**'
|
|
- 'wai/quantum-conformance/eval/**'
|
|
- 'wai/wai-quantum-py/README.md'
|
|
- 'wai/wai-quantum-desktop/README.md'
|
|
- 'wai/quantum-studio/README.md'
|
|
- 'wai/tools/wai-quantum-editor/README.md'
|
|
- 'wai/tools/tree-sitter-openqasm/README.md'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-contract.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/wai-quantum-cli/**'
|
|
- 'wai/wai-quantum-service/**'
|
|
- 'wai/wai-quantum-qpu/**'
|
|
# The schema tests read the answer layer's conformance set and the harness's corpus.
|
|
- 'wai/quantum-conformance/answers/**'
|
|
- 'wai/quantum-conformance/eval/**'
|
|
- 'wai/wai-quantum-py/README.md'
|
|
- 'wai/wai-quantum-desktop/README.md'
|
|
- 'wai/quantum-studio/README.md'
|
|
- 'wai/tools/wai-quantum-editor/README.md'
|
|
- 'wai/tools/tree-sitter-openqasm/README.md'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-contract.yml'
|
|
|
|
jobs:
|
|
# Does this pull request's own diff touch the paths above? This forge matches a
|
|
# workflow's `paths:` against the push, so a merge of main into a branch fires the
|
|
# workflow whatever the pull request changes; every job here waits on this answer.
|
|
# A push to main, or anything the script cannot determine, answers `true`
|
|
# (scripts/pr-touches-paths.py).
|
|
relevant:
|
|
name: does this change touch it
|
|
runs-on: linux-arm64
|
|
outputs:
|
|
run: ${{ steps.gate.outputs.run }}
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
- id: gate
|
|
run: |
|
|
set -euo pipefail
|
|
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-contract.yml \
|
|
"${{ github.event.pull_request.base.sha }}" \
|
|
"${{ github.event.pull_request.head.sha }}")
|
|
echo "this change touches the wai-quantum contract: $r"
|
|
echo "run=$r" >> "$GITHUB_OUTPUT"
|
|
|
|
api:
|
|
name: API compatibility · linux
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 60
|
|
env:
|
|
# The last version of each on crates.io. Raise each when a release is published.
|
|
WAI_QUANTUM_BASELINE: "0.8.0"
|
|
WAI_QUANTUM_CLI_BASELINE: "0.2.3"
|
|
WAI_QUANTUM_QPU_BASELINE: "0.4.0"
|
|
# Built from its source on crates.io, at this version.
|
|
SEMVER_CHECKS: "0.51.0"
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install Rust 1.98.0
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: Install cargo-semver-checks ${{ env.SEMVER_CHECKS }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cargo install cargo-semver-checks --version "$SEMVER_CHECKS" --locked --root "$RUNNER_TEMP/semver"
|
|
echo "$RUNNER_TEMP/semver/bin" >> "$GITHUB_PATH"
|
|
- name: The baselines are the last versions published
|
|
shell: bash
|
|
working-directory: ${{ runner.temp }}
|
|
run: |
|
|
set -euo pipefail
|
|
for pair in "wai-quantum $WAI_QUANTUM_BASELINE" "wai-quantum-cli $WAI_QUANTUM_CLI_BASELINE" "wai-quantum-qpu $WAI_QUANTUM_QPU_BASELINE"; do
|
|
set -- $pair
|
|
latest=$(cargo info "$1" 2>/dev/null | sed -n 's/^version: *\([^ ]*\).*/\1/p' | head -1 || true)
|
|
if [ -z "$latest" ]; then
|
|
echo "::warning::crates.io did not say which version of $1 is the last; the check runs against $2"
|
|
elif [ "$latest" != "$2" ]; then
|
|
echo "::warning::crates.io's last version of $1 is $latest, and this workflow's baseline is $2: raise the baseline"
|
|
else
|
|
echo "$1: the baseline $2 is the last version on crates.io"
|
|
fi
|
|
done
|
|
- name: API compatibility, against the last versions published
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
# The major of a version: the first non-zero component (0.6.16 -> 0.6, 1.2.3 -> 1).
|
|
major() { IFS=. read -r a b _ <<< "$1"; if [ "$a" = 0 ]; then echo "0.$b"; else echo "$a"; fi; }
|
|
# Every change is checked as if for a minor release, so that each breaking
|
|
# change is listed; a breaking change fails the step only when the major
|
|
# is still the baseline's. A build that fails is a failure either way.
|
|
check() {
|
|
local dir=$1 base=$2 cur log
|
|
cur=$(sed -n 's/^version *= *"\([^"]*\)".*/\1/p' "$dir/Cargo.toml" | head -1)
|
|
log="$RUNNER_TEMP/semver-$(basename "$dir").log"
|
|
echo "== $dir: $base -> $cur, the major $(major "$base") -> $(major "$cur")"
|
|
if cargo semver-checks check-release --manifest-path "$dir/Cargo.toml" --baseline-version "$base" --release-type minor 2>&1 | tee "$log"; then
|
|
echo "$dir: no breaking change against $base"
|
|
elif ! grep -q 'semver requires new major version' "$log"; then
|
|
echo "::error::$dir: the check did not run to its end (above)"
|
|
return 1
|
|
elif [ "$(major "$cur")" != "$(major "$base")" ]; then
|
|
echo "::warning::$dir: breaking changes against $base, listed above; allowed, as the major is bumped to $(major "$cur")"
|
|
else
|
|
echo "::error::$dir: a breaking change against $base, listed above, without a major bump: raise $(major "$base") (before 1.0, the 0.y component), and say what to do instead in the changelog"
|
|
return 1
|
|
fi
|
|
}
|
|
r=0
|
|
check wai/wai-quantum "$WAI_QUANTUM_BASELINE" || r=1
|
|
check wai/wai-quantum-cli "$WAI_QUANTUM_CLI_BASELINE" || r=1
|
|
check wai/wai-quantum-qpu "$WAI_QUANTUM_QPU_BASELINE" || r=1
|
|
exit $r
|
|
|
|
schemas:
|
|
name: schemas and the committed tool lists · linux
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install Rust 1.98.0, and uv
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: The engine's schemas, and receipts issued before
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --no-default-features --features quantum_engine,quantum_receipt,quantum_qec,quantum_noise --lib engine::schema:: 2>&1 | tee "$RUNNER_TEMP/engine.log"
|
|
for t in every_schema_reads_names_itself_and_refers_only_to_what_is_there \
|
|
the_checker_reads_the_subset_it_names_and_refuses_what_it_should \
|
|
every_check_s_answer_follows_its_schema \
|
|
every_method_s_job_follows_the_job_schemas \
|
|
a_new_result_names_its_engine_and_schema_and_its_receipt_binds_them \
|
|
error_correction_and_mitigation_follow_their_schemas \
|
|
the_answer_layer_the_correctness_record_and_the_views_bundle_follow_their_schemas \
|
|
the_course_its_exercises_and_its_checks_follow_their_schemas \
|
|
the_variational_and_algorithm_answers_follow_their_schema \
|
|
receipts_issued_before_results_named_them_still_verify; do
|
|
grep -q "^test engine::schema::tests::$t \.\.\. ok$" "$RUNNER_TEMP/engine.log" || { echo "::error::$t did not run and pass"; exit 1; }
|
|
done
|
|
- name: The command line's MCP server, its committed tool list and its docs
|
|
shell: bash
|
|
working-directory: wai/wai-quantum-cli
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --bin wai-quantum 2>&1 | tee "$RUNNER_TEMP/cli.log"
|
|
for t in mcp::tests::the_schemas_read_and_name_themselves \
|
|
mcp::tests::every_tool_s_answer_follows_its_output_schema \
|
|
mcp::tests::the_committed_tool_list_is_this_server_s_live_list \
|
|
mcp::tests::an_agent_initializes_lists_the_tools_and_runs_a_circuit \
|
|
guide::tests::the_committed_docs_are_what_the_binary_generates \
|
|
guide::tests::the_docs_list_exactly_the_servers_tools_and_name_no_version \
|
|
tests::the_command_line_parses_as_documented \
|
|
task::tests::a_lease_follows_its_schema \
|
|
eval::tests::a_scripted_run_reports_what_the_engine_found \
|
|
eval::tests::the_corpus_is_the_task_set_written_three_ways \
|
|
learn::tests::progress_is_kept_in_one_file_and_marks_a_pass_once; do
|
|
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/cli.log" || { echo "::error::$t did not run and pass"; exit 1; }
|
|
done
|
|
- name: The job service's MCP server, its committed tool list, its job and its receipt keys
|
|
shell: bash
|
|
working-directory: wai/wai-quantum-service
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release -- mcp:: jobs:: receipt:: 2>&1 | tee "$RUNNER_TEMP/service.log"
|
|
for t in mcp::tests::the_schemas_read_and_the_versions_are_reported \
|
|
mcp::tests::every_tool_s_answer_follows_its_output_schema \
|
|
mcp::tests::every_tool_has_a_schema_and_a_description \
|
|
mcp::tests::the_command_line_s_snapshot_is_this_server_s_live_list \
|
|
jobs::tests::a_job_s_form_follows_the_job_schema \
|
|
receipt::tests::a_rotation_keeps_the_old_key_published_with_its_window_and_a_signed_transition; do
|
|
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/service.log" || { echo "::error::$t did not run and pass"; exit 1; }
|
|
done
|
|
- name: The provider protocol's messages
|
|
shell: bash
|
|
working-directory: wai/wai-quantum-qpu
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --test protocol_schema 2>&1 | tee "$RUNNER_TEMP/qpu.log"
|
|
for t in the_schema_names_the_protocol_s_own_constants every_message_and_answer_follows_the_protocol_s_schema; do
|
|
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/qpu.log" || { echo "::error::$t did not run and pass"; exit 1; }
|
|
done
|
|
# A second implementation of JSON Schema, sharing no code with the engine's
|
|
# checker: every schema file and both tool lists valid 2020-12 schemas, every
|
|
# reference resolving, and a release build's answers holding to them.
|
|
- name: The schemas, read by a full JSON Schema implementation
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
(cd wai/wai-quantum-cli && cargo build --locked --release)
|
|
uv venv -q --python 3.12 "$RUNNER_TEMP/schemas"
|
|
uv pip install -q --python "$RUNNER_TEMP/schemas/bin/python" --require-hashes -r wai/wai-quantum-py/tests/requirements.lock
|
|
"$RUNNER_TEMP/schemas/bin/python" -I wai/wai-quantum/ci/check_schemas.py wai wai/wai-quantum-cli/target/release/wai-quantum
|
|
|
|
policy:
|
|
name: the policy is linked, and the deprecations page reads
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Every README of a WAI Quantum package links the policy
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
for f in wai/wai-quantum/README.md wai/wai-quantum-cli/README.md wai/wai-quantum-service/README.md \
|
|
wai/wai-quantum-qpu/README.md wai/wai-quantum-py/README.md wai/wai-quantum-desktop/README.md \
|
|
wai/quantum-studio/README.md wai/tools/wai-quantum-editor/README.md wai/tools/tree-sitter-openqasm/README.md; do
|
|
grep -q 'wai/wai-quantum/VERSIONING.md\|(VERSIONING.md)' "$f" || { echo "::error::$f does not link the policy (wai/wai-quantum/VERSIONING.md)"; exit 1; }
|
|
done
|
|
echo "every README links the policy"
|
|
- name: The deprecations page's table is well formed
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
python3 -I - wai/wai-quantum/DEPRECATIONS.md <<'EOF'
|
|
import re, sys
|
|
lines = open(sys.argv[1], encoding="utf-8").read().splitlines()
|
|
head = "| Package | Item | Deprecated in | Replacement | Removed in |"
|
|
assert head in lines, "the table's header is " + head
|
|
at = lines.index(head)
|
|
assert re.fullmatch(r"\|(---\|){5}", lines[at + 1]), lines[at + 1]
|
|
semver = re.compile(r"\d+\.\d+\.\d+")
|
|
rows = []
|
|
for line in lines[at + 2:]:
|
|
if not line.startswith("|"):
|
|
break
|
|
cells = [c.strip() for c in line.strip("|").split("|")]
|
|
assert len(cells) == 5 and all(cells[:4]), f"a row names its package, item, version and replacement: {line}"
|
|
assert semver.fullmatch(cells[2]), f"deprecated in a version: {line}"
|
|
assert cells[4] == "" or semver.fullmatch(cells[4]), f"removed in a version, or not yet: {line}"
|
|
rows.append(cells)
|
|
print(f"{len(rows)} deprecated item(s) listed")
|
|
EOF
|