open-standards/wai/quantum-conformance/receipts/README.md
Transaction Science 2778ddd525 wai-quantum: the planner under a contract, its reasoning sealed, a provider's answer shadowed
Re-landed from PR #668 (branch wai/quantum-planner-contract, merged with main
495651ddd at 37350335b) as one commit on main, under the land-it model. Five
review rounds; round 5's findings (two high: workers that died as PID 1) were
fixed on the branch, and its CI reds were traced and fixed before landing.

A job may carry a contract (deadline, energy ceiling, cost, accuracy); the
planner chooses a route that meets it or refuses with every route's reason,
and its reasoning is sealed in the receipt. Under #676's reading child the
contract job is planned in the child from the per-method timings the parent
sends, bounded as a job naming its chosen method plus its reasoning, kept
across a restart. Both verifiers (Rust and receipt_verify.py) check the
planner statement beside wasmQ's; the receipts corpus is regenerated with
both kinds (268 crafted receipts, 14 plants caught). The views page carries
its engine raw-deflated and stays under its 1 MiB limit (669,484 bytes).

Passed before the push (Framework, linux; M3, macOS): every repository
check, receipts conformance, breaking, corpora, catalogue, the qpu clients,
energy (wai-rs, wai-quantum full, serve), the CLI tests and clippy, writer,
smoke, cross-simulator corpus, job receipts, wasmQ tests and vectors, the
engine with and without receipts under -D warnings, the docs pages, and the
service suite with clippy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:42:46 -05:00

8.8 KiB
Raw Permalink Blame History

Job receipts

A job's receipt, as the provider protocol's §8 states it, judged by two verifiers that share no code. Each must give every receipt here exactly the reasons the manifest names (§8.4): none for a valid one.

file what
manifest.json every receipt in jobs/, the reasons a verifier must give it, how it was made, and what it is. Every expectation is written by hand from §8.4; none is copied from a verifier's output
jobs/cli-*.json made by the command line on this machine, sealed with the published demo key (make.sh): runs by several methods, a compile, runs on the reference provider over a local process, free and priced, and jobs under a contract (§8.7), here, routed to the person's providers and on a provider named under a ceiling
jobs/cli-wasmq-*.json wasmQ jobs (quantum-wasmq) made by the command line from the profile's own envelopes (../wasmq/envelopes/) with the demo key (make.sh): a run of the surface-code round, one of a single instance for the whole run, and faults in a call, of the run's budget, in an instance's init and in a prior
jobs/service-*.json made by the free job service, sealed with its key (make.sh)
jobs/scripted-*.json provider jobs of every built-in kind (sampler-v2, prx-circuits, native-turns with and without a noise model, cloud-tasks on a processor and on its simulator, with rows of shots and with probabilities), each through a service that follows that provider's published contract, sealed with the demo key (wai-quantum-qpu/tests/clients.rs, every_built_in_kind_states_what_it_sent_and_its_receipt_keeps_every_rule, with WAI_QUANTUM_RECEIPT_CORPUS set to this directory); and prx-circuits under a contract, its device's counts with a classical method's beside them (a_built_in_kind_runs_under_a_contract_and_a_classical_method_shadows_it)
jobs/first-form-*.json sealed by the job service as it was on 18 October 2026 (commit 72d18742d), so by the first form's own writer, not by a restatement of it
jobs/fixture-*.json the engine's pinned fixtures, sealed by earlier engines: a run before results named their engine, a provider job of the earlier form, and a compile
jobs/crafted-*.json made by generate.py from the receipts above, each changed one way: the seal's form, the strict signature (a weak-key forgery, S + ℓ, a mixed-order key, …), the first form, what any JSON reader and writer may do (and so must change nothing), the strict reader, and each rule of a provider's result and of a wasmQ job's form, each sealed again with the demo key where the change is inside the seal so that one rule alone breaks

The made receipts are kept as they were made, each pinned by its digest in made.lock.json: a receipt missing or changed fails generate.py --check. A receipt made again differs in its times (and, from the service, perhaps its key) and verifies all the same, so none is made again to pass a test. A row of §8.3 that a client stops writing is held by the receipt that has it.

The two verifiers

  • wai-quantum verify --json FILE (and --lines FILE, one job a line): the reference, wai_quantum::engine::job::check.
  • ../tools/receipt_verify.py: written in Python from §8 alone, by a reader who had not seen the reference. Its wasmq_form, a wasmQ job's form, was written the same way from quantum-wasmq §6.5, whose rules are numbered so that each can be read alone. Standard library only: its BLAKE3, Ed25519, RFC 8785 text and strict reader are its own, each held to published answers by --self-test.

