open-standards/.github/workflows/wai-quantum-cli.yml
Transaction Science 8da651a54b
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 21s
wai-fleet service / does this change touch it (push) Successful in 17s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 26s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 28s
wai-app and Create / wai-app · does this change touch it (push) Successful in 22s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 15s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 50s
wai-quantum editors / does this change touch it (push) Successful in 31s
wai-quantum for JavaScript / does this change touch it (push) Successful in 29s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 40s
wai-audit collector and player integration / does this change touch it (push) Successful in 1m23s
wai-quantum studio and desktop / does this change touch it (push) Successful in 30s
wai-fleet service / service · macos (push) Successful in 59s
wai-quantum for Python / does this change touch it (push) Successful in 31s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 1m27s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 21s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 2m4s
wai-key-proxy / does this change touch it (push) Successful in 1m55s
WAI operation catalogue / does this change touch it (push) Successful in 1m56s
wai-quantum command line / does this change touch it (push) Successful in 1m40s
wai-ledger service / does this change touch it (push) Successful in 1m50s
wai-quantum contract / does this change touch it (push) Successful in 1m41s
WAI Studio / does this change touch it (push) Successful in 12s
wai-sink-swift native sink package / does this change touch it (push) Successful in 25s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 1m43s
wai-sink-android native sink library / does this change touch it (push) Successful in 42s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 2m45s
wai-quantum editors / editors · macos (push) Successful in 2m2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m21s
wai-fleet service / service · linux (push) Successful in 3m36s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 12s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 52s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 2m37s
wai-quantum for Python / python · macos (push) Successful in 2m57s
wai-ledger service / service · macos (push) Successful in 1m16s
wai-quantum editors / editors · linux (push) Successful in 3m46s
wai-audit collector and player integration / collector · linux (push) Successful in 3m39s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 3m23s
wai-quantum for Python / python · linux (push) Successful in 4m26s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 6m38s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m7s
wai-quantum command line / command line · macos (push) Successful in 2m33s
wai-quantum for JavaScript / javascript · linux (push) Successful in 5m35s
wai-key-proxy / proxy · linux (push) Successful in 1m12s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 7m3s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m40s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 7m39s
wai-ledger service / service · linux (push) Successful in 2m5s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 7s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 8m36s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 8m36s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m10s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 4m26s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 6m33s
wai-quantum contract / API compatibility · linux (push) Successful in 4m44s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 13m4s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 6m46s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 14m11s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 14m12s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 13m18s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 10m49s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 11m36s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 12m10s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 16m36s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 9m58s
wai-quantum studio and desktop / studio · browser (push) Successful in 16m35s
WAI Studio / Studio in WebKit · linux (push) Successful in 12m22s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 22m0s
wai release binaries build on Windows / the seven release binaries · windows (push) Successful in 24m31s
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 28m51s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 28m59s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 29m41s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 11m58s
wai-quantum command line / command line · linux (push) Successful in 48m17s
wai: one command for the rest of the toolchain: audit, serve, quantum, cmcd, receipt create, and every record kind in wai verify
Folds, each the program it folds run as itself, so what it prints and its exit
status are the program's for the same arguments: wai audit report|check|verify
(audit.create, audit.check, audit.verify) is wai-audit, --owner its
WAI_AUDIT_OWNER; wai serve <role> [--config F] [--check] (service.start) starts
wai-<role>: relay, room, ingest and key-release from a file, audit, fleet,
ledger and quantum-service from their environment; wai quantum
run|plan|lint|verify|methods (quantum.run, .inspect, .check, .verify, .list)
is wai-quantum with that verb, and no other verb of it is reached. A program is
its flag, WAI_<NAME>_BIN, beside wai, or on PATH; one not there is exit 5,
program-not-found. report is not an N3 verb, so wai audit report is the
operation audit.create.

--check on the daemons: wai-relay, wai-room (its keys too) and wai-ingest
--config F --check, and wai-key-release serve F --check, read the
configuration as each starts from it and start nothing, exit 1 with the reason
when it does not read. The proxy is installed as wai-key-release; wai-key-proxy
is the same program under its earlier name.

