The sign-off loop (check, grade, attest;4b17804c2) ran on the command line and its MCP server. It now runs wherever the engine does, with the same answers. - engine::front holds the check, the attestation and the loop's MCP tools (check_quantum_program, grade_quantum_program, attest_quantum_job, in one wording with the sealer named), which the command line, both MCP servers, the Python package and the npm package call; the command line keeps its flags and its printing. The output schemas of check and attest move from the command line's local-tools.v1.json into the engine's tools.v1.json ($defs/check, $defs/attest), which both servers name. - The job service serves the three tools on its MCP server and as POST /v1/check, /v1/grade and /v1/attest (GET /v1, the OpenAPI document and the README list them), a grade and a sign-off sealed with its own key, their energy unmetered with the reason. Each is counted as a check, held to its bounds and made in a reading child, each case within half the child's memory. A check of a task, a grade, and a sealed grade POST /v1/verify works out again are now held case by case (analysis::grade_bounds: each case's intent that compares the program with a reference held to the equivalence check's bound with the program written out at that case's inputs, and the task's job read as a job's is), where before a grade was remade within the reading child's budget whole; a case past them is refused at that case (J031, "case N: ...", naming wai-quantum grade). An attestation holds what verifying the job works out again and the intent its sign-off checks to the same bounds (naming wai-quantum attest). - Python: wq.check, wq.grade, wq.attest (and _engine.check/grade/attest); npm: QuantumEngine.check, .grade, .attest; each with a test (test_engine.py, smoke.mjs, a wasm32 test in wai-web) and a README example. - The instructions both MCP servers give at initialize describe the loop (guide::LOOP), and the committed lists (docs/local-mcp.json, docs/service-mcp.json), llms.txt, llms-full.txt and the skill's tools.md are written again from the live lists. - A whole task set graded: wai-quantum grade --set FILE --programs ANSWERS (the model harness's set; the programs a JSON object of programs or of a model's answers as eval run --scripted reads them, or a directory of NAME.qasm files), or --course --programs DIR; engine::grade::batch, wai.quantum.grades/1 (schemas/grades.v1.json), quantum-providers §8.10 "A task set graded": one sealed grade a task, missing and refused tasks said so, the set's digest and a summary. The batch is not sealed; wai-quantum verify works it out again (grade::check_batch): each grade checked and made again, each entry's name and grade held to what its grade seals, a refused entry refused again, the digest and the summary made again from the sealed results. The same set, programs and key give the same bytes. quantum-conformance/grades/ holds a batch and six changed ones with what a reader must say (written by hand from §8.10), natively and under wasm32-wasip2 (tests/grades_conformance.rs), including one that shows what a batch does not: a grade dropped as missing with the summary made to agree is valid; that a task had no program is the grader's statement. - wai-web's pinned digest of a level-2 compile (COMPILE_DIGEST) follows the compile of7c00ef138, which writes each region of CNOT and phase gates again at level 2 and so changed those sealed bytes; wai-web wasm32 runs on pull requests and nightly only, so nothing had run it since. Native and wasm32 give the new digest alike. The npm smoke runs the loop on an engine of 1 GiB: one of 256 MiB says an intent's exact check is unchecked, and why (asserted too). - Which trust list an attester used stays outside the seal: sealing it would add a member to the sign-off's closed request, which every released verifier refuses (J001), and it would show only which keys the attester trusted, which is each reader's to decide; the changes entry and the skill say so. Tests: engine::front, engine::grade (the batch), the guide's instructions, service analysis (bounds case by case), mcp (an agent runs the loop over the service's server; every tool's answer held to its output schema), verify.rs (the loop over HTTP in a running service, its grade and sign-off verified), the command line's front_door.rs (a whole set graded through the binary, verified, a changed summary found) and check_schemas.py (the batch and an attestation held to their schemas). wai-quantum-verdict.yml, -service.yml, -cli.yml and wai-web-wasm.yml require the new tests by name. schema::FILES gains grades.v1.json (acknowledged in the changes entry). Co-Authored-By: Claude Opus 5.5 <[email protected]>
7.9 KiB
The sign-off loop: check, grade, attest
Writing a quantum program is the easy part. Saying it is right takes the engine. Three calls do that, and each answer names the layer that decided it and what that layer does not show. A classical checker decides only what it can simulate, so where no layer reaches a case, the answer is unchecked, never passed.
| Step | Command line | MCP tool | Sealed |
|---|---|---|---|
| Check, until it passes | wai-quantum check FILE --intent F (or --task T) |
check_quantum_program |
no: this is the inner loop |
| Grade what passed | wai-quantum grade FILE --task T |
grade_quantum_program |
yes: a job anyone can run again |
| Attest a job another host ran | wai-quantum attest JOB --trust KEYS |
attest_quantum_job |
yes: a sign-off with the user's key |
1. Check until it passes
check reads the program and reports its diagnostics, measures and smells. Where the reader refuses it, check lists the fixes the engine would make on the flagged lines and what needs a person. Given what the program must do, check runs each case and grades it passed, failed or unchecked, with the verdict and its layer. It also says what went wrong and where to look, in the words the course's learn why uses. next is the next thing to do.
printf '{"00": 0.5, "11": 0.5}\n' > bell-dist.json # what bell.qasm must do: its exact distribution
wai-quantum check bell.qasm --intent bell-dist.json # passes: exit 0
wai-quantum check bell.qasm --intent bell-dist.json --json > checked.json
wai-quantum check broken.qasm # exits 1
wai-quantum repair broken.qasm > fixed.qasm # the fixes check named
wai-quantum check fixed.qasm
Exit 0 means every case passes. Exit 1 means a case fails or the program does not read. Exit 2 means no layer decided a case. Change only what the answer points at, then check again. Never call a case passed that came back unchecked.
2. Grade what passed
A task says what the program must do, in the form the course keeps its exercises in. It gives an intent, or cases (each with name, inputs for a generator's integer inputs, and intent). It may also give a job (shots, seed, noise) and constraints (allow, counts, t_count, two_qubit, no_smells, estimate, qubits, inject). A task carries no reference, wrong or hints, because the grade's receipt seals the task whole.
cat > task.json <<'EOF'
{
"title": "A Bell pair",
"job": { "shots": 64, "seed": 5 },
"cases": [
{ "name": "distribution", "intent": { "distribution": { "00": 0.5, "11": 0.5 } } },
{ "name": "samples", "intent": { "sampled": { "00": 0.5, "11": 0.5 }, "test": "chi_squared", "alpha": 0.001, "shots": 400 } }
],
"constraints": { "allow": ["h", "cx"], "two_qubit": { "max": 1 } }
}
EOF
wai-quantum grade bell.qasm --task task.json --json > grade.json
wai-quantum verify grade.json # the grade, worked out again
grade is the same check, sealed as a job (wai.quantum.grade/1). The request holds the task and the program, so anyone can run the grade again, and with the same key they get the same bytes. verify works it out again: each case's checks are decided again, each case's grade is held to the checks it rests on, and each constraint is measured again. A grade that a host sealed but its job does not bear out fails verification, though its seal holds.
A task can also come from the course (--lesson ID, its solution left out of what is sealed) or from the model harness's task set (--set tasks.json --name NAME):
wai-quantum grade bell.qasm --set tasks.json --name bell
Quote each case's grade and layer. A case decided by samples or random_states is evidence, not proof.
3. Attest a job another host ran
A job from elsewhere, such as the job service, a person's provider or another machine, carries that host's seal. attest first verifies the job as verify does: its receipt, every rule of its form, and any verdict or compile it seals, worked out again. With --trust, it holds the job's key to a trust list, and with --at, to a time. A job that fails any of these is refused, and nothing is sealed. Then attest checks what the job's answer must do (--intent) against it, and signs it off with the user's key (wai.quantum.signoff/1). Beside the sign-off, outside its seal, attested says what the job was checked against.
Here a job sealed with the published demo key stands in for another host's. The trust list names that key, as the job service publishes its own at /.well-known/wai-quantum-receipt-keys:
wai-quantum run bell.qasm --shots 100 --demo-key --json > theirs.json
cat > keys.json <<'EOF'
{ "format": "wai.quantum.receipt-keys/1", "algorithm": "ed25519",
"keys": [{ "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61" }] }
EOF
printf '{"outcomes": ["00", "11"]}\n' > outcomes.json
wai-quantum attest theirs.json --trust keys.json --intent outcomes.json --json > attested.json
wai-quantum verify attested.json
Attest only what the person asked you to attest. A sign-off puts the person's key on the verdict. attested is not sealed: it says what this attester held the job to, and a reader holds the job to their own trust list when they verify it (the sign-off's request is closed, so a verifier released before a sealed list would refuse every sign-off that carried one).
4. Grade a whole task set
With a task set and a program for each task, grade grades them all at once: one sealed grade a task, and a summary (wai.quantum.grades/1). The set is the model harness's (--set tasks.json, as eval run --tasks reads it) or the course's (--course, each lesson's exercise). The programs come from --programs: a JSON object from each task's name to its program, or to a model's answer with the program in a fenced block (what eval run --scripted reads), or a directory of NAME.qasm files. A task with no program is listed as missing, not graded:
cat > answers.json <<'EOF'
{ "bell": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" }
EOF
wai-quantum grade --set tasks.json --programs answers.json --json > grades.json # exits 1: only bell has a program; the rest are missing
wai-quantum verify grades.json # each grade made again, the set and the summary worked out again
The batch itself is not sealed; each grade in it is. verify checks each grade's receipt and makes the grade again, holds each entry's name and grade to what its grade seals, and works the set's digest and the summary out again from the entries. The same set, programs and key give the same bytes. A task listed as missing is the grader's word: to be sure it had no program, grade it again.
Where the loop runs
The same three calls, with the same answers, run on every surface:
| Surface | Check | Grade | Attest |
|---|---|---|---|
| Command line | wai-quantum check |
wai-quantum grade |
wai-quantum attest |
Local MCP server (wai-quantum mcp) |
check_quantum_program |
grade_quantum_program, sealed with this user's key |
attest_quantum_job, sealed with this user's key |
| Job service | the same tools on its MCP server, and POST /v1/check |
POST /v1/grade, sealed with the service's key |
POST /v1/attest, sealed with the service's key |
Python (wai_quantum) |
wq.check(program, intent) |
wq.grade(program, task, key=...) |
wq.attest(job, intent, trust=...) |
npm (@transaction-science/wai-quantum) |
engine.check(args) |
engine.grade(request) |
engine.attest(args) |
The job service holds each case to its bounds before any case runs (a reference compared with a wide case of a generator, for one) and refuses one past them, naming the local command: a check that takes long belongs on this machine.