../tools/receipt_differential.py holds them to each other:

python3 -I receipts/generate.py --check                       # the crafted half is as written
python3 -I tools/receipt_verify.py --self-test ../wai-quantum/src/engine/testdata/es-number-tostring.txt
python3 -I tools/receipt_differential.py --reference PATH/wai-quantum
python3 -I tools/receipt_differential.py --reference PATH/wai-quantum --plant lenient-key
python3 -I tools/receipt_differential.py --second-only        # no build: the second verifier and the manifest

The differential requires, for every receipt here, that both verifiers and the manifest name the same reasons, and that both verifiers state the same key, form, class read by provenance and counts' source. Then it changes every valid receipt one member at a time (each member of its request, its result, its receipt and its unsealed envelope, removed, and its value replaced) and requires the same reasons from both for every change, and two oracles beside them: a change inside the request, the result or the receipt is never valid, and a change to the unsealed envelope, or a member added to the receipt, never changes the verdict.

A change of one member is caught by the seal before any rule of a job's form is read, so it cannot hold the two verifiers to those rules. The rules stage does: for each rule of a wasmQ job's form (quantum-wasmq §6.5), the genuine wasmQ jobs changed at the rule's bound, on both sides of it where it has two, and sealed again with the demo key, each with the verdict written for it by hand from the text (rule_cases in the differential). Both verifiers must give every case its written verdict, so a rule dropped or a bound moved in either one fails: this stage, and the manifest, are what back "the two verifiers agree" on the form's rules. --second-only runs it for the second verifier. Each crafted wasmQ receipt that names one rule breaks that rule alone: the reference states one problem for it, and the second verifier that rule.

The labelled stage gives both verifiers the second verifier's own hand-labelled wasmQ cases (receipt_verify.py --wasmq-cases), each as its exact text: the labels its author wrote from the text bind the reference as they bind the second verifier, so one labelled corpus holds both.

../tools/wasmq_mutants.py weakens each rule in each verifier, from the code (each check dropped, each comparison moved by one, a count or a digest read loosely, a null member read as absent and an absent one as null, the most of many read as the least), and requires the differential (--wasmq-only --first) to catch every weakening, counted per rule:

python3 -I tools/wasmq_mutants.py python --reference PATH/wai-quantum
python3 -I tools/wasmq_mutants.py rust-schemata ../wai-quantum/src/engine/wasmq.rs mutants.json  # in a scratch checkout
# build the command line from that checkout, then
python3 -I tools/wasmq_mutants.py rust --reference PATH/wai-quantum --mutants mutants.json

It also changes each number of the provider receipts to values past a double's whole numbers, and sets a job's shots in both its request and its result, to see a limit one verifier has and the other does not; and it seals those of the demo key again, so that each is judged by what it says and not only by its digest.

--plant NAME runs the second verifier with a defect planted in it: a lenient key check, alg unchecked, the last of a member named twice 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, and under a contract (§8.7) each route's outcome, the planner's choice or a shadow's distance taken as it stands. The differential succeeds only when it catches the plant. That is the proof that it can fail.

--extra DIR adds receipts a person holds and does not publish: both verifiers must find each valid. A receipt from a cloud account's task names that account's number in its job id, and the receipt seals it, so the corpus holds none: the shape of such a receipt is here (scripted-cloud-tasks-*), and real ones are checked with --extra.

CI runs the whole differential beside the build of the command line (.github/workflows/wai-quantum-cli.yml, job cli-linux), and the second verifier against the manifest, with its plants, among the independent verifiers (.github/wai-conformance/verifiers/quantum-receipts.sh).

Making it again

bash receipts/make.sh PATH/wai-quantum receipts/jobs          # the command line's and the service's
(cd ../wai-quantum-qpu && WAI_QUANTUM_RECEIPT_CORPUS=$PWD/../quantum-conformance/receipts/jobs \
  cargo test --test clients -- every_built_in_kind a_built_in_kind_runs_under_a_contract) # the built-in kinds'
python3 -I receipts/generate.py                                 # the crafted half and the manifest

Making them again replaces receipts a release already judged, and --check then fails: add receipts under new names, and keep the ones there. A new name is added to MADE in generate.py; generate.py --pin NAME records its digest, and refuses a name it holds.