wai cmcd encode|decode|verify is wai_telemetry's tool compiled in (cmcd.rs, one
module behind both; telemetry joins full). wai receipt create <file>... --from
<input>... [--key] signs the operation receipt a toolchain write leaves, for
files made elsewhere. wai verify reads every record by its kind: by name, and a
JSON record by what it holds (a delivery receipt or claim as receipt_verify
answers it, a room session, an audit bundle as wai-audit verify does with
--owner/--channel, a sealed playlist by its mr/<blake3>.json as check-playlist
does); a file of no kind is exit 2, unknown-record. wai mcp --toolset quantum
starts wai-quantum mcp and answers each quantum_* tool with that server's own
answer and schemas. wai media score build|render alias wai score. The quantum
operations' SDK names are registered labels.

Tests, each fold against what it folds: cli_folds (cmcd over the whole corpus,
receipt create against wrap's receipt, verify on the claim, JWP, room and audit
corpora and a sealed playlist, audit|serve|quantum against stand-ins);
wai-audit tests/fold.rs, wai-quantum-cli tests/wai_fold.rs and joule-moq-room
tests/cli_serve.rs against the real programs; cli_mcp's quantum toolset; the
daemons' --check (config_check.rs, check.rs). ops.json, mcp.json, the registry
export and Studio's actions regenerated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-10 02:57:54 -05:00

386 lines
26 KiB
YAML

