The course's documents join the version contract. The course as kept and as served (wai.quantum.course/1) and each exercise (wai.quantum.exercise/1) move from course/schema/ to the crate's schemas/, named and versioned as the rest; a check, a lesson as served and a hint are wai.quantum.course-check/1, and a check now names its engine beside its schema; the command line's progress file is wai.quantum.learn-progress/1. The engine's test holds the course, every exercise, every lesson in each locale and a check and hint of some to them; the learn test holds the progress file; the contract workflow requires both, and the eval and lease tests, by name, and runs when the answer and eval sets its tests read change. The docs runner reads every page the branches added: the error-correction page, the version policy, the deprecations page, the service's self-hosting, scheduler and provenance pages, and the views' README. Each block the other branches wrote either runs or says why it cannot (a sweep fixture added), and the job service it starts keeps its store in its scratch. The crate's README names wai-quantum 0.7. The Chinese READMEs hold the English pages' code again: the sections for error correction, sets and sweeps, the service's dry runs, the scheduler, scoring a model, the answer layer, views and versions are translated, and the compile pages name the gate set basis. The command line's Chinese words cover task, qec, eval and version, and submit as it reads now. The documentation index links the policy, the deprecations, the error-correction page and the service's pages in both languages. The policy lists every schema file and says the answer layer is a draft outside its promise. The 0.7.0 heading names what the four branches bring. The views page bundles the Studio components that now read their words through the locale module; build-views.mjs gives it the English words alone, so no other language's words reach a host. The result parts the Studio and the views share read their words through it too, so the Studio's result pane stays translated, its English unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
309 lines
16 KiB
YAML
309 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 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.6.16"
|
|
WAI_QUANTUM_CLI_BASELINE: "0.1.4"
|
|
WAI_QUANTUM_QPU_BASELINE: "0.2.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 \
|
|
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
|