All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
WAI energy receipts / does this change touch it (pull_request) Successful in 7s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 10s
wai-quantum command line / does this change touch it (pull_request) Successful in 9s
wai-quantum contract / does this change touch it (pull_request) Successful in 8s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 8s
wai-quantum editors / does this change touch it (pull_request) Successful in 7s
wai-quantum for Python / does this change touch it (pull_request) Successful in 7s
wai-quantum service / does this change touch it (pull_request) Successful in 8s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 8s
wai-quantum views in agent hosts / does this change touch it (pull_request) Successful in 7s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 10m19s
wai-quantum service / service energy · linux packages (pull_request) Successful in 1m39s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m37s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 39s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 2m39s
wai-quantum contract / the policy is linked, and the deprecations page reads (pull_request) Successful in 6s
wai-quantum command line / command line · linux (pull_request) Successful in 3m22s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 13s
wai-quantum contract / API compatibility · linux (pull_request) Successful in 4m36s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 2m16s
wai-quantum for Python / python · linux (pull_request) Successful in 3m13s
wai-quantum contract / schemas and the committed tool lists · linux (pull_request) Successful in 6m46s
wai-quantum editors / editors · linux (pull_request) Successful in 5m25s
wai-quantum service / service · linux (pull_request) Successful in 4m49s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 4m11s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 3m11s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m41s
wai-quantum command line / command line · macos (pull_request) Successful in 1m28s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 3m32s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m57s
wai-quantum for Python / python · macos (pull_request) Successful in 2m4s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 14m46s
wai-quantum service / service · macos (pull_request) Successful in 3m46s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m45s
wai-quantum editors / editors · macos (pull_request) Successful in 6m36s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 15m28s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 20m4s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 13m3s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 29m23s
A client that speaks MCP 2025-11-25 can run run_on_provider as a task. initialize declares tasks (list, cancel, tools/call) from that version on, and run_on_provider declares execution.taskSupport "optional". tools/call with task records and sends the job and answers once the provider has taken it, the job's handle its taskId; a run that is not free and not confirmed makes no task and sends nothing, its plan in the error's data. Every job recorded here is a task, whatever sent it. tasks/get asks the provider once; tasks/list reads the records and asks no one; tasks/cancel cancels at the provider and refuses a task that has ended. The ttl is null, as records are kept. tasks/result asked before the job has ended is answered when it ends, by a thread of its own asking at 2 s doubling to a minute, so the stdio server answers everything else meanwhile. Replies go through one shared writer. A tasks/cancel here wakes the wait at once; a request the client cancels (notifications/cancelled) is never answered; at most 64 wait at once. A process-wide lock keeps a tool call and a wait from both sealing one job. Tested against the reference provider over HTTP: the result of a short queue verifies; ping answers while a long one's result waits; a cancel ends that wait within 3 s once it asks every 8 s; a cancelled request gets no reply; tasks outlive the session. The CLI workflow names the new tests, so a run that skips one fails. Co-Authored-By: Claude Opus 5.5 <[email protected]>
203 lines
12 KiB
YAML
203 lines
12 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.
|
|
|
|
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-qpu/**'
|
|
- 'wai/quantum-conformance/eval/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-cli.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/wai-quantum-cli/**'
|
|
- 'wai/wai-quantum-qpu/**'
|
|
- 'wai/quantum-conformance/eval/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-cli.yml'
|
|
|
|
jobs:
|
|
relevant:
|
|
name: does this change touch it
|
|
runs-on: linux
|
|
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.98.0 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.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal --component clippy
|
|
rustup default 1.98.0
|
|
rustup target add --toolchain 1.98.0 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
|
|
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; }
|
|
# 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 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; 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
|
|
- 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 release binary, pinned by version and SHA-256, rather than a build from source.
|
|
- name: Install wasmtime 49.0.2
|
|
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=49.0.2
|
|
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 "a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 $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" $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" 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; }
|
|
# 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" 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" 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" 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"
|
|
|
|
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
|
|
nice -n 10 cargo test --locked --release --features metal 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; }
|
|
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")"
|