name: wai-quantum command line
# The wai-quantum command line (wai/wai-quantum-cli): its tests and clippy with
# warnings as errors, with and without `submit`; its smoke checks (smoke.sh: lint,
# plan, run, verify and a forged result refused, key, draw, MCP over stdio, the
# research tools); and the WASI component, whose output under wasmtime equals the
# native build's byte for byte. On macOS, also with the GPU state vector. Its tests
# include the model harness (src/eval.rs): every task's reference solved and an
# empty program not, a scripted run, a model server on this machine asked and one
# elsewhere refused, and the corpus (wai/quantum-conformance/eval/) held to the
# task set. On Linux, also the cross-simulator corpus's CI tier
# (wai/quantum-cross-simulator/: the engine against pinned third-party
# simulators and integer references, each answer within its stated tolerance), its
# self-test (every injected fault caught), and its provider template held to the
# provider protocol's conformance vectors through `providers test` and `run --provider`.
# And job receipts by two verifiers (wai/quantum-conformance/receipts/):
# `verify` and receipt_verify.py, written from the provider protocol's section 8
# alone, each held to the corpus's manifest and to the other on every receipt and
# every one-member change of each valid one, and each planted defect caught.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'wai/wai-wasmq/**'
- 'wai/quantum-conformance/eval/**'
- 'wai/quantum-conformance/wasmq/**'
- 'wai/wai-quantum-writer/**'
# The writer's model runs on this crate, which it path-depends on.
- 'jouleclaw/jouleclaw-rs/crates/jouleclaw-loader-gguf/**'
- 'wai/quantum-cross-simulator/**'
- 'wai/quantum-conformance/receipts/**'
- 'wai/quantum-conformance/tools/receipt_verify.py'
- 'wai/quantum-conformance/tools/receipt_differential.py'
- 'wai/quantum-conformance/tools/wasmq_mutants.py'
- 'wai/quantum-conformance/tools/referee.py'
- 'wai/extensions/quantum-providers.md'
- 'wai/extensions/quantum-wasmq.md'
# tests/wai_fold.rs builds wai and holds `wai quantum` and the quantum toolset to this crate's program.
- 'wai/wai-rs/src/bin/wai_cli/fold.rs'
- 'wai/wai-rs/src/bin/wai_cli/mcp/quantum.rs'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-cli.yml'
# Nightly, beside the pushes: each rule's weakenings (below), which take a second build.
schedule:
- cron: '41 3 * * *'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'wai/wai-wasmq/**'
- 'wai/quantum-conformance/eval/**'
- 'wai/quantum-conformance/wasmq/**'
- 'wai/wai-quantum-writer/**'
# The writer's model runs on this crate, which it path-depends on.
- 'jouleclaw/jouleclaw-rs/crates/jouleclaw-loader-gguf/**'
- 'wai/quantum-cross-simulator/**'
- 'wai/quantum-conformance/receipts/**'
- 'wai/quantum-conformance/tools/receipt_verify.py'
- 'wai/quantum-conformance/tools/receipt_differential.py'
- 'wai/quantum-conformance/tools/wasmq_mutants.py'
- 'wai/quantum-conformance/tools/referee.py'
- 'wai/extensions/quantum-providers.md'
- 'wai/extensions/quantum-wasmq.md'
# tests/wai_fold.rs builds wai and holds `wai quantum` and the quantum toolset to this crate's program.
- 'wai/wai-rs/src/bin/wai_cli/fold.rs'
- 'wai/wai-rs/src/bin/wai_cli/mcp/quantum.rs'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-cli.yml'
jobs:
relevant:
name: does this change touch it
runs-on: gate
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-cli.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the wai-quantum command line: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
cli-linux:
name: command line · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.99.1 with clippy and the wasm32-wasip2 target
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.99.1
rustup toolchain install 1.99.1 --profile minimal --component clippy
rustup default 1.99.1
rustup target add --toolchain 1.99.1 wasm32-wasip2
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Tests and clippy, with and without submit
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
# No test reaches a person's timings: every test here names its file,
# so none may appear where a person's would be.
XDG_CONFIG_HOME="$RUNNER_TEMP/no-timings" cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
test ! -e "$RUNNER_TEMP/no-timings/wai/quantum-timings.json" || { echo "::error::a test wrote the timings of whoever runs it"; exit 1; }
# Your own providers end to end (tests/providers.rs), against the
# reference provider this binary serves, and their jobs as MCP
# tasks, each test by name.
for t in json_output_does_not_skip_the_history_it_was_asked_to_check \
lines_judge_a_set_as_a_set \
the_mcp_tool_given_a_job_s_text_gives_the_corpus_s_verdict \
a_member_named_twice_is_unreadable_on_every_kind_of_job \
the_mcp_tool_gives_the_corpus_s_verdict_on_what_a_parsed_job_shows \
a_named_credential_that_is_empty_never_falls_to_the_account_s_keys \
a_person_adds_tests_plans_and_runs_on_a_provider_and_the_receipt_verifies \
a_run_that_is_not_free_is_refused_until_the_person_confirms_it \
a_credential_is_kept_in_a_file_only_its_owner_reads_and_appears_in_nothing_printed \
a_provider_over_http_runs_with_its_credential_in_the_header \
an_agent_lists_estimates_is_refused_and_runs_only_with_confirm_cost \
an_agent_runs_on_a_provider_as_a_task_and_the_server_answers_while_it_waits \
providers::tasks::tests::a_job_reads_as_its_task \
providers::tasks::tests::a_cancelled_request_ends_its_wait_and_a_change_wakes_it \
a_contract_weighs_every_route_and_its_receipt_says_why \
an_agent_weighs_a_contract_and_runs_on_the_route_it_chooses \
a_run_is_stopped_at_its_ceiling_by_a_live_reading_and_its_receipt_says_so \
a_route_nothing_can_stop_is_refused_under_a_ceiling_on_every_surface \
an_estimate_refuses_a_job_clearly_past_a_ceiling_and_keeps_to_its_build \
estimates::tests::the_timings_keep_each_method_s_last_runs_and_read_back \
estimates::tests::three_runs_read_give_an_estimate_and_none_of_them_a_promise \
estimates::tests::a_route_nothing_can_read_or_stop_has_no_watch \
estimates::tests::a_run_in_its_own_process_is_killed_at_its_ceiling_and_read_whole \
estimates::tests::readings_are_kept_under_the_machine_reader_and_build \
estimates::tests::a_counter_is_asked_twice_and_a_reading_kept_nowhere_is_not_taken \
a_run_under_a_ceiling_ends_with_its_command_line \
keys::tests::keys_made_at_once_are_one_key \
estimates::tests::a_run_is_read_only_where_something_needs_the_reading \
estimates::tests::the_wait_for_a_counter_is_in_no_run_s_power \
estimates::tests::a_package_counter_is_read_over_a_few_of_its_updates \
estimates::tests::the_timings_are_kept_where_their_variables_say \
estimates::tests::runs_that_finish_together_all_keep_theirs \
estimates::tests::a_file_that_is_no_store_is_kept_and_a_link_stays_a_link \
tests::the_command_line_parses_as_documented; do
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/test.log" || { echo "::error::$t did not run and pass"; exit 1; }
done
# The model harness and the views in an agent's host, each test by name.
for t in eval::tests::every_task_s_reference_is_solved_and_an_empty_program_is_not \
eval::tests::a_scripted_run_reports_what_the_engine_found \
eval::tests::a_model_on_this_machine_is_asked_and_one_elsewhere_is_refused \
eval::tests::the_corpus_is_the_task_set_written_three_ways \
eval::tests::an_answer_is_read_for_its_program_and_scored \
mcp::tests::a_host_that_draws_views_gets_them_and_one_that_does_not_gets_words; do
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/test.log" || { echo "::error::$t did not run and pass"; exit 1; }
done
# The token counter needs a model's vocabulary to run; here it is only read.
python3 -I -c 'import ast, sys; ast.parse(open(sys.argv[1]).read())' ../quantum-conformance/eval/token_cost.py
cargo clippy --locked --release --all-targets -- -D warnings
cargo clippy --locked --release --all-targets --no-default-features -- -D warnings
# wai-quantum-writer: the loop that writes a program for a task, its model on the
# pure-Rust runtime, checked by the engine as a lesson's answer is. Its tests run the
# loop with a scripted model (no checkpoint), with and without the model compiled in.
- name: The writer, with a scripted model, and clippy
shell: bash
working-directory: wai/wai-quantum-writer
run: |
set -euo pipefail
cargo test --locked --release
cargo test --locked --release --no-default-features
cargo clippy --locked --release --all-targets -- -D warnings
cargo clippy --locked --release --all-targets --no-default-features -- -D warnings
- name: The smoke checks
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
cargo build --locked --release
bash smoke.sh ./target/release/wai-quantum
# The cross-simulator corpus's CI tier: the circuits marked `ci` in its manifest, a few
# seconds of work each, on the engine, pinned third-party simulators (wheels checked
# against their hashes) and the integer references. Every kind of check runs on at least
# one circuit but the pairwise comparison of wide circuits (`wide_pairwise`, 26 qubits),
# which only the full tier makes. A crash, time-out or unlisted refusal of the engine
# under test fails it. Then each fault compare.py can inject must fail its own check on
# its own engine; then the provider template end to end. The full corpus is
# `reproduce.sh --tier full`, run by hand (its README). referee.py is on the paths above
# because the corpus imports it.
- name: The cross-simulator corpus (CI tier), its self-test, and the provider template
shell: bash
run: |
set -euo pipefail
export QCORPUS_CACHE="$HOME/.cache/wai-quantum-cross-simulator"
bash wai/quantum-cross-simulator/reproduce.sh --tier ci --jobs 2 --out "$RUNNER_TEMP/cross-simulator.json"
bash wai/quantum-cross-simulator/reproduce.sh --self-test --jobs 2
# The page's results section is what the committed results files say.
python3 -I wai/quantum-cross-simulator/tools/report.py --check
# Job receipts, judged by two verifiers that share no code: this binary's `verify`, and
# receipt_verify.py, written in Python from wai/extensions/quantum-providers.md section 8
# alone (standard library only, its BLAKE3 and Ed25519 its own). The corpus's crafted half
# is what generate.py writes; the second verifier's self-test holds its BLAKE3, Ed25519,
# RFC 8785 and number text to published answers. Then both verifiers and the manifest must
# agree on every receipt of the corpus, and the two verifiers on every receipt made from a
# valid one by changing one member, or by setting a number past a double's whole numbers.
# Each planted defect of the second verifier (a lenient key check, alg unchecked, the last
# of a duplicate member kept, inexact numbers let through, a simulator's counts let pass as
# measured, a ceiling on every whole number, R of small order let through, the canonical
# spelling of a large double refused, a set's seal checked and nothing of its items,
# qubit names held to the layouts only, and under a contract each route's outcome, the
# planner's choice or a shadow's distance taken as it stands) must make the
# differential fail: the proof that it can. A plant that the corpus alone catches is caught
# by the second-only run too, in the independent verifiers' step.
- name: Job receipts, two verifiers
shell: bash
run: |
set -euo pipefail
python3 -I wai/quantum-conformance/receipts/generate.py --check
python3 -I wai/quantum-conformance/receipts/generate.py --self-test
python3 -I wai/quantum-conformance/tools/receipt_verify.py --self-test wai/wai-quantum/src/engine/testdata/es-number-tostring.txt
python3 -I wai/quantum-conformance/tools/receipt_differential.py --reference wai/wai-quantum-cli/target/release/wai-quantum
# Each plant runs the whole differential with one verifier broken on purpose, a few
# minutes each: they run side by side, and every one must be caught.
printf '%s\n' lenient-key no-alg last-duplicate no-inexact measured-simulator whole-cap lax-small-order-r no-canonical-spelling set-seal-only names-layout-only route-unchecked choice-unchecked distance-unchecked wasmq-unchecked \
| xargs -P "$(nproc)" -I{} bash -c 'set -o pipefail; python3 -I wai/quantum-conformance/tools/receipt_differential.py --reference wai/wai-quantum-cli/target/release/wai-quantum --plant "{}" | tail -1' \
| tee "$RUNNER_TEMP/plants.log"
test "$(grep -c ': caught' "$RUNNER_TEMP/plants.log")" -eq 14 || { echo "::error::not every plant was caught"; exit 1; }
# Each rule of a wasmQ job's form weakened in each verifier, from its code (the check
# dropped, a comparison moved by one, a count or a digest read loosely, a null member read
# as absent and an absent one as null), and every weakening caught by the differential:
# the second verifier's as files, the engine's as one build that holds every weakening,
# each switched on alone (wasmq_mutants.py). Counted per rule. Nightly.
- name: Each rule's weakenings caught, in both verifiers
if: github.event_name == 'schedule'
shell: bash
run: |
set -euo pipefail
python3 -I wai/quantum-conformance/tools/wasmq_mutants.py python --reference wai/wai-quantum-cli/target/release/wai-quantum --jobs 4
m="$RUNNER_TEMP/wasmq-mutants"
rm -rf "$m" && mkdir -p "$m"
git worktree add --detach "$m/wt" HEAD
python3 -I wai/quantum-conformance/tools/wasmq_mutants.py rust-schemata "$m/wt/wai/wai-quantum/src/engine/wasmq.rs" "$m/mutants.json"
(cd "$m/wt/wai/wai-quantum-cli" && CARGO_TARGET_DIR="$m/target" cargo build --locked --release)
python3 -I wai/quantum-conformance/tools/wasmq_mutants.py rust --reference "$m/target/release/wai-quantum" --mutants "$m/mutants.json" --jobs 4
git worktree remove --force "$m/wt"
# The release binary, pinned by version and SHA-256, rather than a build from source.
# Every WAI workflow that runs wasmtime pins this one version, and every run passes
# -W nan-canonicalization=y -W relaxed-simd-deterministic=y: every NaN a float
# instruction makes is the canonical one, and each relaxed SIMD instruction has one
# behaviour on every host. A stock wasmtime leaves both off. The fixed relaxed
# behaviour is the engine's own: on 50.0.1 relaxed_madd is fused (measured on arm64 and
# x86_64), where WebAssembly's deterministic profile prescribes the unfused result.
# The repository checks (scripts/check-wasmtime-pin.py) fail a workflow that pins another
# release or runs wasmtime without both settings.
- name: Install wasmtime 50.0.1
shell: bash
run: |
set -euo pipefail
[ "$(uname -m)" = x86_64 ] || { echo "::error::the pinned wasmtime archive is x86_64; this host is $(uname -m)"; exit 1; }
if ! command -v xz >/dev/null 2>&1; then
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq xz-utils
fi
v=50.0.1
f="wasmtime-v$v-x86_64-linux.tar.xz"
curl --proto '=https' --tlsv1.2 -sSfL -o "$RUNNER_TEMP/$f" \
"https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f"
echo "685466455dd42a14774a86c36c8f2e45cbb9bd6d92a850139315df60a75498a5 $RUNNER_TEMP/$f" | sha256sum -c -
tar -xJf "$RUNNER_TEMP/$f" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/wasmtime-v$v-x86_64-linux" >> "$GITHUB_PATH"
# The same source as a WASI component: a circuit's state hash, a run's
# result and a receipt signed with the published key are the native
# build's, byte for byte.
- name: The WASI component gives the native build's bytes
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
cargo build --locked --release --target wasm32-wasip2 --no-default-features
w=target/wasm32-wasip2/release/wai-quantum.wasm
n=target/release/wai-quantum
printf 'OPENQASM 3.0; qubit[3] q; h q[0]; cx q[0], q[1]; t q[2]; cx q[1], q[2];' > "$RUNNER_TEMP/p.qasm"
for args in "lab run ghz -n 5" "lab receipt ghz -n 3 --demo-key"; do
$n $args > "$RUNNER_TEMP/native.txt" 2> /dev/null
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y "$w" $args > "$RUNNER_TEMP/wasi.txt" 2> /dev/null
cmp "$RUNNER_TEMP/native.txt" "$RUNNER_TEMP/wasi.txt" || { echo "::error::$args differs under wasmtime"; diff "$RUNNER_TEMP/native.txt" "$RUNNER_TEMP/wasi.txt" || true; exit 1; }
done
$n run --demo-key --json --shots 100 < "$RUNNER_TEMP/p.qasm" 2> /dev/null | python3 -c 'import json,sys; j=json.load(sys.stdin); j["result"].pop("elapsed_ms"); j["result"]["receipt"].pop("digest"); j["result"]["receipt"].pop("signature"); print(json.dumps(j, sort_keys=True))' > "$RUNNER_TEMP/native.json"
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y "$w" run --demo-key --json --shots 100 < "$RUNNER_TEMP/p.qasm" 2> /dev/null | python3 -c 'import json,sys; j=json.load(sys.stdin); j["result"].pop("elapsed_ms"); j["result"]["receipt"].pop("digest"); j["result"]["receipt"].pop("signature"); print(json.dumps(j, sort_keys=True))' > "$RUNNER_TEMP/wasi.json"
cmp "$RUNNER_TEMP/native.json" "$RUNNER_TEMP/wasi.json" || { echo "::error::a run's result differs under wasmtime"; exit 1; }
# With a home it can reach, a run still gives its result, and keeps nothing:
# a wasm build reads no counter and writes no timings.
mkdir -p "$RUNNER_TEMP/wasi-home"
for e in HOME=/h WAI_QUANTUM_TIMINGS=/h/timings.json; do
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y --env "$e" --dir "$RUNNER_TEMP/wasi-home::/h" "$w" run --demo-key --json --shots 100 < "$RUNNER_TEMP/p.qasm" 2> /dev/null | python3 -c 'import json,sys; assert sum(json.load(sys.stdin)["result"]["counts"].values())==100'
done
test -z "$(find "$RUNNER_TEMP/wasi-home" -type f)" || { echo "::error::a wasm build kept a file"; exit 1; }
# A compile to Clifford+T (rotation synthesis in exact and fixed-point
# arithmetic, the passes, the certificate and its seal): byte for byte.
printf 'OPENQASM 3.0; qubit[2] q; h q[0]; rz(0.3) q[0]; cx q[0], q[1]; ry(1.1) q[1]; cp(0.7) q[0], q[1];' > "$RUNNER_TEMP/angles.qasm"
$n compile --demo-key --json --level 2 --epsilon 1e-8 < "$RUNNER_TEMP/angles.qasm" > "$RUNNER_TEMP/native-compile.json" 2> /dev/null
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y "$w" compile --demo-key --json --level 2 --epsilon 1e-8 < "$RUNNER_TEMP/angles.qasm" > "$RUNNER_TEMP/wasi-compile.json" 2> /dev/null
cmp "$RUNNER_TEMP/native-compile.json" "$RUNNER_TEMP/wasi-compile.json" || { echo "::error::a compile differs under wasmtime"; exit 1; }
# A run on a device target: the layout search, the noise derived and
# the density matrix, the same bits.
for t in synthetic-line-5 synthetic-all-5; do
$n run --demo-key --json --shots 100 --target $t < "$RUNNER_TEMP/p.qasm" 2> /dev/null | python3 -c 'import json,sys; j=json.load(sys.stdin); j["result"]["receipt"].pop("digest"); j["result"]["receipt"].pop("signature"); print(json.dumps(j, sort_keys=True))' > "$RUNNER_TEMP/native-$t.json"
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y "$w" run --demo-key --json --shots 100 --target $t < "$RUNNER_TEMP/p.qasm" 2> /dev/null | python3 -c 'import json,sys; j=json.load(sys.stdin); j["result"]["receipt"].pop("digest"); j["result"]["receipt"].pop("signature"); print(json.dumps(j, sort_keys=True))' > "$RUNNER_TEMP/wasi-$t.json"
cmp "$RUNNER_TEMP/native-$t.json" "$RUNNER_TEMP/wasi-$t.json" || { echo "::error::a run on $t differs under wasmtime"; exit 1; }
done
# A resource estimate (the counts, each rotation's synthesis, the
# planar model's factory search and the frontier, every receipt and
# the seal): byte for byte.
printf 'OPENQASM 3.0; qubit[4] q; h q; ctrl(3) @ x q[0], q[1], q[2], q[3]; rz(0.3) q[0]; ry(1.1) q[2]; t q[1];' > "$RUNNER_TEMP/est.qasm"
$n estimate --demo-key --json --qubit us-e3 < "$RUNNER_TEMP/est.qasm" > "$RUNNER_TEMP/native-estimate.json" 2> /dev/null
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y "$w" estimate --demo-key --json --qubit us-e3 < "$RUNNER_TEMP/est.qasm" > "$RUNNER_TEMP/wasi-estimate.json" 2> /dev/null
cmp "$RUNNER_TEMP/native-estimate.json" "$RUNNER_TEMP/wasi-estimate.json" || { echo "::error::an estimate differs under wasmtime"; exit 1; }
echo "the WASI component's output equals the native build's"
# verify gives its verdict at its own bound under wasm32 as natively: a job of 384 MiB,
# one of 2^22 values, and one more of each (too_large).
- name: The WASI component judges a job at verify's bound
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
w=target/wasm32-wasip2/release/wai-quantum.wasm
n=target/release/wai-quantum
d=$(mktemp -d "$RUNNER_TEMP/bound.XXXX")
python3 -I tests/verify_at_bound.py "$d"
for f in long longer many more; do
$n verify --json "$d/$f.json" > "$d/native-$f.txt" || true
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y --dir "$d" "$w" verify --json "$d/$f.json" > "$d/wasi-$f.txt" || true
test -s "$d/native-$f.txt" || { echo "::error::no verdict at verify's bound ($f)"; exit 1; }
cmp "$d/native-$f.txt" "$d/wasi-$f.txt" || { echo "::error::verify at its bound differs under wasmtime ($f)"; exit 1; }
done
rm -rf "$d"
cli-macos:
name: command line · macos
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo on the macOS host runner.
- name: Put cargo on PATH
shell: bash
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Tests and clippy, with the GPU state vector
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
XDG_CONFIG_HOME="$RUNNER_TEMP/no-timings" nice -n 10 cargo test --locked --release --features metal 2>&1 | tee "$RUNNER_TEMP/test.log"
test ! -e "$RUNNER_TEMP/no-timings/wai/quantum-timings.json" || { echo "::error::a test wrote the timings of whoever runs it"; exit 1; }
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
nice -n 10 cargo clippy --locked --release --all-targets --features metal -- -D warnings
- name: The smoke checks, and a run on the GPU
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
nice -n 10 cargo build --locked --release --features metal
bash smoke.sh ./target/release/wai-quantum
printf 'OPENQASM 3.0; qubit[12] q; bit[12] c; h q; rx(0.3) q[0]; cx q[0], q[1]; c = measure q;' > "$RUNNER_TEMP/gpu.qasm"
./target/release/wai-quantum run "$RUNNER_TEMP/gpu.qasm" --method statevector_single --shots 200 --demo-key --json > "$RUNNER_TEMP/gpu.json" 2> /dev/null
python3 -c 'import json,sys; j=json.load(open(sys.argv[1])); r=j["result"]; assert r["accuracy"]["class"]=="single" and sum(r["counts"].values())==200, r' "$RUNNER_TEMP/gpu.json"
echo "statevector_single ran on $(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["result"]["accuracy"]["device"])' "$RUNNER_TEMP/gpu.json")"