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]>
8.8 KiB
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. Itswasmq_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.