wai-quantum: no anonymous request ends the job service or holds it without limit #676

Closed
dcharlot wants to merge 27 commits from wai/quantum-parse-depth into main
Owner

Re-landed on main as one commit, ce1c91131 (2026-11-09), under the land-it model. Round 6's 16 findings are fixed, each with a fail-first test (b35ae093e). main f6e302665 was merged at 0cf954bfc (local), with wasmQ's verify surfaces brought under the same admission, turns and reading child, and wasmQ's profile stating this reader's nesting bound (Q006). Passed on the merged tree, linux and macOS: the service suite under ulimit -Sn 256, the library's 1,458 tests, the CLI, wai-wasmq and its vectors, clippy, wai breaking, and the repository checks. Main's CI runs on the push. Closed.


What was wrong

  • The OpenQASM parser recursed once per ( (about thirteen frames a level), once per statement in a block, once per prefix operator, with no limit, and the elaborator recursed over whatever tree the parser built (a chain such as 0 + 0 + …, read in a loop, built a tree as deep as it was long). The job service parsed on a tokio worker (2 MiB stack) before any rate limit, so a POST of about 1.3 KB with some 600 nested parentheses in qasm, or in an intent's reference, aborted the whole process.
  • A job's intent references were read three or four times each (intent_bounds, equivalence_work, catalog::plan, with_intent) before the count, size and rate checks, and refused requests were never counted, so a few small requests with heavy references held every worker for minutes.

What is bounded, and where

Reader What recursed Bound Refusal
qasm::parse statements in blocks and bodies, if/for/while/switch/box, gate and def bodies, parentheses, prefix operators, **, binary chains, index chains, ++, calls, casts, sets, types (complex[, array[), durationof, measure MAX_NESTING = 64 levels in the text: recursion depth and each expression tree's height together Q006 (new)
qasm::elab statements, expressions, conditions, tangents, gate expansion, subroutine calls 256 levels in all (MAX_NEST), beside the existing 32 for calls and gate definitions (Q209) Q006
qasm::instantiate (unroll) integer expressions ((, prefix, **), nested for/if written out, brace-less statement scans 64; a flat sum and an else if chain are loops, at any length Q006
pulse reader calibration expressions (it borrows the parser) 64, through the parser's own counter Q006
QEC circuit and DEM text REPEAT/repeat blocks; their lowering and model walk 32 (MAX_BLOCK_DEPTH) on the block's line (J009 in the service)
frame lowering / DEM walk a block repeated 2⁶⁴−1 times that writes nothing ran forever; nested REPEAT 0 cloned the circuit at each level every pass counts toward the unroll bound; unrun blocks checked once on one copy refused
editor index (LSP, visual edit) statements 64; deeper statements passed over without recursion none (tolerant)
answer layer a statement's value through a chain of statements 32 (CHAIN_DEPTH) error
flat runs (a + b - c, &&, ` ), else if` chains, a switch's case values on measured bits a tree as deep as the run is long
array rank a comma-separated list of dimensions, each a level of the value 7 (MAX_ARRAY_DIMS) Q006
QEC text indices a vector as long as an index in a few bytes 2²⁴ qubits and detectors (MAX_QUBITS, MAX_DETECTORS), offsets counted on the line (J009 in the service)
dynamic expectation walk one recursion per two-outcome measurement a list, same leaf order (same bits) MAX_LEAVES as before

Modifier chains (inv @ inv @ …) and include lists are iterative and read at any length; a test holds that.

Frame sizes were measured on the Framework (Linux x86_64, release and debug, threads of 1 and 2 MiB) with a temporary probe, the limits lifted for it.

  • On main, on 2 MiB: release overflowed past 304 parentheses, 466 blocks and 1759 chain links; debug past 56 parentheses and 43 blocks.
  • To make 64 levels safe with room in debug, the parser now reads binary operators by precedence climbing (one frame a level of nesting rather than one a precedence level) and each statement form in a function of its own, and the elaborator splits exec and ev the same way.
  • Measured after those splits: release on 1 MiB read at least 380 levels of every construct probed; debug on 2 MiB at least 113 (parentheses 126, blocks 306). Through 30 nested calls, release on 2 MiB held 58 levels of if per body, about 1 800 levels of the elaborator.
  • atom was split the same way after that measurement, so these figures are from just before it and were not taken again. The tests below ran on the final code.

Main's own nesting bound

Main took a nesting bound of its own for this reader in 15ae7d457 (Q130 at 128 levels, a chain of at most 127 operators, at most 1024 else if arms), after this branch last merged it. The merge of main at 1929b8436 keeps this branch's reader (its sources, its diagnostics table and its docs), which bounds nesting and memory already, and holds main's tests of that bound to it: lang_tests::nesting_is_bounded_as_the_text_counts_it reads each shape inside the bound and refuses it past (Q006), a run of 10,000 of one operator and ladders of 1024 and 5000 arms read, and a chain through calls past 256 is refused; tests/reader_stack.rs reads every program at the bounds on 1 MiB and 2 MiB threads in release (16 MiB in debug) and refuses the same shapes nested 5000 deep on them; the service's tests/verify.rs reads the 2000-term sum and refuses 1000 brackets and 1000 blocks on seven routes. Main's changelog entry says what holds (no release carried its Q130).

The budget primitive and #648

This adopts #648's qasm::within_steps as it is (signature, per-thread memo of readings with no inputs, inner budgets run within the outer one, Q312 past it), so main has one primitive. On top of it: gates written and a generator's loop passes spend from the budget too (statements alone let pow(k) @ or a sweep's unrolling run outside it), and qasm::steps_left() tells a budget refusal from a program's own (Some(0)). When #648 merges main, take main's qasm.rs and elab.rs hunks; within_steps(1_000_000, …) in its writer works unchanged, and steps_left() == Some(0) after a refused read is the test for "over the budget".

The service

No anonymous request ends the process, holds a thread without limit, or makes the service keep memory without limit, on any route. This is what each request now goes through. The defaults are in the README's settings table.

  • Arrival: every request is answered within WAI_QSVC_ANSWER_SECS (55) of its arrival, under a proxy's 60 s. A body is parsed only once its client's window has room. A resend under its key is parsed first, as it is answered before any limit. A large MCP message from a client past its window is refused unread.
  • Order by cost (http::admit_to):
    1. A resend under its key is matched as received, reading nothing, taking no turn and counting nothing.
    2. Refusals that read nothing are not counted: jobs at once, memory held, a full queue, a spent window.
    3. Then a turn, then the count, so a request told to try again has spent nothing; there is no refund.
    4. Then cheap_bounds, then every program read, each once, within 22,000,000 steps.
      Retry-After on a 429 is the window's pace.
  • In a child (offload.rs): admission, dry runs, every check's bounds and answer, the engine calls, a set's receipt and the MCP verdicts run in wai-quantum-service read, sandboxed as a worker is. Each child is capped:
    • memory, WAI_QSVC_READ_GB (4), polled every 50 ms from a thread of its own;
    • time, WAI_QSVC_READ_SECS (45), and the answer's deadline;
    • its answer, WAI_QSVC_READ_ANSWER_MIB (128) as parsed, about 80 bytes a value.
      It is killed by a signal to its process group, sent again until it ends. An abort, a stack overflow or a panic there is that request's 500, and a hang-up ends the child.
  • Turns (offload::Gate): four reading turns and four check turns. A client's share is 2 reading turns and 1 check turn (WAI_QSVC_CLIENT_READS, WAI_QSVC_CLIENT_CHECKS). Past it, one more turn is lent to the client while two stay free for others (in the checks; the reading pool lends none at a share of two), and none while one of the client's own turns is free or held by a request still waiting for the pool's. A lent turn goes back after 5 s once another client's request waits, or once one of its own client's has waited 7 s, so a client's quick calls pass its own slow ones, however many. Up to eight of a client's requests wait behind its turns (its eleventh at once is refused), 32 wait in all, each for 10 s. Past these the request gets 503 J032, saying whether its own requests or the pool held it up.
  • Budget: a check's answer reads inside the same budget as its bounds. A spent budget is a refusal whatever the code made of it. The QEC calls measure each point's setup and a pilot of its shots before running the rest. They are refused at once past WAI_QSVC_QEC_SECS (25) and stopped a fifth past it.
  • MCP: a tool that says nothing about a program takes no turn. A verdict reads only for a call that was counted, and only if time is left within the answer. A poll states what its job holds and reads nothing. A call refused before its count, told to try again, or out of time states a verdict giving the reason, so every answer keeps its schema.
  • What ended jobs keep: the request's canonical text, not its parse, and a set lets go of its items when it ends. Read results are held to WAI_QSVC_CLIENT_KEPT_MIB (128) per client, oldest first, and WAI_QSVC_KEPT_MIB (4096) in all, the largest holder's first; unread ones are dropped by neither, and hold their client's next job past WAI_QSVC_CLIENT_UNREAD_MIB (256) and everyone's past WAI_QSVC_UNREAD_MIB (8192), every job queued or running counted at its result's bound. A result is held to WAI_QSVC_RESULT_MIB (64) before its job runs, by a bound read from the request (below).
  • Long polls: a client holds at most WAI_QSVC_CLIENT_STREAMS (4) long polls and event streams; a long poll past them is answered 429 (503 past everyone's 512), J032, with Retry-After: 2 and the job's status.
  • Connections and restarts: http::serve holds a header section to WAI_QSVC_HEADER_SECS (150, above a proxy's 2-minute idle pool) and a body to WAI_QSVC_BODY_SECS (30, then 408). A restart reads no program. Readers and workers start from the service's own binary as it was started, so the file can be replaced before the restart.

Findings

All 33 of the #676 review, with the test that holds each. "Before" is the code before this round (26a001db6) with the test grafted on, on the iMac, unless the row says otherwise.

# Finding Fix Test Before
1 QEC index allocations MAX_DETECTORS, MAX_QUBITS at parse time, offsets counted an_index_past_what_a_text_holds_is_refused_on_its_line, an_index_past_what_a_model_holds_… fails; on e54 the 79-byte POST ended the process (review run)
2 refused MCP task read inline reads only if counted, in a child; J034 before the count reads nothing a_refused_mcp_task_reads_nothing_and_holds_no_worker fails: the program is read
3, 6 array memory bytes bound before anything is made (Q130), in the earlier commit of this PR programs_that_would_hold_without_limit_…, what_a_program_would_hold_… on e54 the run took the host's 64 GB and was killed
4 array rank MAX_ARRAY_DIMS = 7 in the parser an_array_past_its_dimensions_… fails
5, 7 verify of a set counted, in a child, in a budget; signature first, so a forged seal writes nothing out a_check_and_a_set_s_receipt_count_before_they_read, a_seal_that_does_not_hold_is_not_written_out both fail
8 budget refusal read as zero work within_budget refuses on a spent budget whatever read returned a_budget_spent_is_a_refusal_whatever_the_check_made_of_it fails
9 Blossom CPU QEC_MAX_FIRED = 1,024, and the child's deadline a_decode_is_held_to_the_detectors_it_is_given fails
10 reads hold pools killable child with a deadline (120 s in round 1, 45 s since round 2, not 300); size charged in steps; a client's share of the turns a_reading_past_its_time_is_stopped_and_a_hang_up_ends_it, reading_leaves_the_runtime_free_and_a_client_has_its_share, a_client_is_held_to_its_share_of_the_turns the deadline test passes on 26a001db6, which already had the child; the share test fails: three requests all waited
11 refund buys free reads no refund; a key is matched against the digest as received, before any count or read a_source_job_sent_again_under_its_key_reads_nothing_and_counts_nothing new API, not grafted; the review's run gave 17 free reads on e54
12 MCP reads uncounted Reading says what a verdict may read; a tool with no verdict takes no turn; polls read nothing a_call_that_was_not_counted_reads_nothing_and_still_states_a_verdict, the_tools_are_answered_with_no_turn_to_be_had, a_poll_reads_nothing the first fails; the others use new API
13 unbounded waiters at most 32 wait, a client's share first a_wait_for_a_turn_is_bounded_in_time_and_in_number new API; the review measured 300 waiters at 34 MB each
14 answer outside the budget bounds and answer in one budget a_check_s_answer_is_held_to_its_budget fails: no answer in the test's 120 s
15 Retry-After 5 refusals that read nothing are not counted; Retry-After is the window's pace a_client_told_to_retry_spends_nothing_… fails on the new header
16, 26 resend refused key lookup before turn and count, for source too a_source_job_sent_again_… (all turns held, window spent) new API; the review reproduced both on e54
17 bit-string panic Q112, and any panic is the child's a_bit_string_wider_than_an_integer_…, programs_that_would_hold_… panicked on e54
18 stuck connections, slow restart http::serve with header and body limits; a restart reads no program a_stuck_connection_is_let_go, a_restart_reads_no_program both fail
19 QIR and jeff conditions flat runs read at any length; MAX_COND_DEPTH 30, what the reader takes a_condition_that_fits_reads_back fails
20 flat chains settled: the limit was not needed, so E::Chain and S::If { arms } hold them flat, any length, on 1 MiB, same results a_run_of_operators_and_an_else_if_chain_read_at_any_length fails
21 switch case list Ct::All, Ct::Any, flat a_switch_on_measured_bits_lists_any_number_of_values aborts: stack overflow
22 recursion under MAX_NEST kept, as the skeptic advised; stated in the matrix, the changelog and below text not a code change
23 generator disagrees with reader flat runs read; the writer follows an else if chain in a loop both_readers_of_a_generator_take_a_flat_sum fails
24 answer memo order a result the limit cut short is kept by its depth, and a kept result carries its chain's length, so order changes nothing and shared references do not multiply the_chain_limit_refuses_the_same_statements_in_any_order fails
25 own program refused READ_STEPS = MAX_STEPS + MAX_OPS, 22,000,000; texts corrected the_budget_is_as_large_as_one_program_may_be, a_job_within_the_reader_s_bounds_reads_here both fail
27 no test of a generator's spend test added, and it covers a fat body too a_generator_s_passes_and_what_they_write_spend_the_budget passes; with the spend removed (M19) it fails
28 off-runtime and count order untested tests added reading_leaves_the_runtime_free_…, a_check_and_a_set_s_receipt_count_…, a_request_with_no_turn_is_refused_in_time_and_spends_nothing first two fail
29 Q312, J031, J032 rows reworded as additions; J020 too table test text
30 OpenAPI, README 503, 500, 422 and the bounds row; /v1/verify's refusals the_openapi_document_names_every_route text
31 changelog rewritten text
32 PR body this text text
33 tests that could only hang ends_within: a thread and a deadline, by name the two tests the review's mutants ran to the job's limit

Second review: the machinery (7 findings) and the correlated-error carrier

"Before" is 264cec659 (this branch with main merged, before these fixes), the new tests grafted on, run on the Framework (Linux x86_64).

# Finding Fix Test Before
1 one client held every check turn; at-bound QEC calls ran past the cap by more than 10x each pool has its own per-client share, smaller than the pool: WAI_QSVC_CLIENT_READS (2 of 4) and WAI_QSVC_CLIENT_CHECKS (1 of 4, up from a pool of 2). A client's requests past its share wait behind it, 4 at most, rather than being refused. QEC decode and threshold measure their shots as they run and are refused once the rest would take more than WAI_QSVC_QEC_SECS (25). Rounds are held to 21 one_client_cannot_hold_every_check_turn, a_qec_call_at_its_bounds_ends_well_inside_the_reading_s_time, a_time_holds_the_shots_sampled_and_decoded, a_memory_experiment_is_held_to_its_rounds, a_client_holds_no_more_of_a_pool_than_its_share the first fails (503 after 10.0 s); the second fails (422 at 120 s); the rest use new API
2 a reading's answer was rebuilt at the child's size in the service, on a runtime worker the child refuses an answer that would take more than WAI_QSVC_READ_ANSWER_MIB (128 MiB, down from 256 MiB of text) once read, value by value, before writing it; the service parses off its runtime and takes values rather than copying them; a dry run gets back plans and points only a_reading_s_answer_is_held_to_what_it_takes_once_read (the service's own peak memory checked on Linux) fails: the job is accepted, 202
3 the header timeout closed idle keep-alive connections at 30 s, before Caddy's 2 minutes WAI_QSVC_HEADER_SECS defaults to 150; self-host.md pairs it with the proxy's idle limit, and its example sets keepalive 60s the_defaults_fit_inside_the_proxy_s fails (30 s)
4 clients behind one address are refused at their third concurrent request per-pool shares, both configurable; requests past a share wait rather than being refused; the README and self-host.md say that one address is one client reading_leaves_the_runtime_free_and_a_client_has_its_share (five parallel calls from one client all answered) fails: 503
5 replacing the binary before the restart broke every reading readers and workers start from /proc/<pid>/exe on Linux, and on macOS from a copy kept beside the signing key, named by its digest; self-host.md gives the update procedure the_binary_can_be_replaced_before_the_restart (rename on Linux, in-place copy on macOS; a critique and a run both answered) fails: 503 J032 "could not start a reader"
6 the memory cap is a 50 ms poll of resident pages, run on the runtime the poll runs on a thread of its own (Watch) and reads the physical footprint on macOS (compressed pages included) and resident plus swap on Linux. On Linux a reader also caps its own address space at twice its cap plus 1 GiB (a failed allocation is answered 422), and readers and workers set oom_score_adj 1000. The README states exactly what a poll is and is not; macOS gives an unprivileged process neither kernel limit covered by the depth tests' memory cases; the thread itself has no test that fails first not grafted
7 the reading cap (120 s) was longer than Caddy's 60 s response_header_timeout WAI_QSVC_READ_SECS defaults to 45, so the cap plus the 10 s wait for a turn fits inside 60 s; documented with how to lower it behind a shorter proxy the_defaults_fit_inside_the_proxy_s fails (120 s)
#675 the correlated-error instruction adds one carrier qubit after the text's indices are checked lower() checks the circuit's width, carrier included, against MAX_QUBITS, and refuses at the first correlated error's line a_correlated_error_with_no_index_left_for_its_carrier_is_refused fails, with the check removed

The QEC timing was measured on Thor (Linux arm64, load 12 to 17 on 14 cores) against a local release build with the defaults:

Call Answer
threshold, surface, distances [7,7,7,7], p 0.04–0.055, 12,500 shots 422 in 1.0 s (projected 1,230 s)
decode, memory d=7 r=7 p=0.05, 200,000 shots 422 in 1.0 s (projected 3,473 s)
decode, memory d=7 r=21 p=0.01, 200,000 shots 422 in 1.0 s
threshold, surface d=7 ×4, p 0.007–0.01, 12,500 shots 422 in 1.0 s (projected 125 s)
decode, memory d=7 p=0.005, 200,000 shots 422 in 0.9 s (projected 91 s)
threshold with its defaults 200 in 10.2 s
decode, memory d=5 p=0.001, 200,000 shots 200 in 1.0 s

Not done: the macOS memory poll was not tested under memory pressure. Copies of earlier binaries beside the key are not removed automatically.

Third review (14 findings)

"Before" is 4f44194bf (the previous head) with the new tests grafted on, run on the Framework (Linux x86_64). Memory figures are the verifier's harness (skp_mem.py), on Thor (Linux arm64) and the M4 (macOS arm64).

# Finding Fix Test Before
1 ended jobs kept their parsed request for a day; parsing before the gate a job keeps its request's canonical text, and a set lets go of its items when it ends; what ended jobs keep is capped per client (WAI_QSVC_CLIENT_KEPT_MIB, 128) and in all (WAI_QSVC_KEPT_MIB, 2048), oldest first; bodies are parsed after the window check; job reports are spliced from the text; memory is given back after large requests; on macOS the service starts with MallocLargeCache=0; weight counts 80 B a value ended_jobs_keep_little_and_a_restart_keeps_it_little, ended_jobs_keep_their_request_as_text_within_their_caps fails: 1,072 MiB after 30 jobs
2 MCP runs passed Caddy's 60 s one deadline from arrival, WAI_QSVC_ANSWER_SECS (55): turns, readings and run waits end by it; an MCP verdict is read only if time is left; self-host.md gives each route's arithmetic a_run_s_answer_comes_within_the_answer_s_time, the_defaults_fit_inside_the_proxy_s fails: 202 after 25.1 s
3, 8, 13 a client's quick calls waited behind its own slow one; bursts of 6+ refused; misleading message one turn is lent past the share while two stay free, for 5 s; 8 may wait per client; the refusal says whether the client's own requests or the pool held it up a_client_s_quick_calls_pass_its_own_slow_one_and_its_burst_is_answered, a_client_holds_no_more_of_a_pool_than_its_share_and_a_quick_call_passes; one_client_cannot_hold_every_check_turn still holds fails: 503 J032
4, 7, 9 the QEC projection multiplied setup; a default ran 23 s before refusal; code capacity was untimed each point's setup and sampling are timed apart and a pilot of its shots (at most 1 %, up to 0.5 s) measures its rate; every point is measured before the rest run, so a call is refused at once or run; code-capacity points are piloted too; stopped a fifth past the time a_time_is_measured_apart_from_setup_and_holds_every_threshold, a_time_holds_the_shots_sampled_and_decoded fails: a call that fits refused at 0.6 s
5 WAI breaking gate the two Breaking lines; wai breaking --against e54f28102 --text --all exits 0 (and did against 5df2ccbde before main was merged) the gate exit 1
6 jobs::kill spawned kill -9 libc::kill on the child's process group (children get their own), again every 100 ms until it ends; job workers stopped the same way; a small answer is parsed inline, so no thread is needed a_reading_is_stopped_when_no_process_can_be_started (Linux, prlimit --nproc=1) fails: connection dropped
10–12, 14 text the critique refusal stated (200,000 gates refused, 150,000 answered); this body; OpenAPI statuses on every check route; the poll's empty critique in the changelog and README

Memory, one client, 30 jobs of a 999 KB body (the harness's retain), then a restart from the same store:

Host Body Before (verifier) After 30 jobs After restart
Thor, Linux [0,0,…] 1,055 MiB, 1,036 after restart (Framework) 41 MiB 36 MiB
Thor, Linux [{"a":0},…] 2,119 MiB, 1,752 after restart (Framework) 38 MiB 36 MiB
M4, macOS [0,0,…] 2,096 MiB, 1,061 after restart 4 MiB 32 MiB

Requests refused at the window, 120 at once (transient): Thor 11 MiB peak; the M4 4 MiB peak, where the verifier saw 2,333 MiB.

Fourth review (11 findings)

"Before" is 53676a3e5 (the previous head) with the new depth tests grafted on, run on the Framework (Linux x86_64).

# Finding Fix Test Before
1, 4 the open-file limit stayed at 256 and connections had no cap, so held connections took every descriptor the service raises its soft open-file limit to its hard one as it starts (on macOS to kern.maxfilesperproc and 10240 at most) and logs it; it holds at most WAI_QSVC_CONNECTIONS (4096) connections, fewer under that limit (a quarter, at least 64, left for pipes and files); past them the connection idle longest, or the one whose body has been arriving longest past a second, is closed for the new one, and with none to close the new one gets 503 at once; a client may have WAI_QSVC_CLIENT_REQUESTS (32) requests open, refused 503 before its body is read (a health check exempt) held_connections_leave_room_and_health_answers (hard and soft limit 256: 400 header sections from one client, 400 bodies from 400 clients, 400 bodies from one, health 15 of 15 each time, no Too many open files) fails: one client's 400 held bodies are not refused; the verifier's probe had health 0 of 15 (in this harness the old build still answered health)
2 a result over the client's cap was dropped as it ended; /v1/run 202 then 404; MCP isError: false with no result a result is kept as its RFC 8785 text, counted at its length; an ended job not yet read is kept for WAI_QSVC_KEPT_MIN_SECS (600) whatever it weighs; while a client's unread results hold its cap its next job is refused 429, and while everyone's hold the service's, 503; an MCP run whose job is gone is an error a_result_larger_than_its_client_keeps_is_kept_until_it_is_read (16 qubits, 20,000 shots a point, cap 1 MiB: poll, /v1/run and run_quantum_set) fails: 404 0.4 s after submission
3 a check on a lent turn was cut at 5 s after its client's own turn was free a lent reading moves onto the client's own turn as one comes free; past 5 s it runs on while no other request waits for a turn a_documented_qec_call_beside_another_check_from_its_client_is_answered (a critique and a decode at once; two decodes 30 ms apart) fails: 503 J032 at 5.0 s
5 global eviction was plain LRU past the service's cap the client keeping the most loses first, the newest between equals ended_jobs_keep_their_request_as_text_within_their_caps new rule, unit test
6, 7 a job's bytes depended on the route and changed at a restart one RFC 8785 writer for every route (poll, events, cancel, MCP job tools; MCP answers written as RFC 8785), request and result spliced in as kept, the receipt's keys sorted a_job_reads_the_same_on_every_route_and_after_a_restart fails: a report is not RFC 8785
8 a large MCP message past the window got 429 with id null read only for id, method, tool and key; answered under its own id with a tool result; a keyed resend of a held job and a receipt check are read whole a_large_mcp_message_past_the_window_is_answered_under_its_own_id fails: 429, id null
9 any Idempotency-Key skipped the window; /v1/verify parsed with no limit the window is skipped only for a key the client holds a job under; receipt checks have their own window, WAI_QSVC_CLIENT_VERIFIES (120), asked before the body is read a_body_is_read_only_once_its_window_or_its_key_allows fails: 400 (parsed) past the window
10 tasks/result held 50 s whatever the deadline held for what is left of the answer's time a_task_s_result_is_held_within_the_answer_s_time (WAI_QSVC_ANSWER_SECS 10) fails: answered after 50.0 s
11 no 408 in the OpenAPI document 408, 413 and the pre-handler 503 on every route the_openapi_document_names_every_route no route listed 408 (the verifier's probe)

Fifth review (8 findings)

"Before" is a0804db72 with this head's tests/depth.rs copied in (a worktree of a0804db72), run through rb on the Framework (Linux x86_64) and the M3 Ultra Studio (macOS arm64), each test alone, under ulimit -Sn 256 and under ulimit -n 256. Findings 1 and 2 are now tested by the round-5 verifier's own probes and timing. The earlier versions of these two tests passed on a0804db72 because they were not those probes. The first held its polls on long_job(), not the verifier's pump, mixed a third of them with event streams, sent them from 8 addresses with no pacing, and checked health after 1 s; it never checked that the job still ran or that any poll was held, so which of these let it pass was not isolated. The second raised WAI_QSVC_QEC_SECS, WAI_QSVC_READ_SECS and WAI_QSVC_ANSWER_SECS and sent the README's threshold as it is, which takes about 6 s alone on these hosts, inside the 10 s a waiting call is given; the critiques waited for it and were answered, and the test asked only for 200s, not when.

# Finding Fix Test Before
1 long polls and event streams, never evictable, could fill the connection cap and health got 503 four connections past the cap are held for health checks alone (one request each, 2 s); long polls and event streams are held to WAI_QSVC_CLIENT_STREAMS (4) a client and WAI_QSVC_STREAMS (512, a quarter of the connections at most) in all; past them a long poll is answered at once with the job as it is (round 6: 429 with Retry-After), and an event stream is refused 503 long_polls_and_streams_leave_a_health_check_answered, the verifier's probe (_sk/probe.py): its pump (a T layer and a CX ladder, 30 times, one shot; 24 qubits where it had 22, so a faster host still runs it past every wait), then 210 ?wait=25 polls with the probe's own X-Forwarded-For values (10.0.k.m), 3 ms apart, under a hard limit of 256 (192 connections), then 25 health checks; then the same with event streams. It checks that the job still runs and that polls were held, then asks for 25 of 25. Under ulimit -n 256 the test process itself holds 64, and lowers the service's cap to 58 fails on both hosts and both limits: health answered 0 of 25 beside 210 long polls, 192 of them held: open files: 256, the hard limit; under ulimit -n 256, 0 of 25 beside 64 long polls, 58 of them held. Head: 25 of 25, polls and streams, on both hosts and both limits
2 a long check moved onto its client's own turn kept the lent one, so the client's quick calls waited 10 s and were refused a reading that moves onto its client's own turn gives the lent turn back, so the next quick call is lent one the_readme_s_burst_from_one_client_is_answered, the verifier's burst (_skp/lm.py) at the service's own limits, nothing raised: the README's decode at 0 ms, a threshold at 30 ms, six Bell critiques from 60 ms, 30 ms apart, from one client, over HTTP and then over MCP. The threshold is made long as the verifier made it (it raised the shots to 30,000): shots raised half again at a time to 33,333, then its rounds, until it takes 11 s alone, past a waiting call's 10 s. Every call must be answered, and each critique at least 1 s before the threshold fails on both hosts and both limits: Framework, threshold alone 12.9 s and 14.1 s, every critique 503 … your own 1 turn of this pool is still taken by your other requests after 10 s [J032] at 10.07 to 10.21 s; M3 Ultra, alone 18.8 s and 11.1 s, refused at 10.07 to 10.22 s. Head: every critique answered, at 0.2 to 3.8 s, each at least 1 s before the threshold, HTTP and MCP
3, 4, 5 unread results could 429 a shared address's co-tenants for long, largest-holder eviction dropped read results early, about 16 identities could hold every next job with a renewable 503 read and unread results counted apart; only read ones are dropped before their time; a client's next job waits while its unread results, with each running job at its own result's bound and the new one at WAI_QSVC_RESULT_MIB (64), would pass WAI_QSVC_CLIENT_UNREAD_MIB (256); every next job waits while all unread hold WAI_QSVC_UNREAD_MIB (8192, 32 clients' whole allowances); WAI_QSVC_KEPT_MIB is 4096; a result's size is held to WAI_QSVC_RESULT_MIB before it runs (below); the README states the bounds that remain read_results_go_before_unread_ones_and_unread_ones_hold_their_client_s_next_job, a_result_s_bound_is_held_to_the_cap_and_running_jobs_count_their_own, a_result_past_what_a_job_may_give_is_refused_before_it_runs new rules
6 MCP job reads were about twice main's time; tasks/get built the whole report a task's state is read from the report without its request and result; MCP answers are written by serde, with RFC 8785's numbers, not sorted again by reading not measured again
7 MCP ids past 2^53 came back changed integers within 64 bits are written exactly as read (round 6: every id as the text it was sent as) an_mcp_id_is_echoed_exactly fails: 9007199254740993 answered as 9007199254740992
8 self-host.md put the 192 cap on the macOS default corrected: launchd's default hard limit is unlimited and the service raises its soft limit itself; 192 only at a hard limit of 256 text text

Sixth review (16 findings)

"Before" is d5564748e with this head's new tests copied in (a worktree of d5564748e: engine::bound_tests, the new tests of tests/depth.rs and the changed a_reading_s_answer_is_held_to_what_it_takes_once_read, and the two offload unit tests), run through rb on the Framework (Linux x86_64), each service test alone under ulimit -Sn 256. Every one fails there and passes here.

# Finding Fix Test Before
1 a failing sampled intent's fit listed every outcome it rules out, in impossible, its note and twice in its why; the bound counted 64 stats::goodness_of_fit names at most 64 of them and gives how many (impossible_outcomes, new, in the verdict schema); the reason says the note once; the bound counts each intent's four lists at 64 outcomes each, and the statement's two reasons engine::bound_tests::a_failing_intent_trajectories_and_notes_stay_within_their_bounds, and the property test no_random_job_s_result_passes_its_bound fails: a sampled intent ruling out 4,095 outcomes: its result is 2713555 bytes, past its bound of 149528; the property test's first: job 24 (seed 676), sampled intents that fail: its result is 245762 bytes, past its bound of 145949
2 a trajectories run lists options.trajectories samples; the bound took the shots catalog::samples_listed: the trajectories (or the shots, the larger) for a trajectories run, on a device target too the same test, one shot, 30,000 trajectories fails: one shot, 30,000 trajectories: its result is 71578 bytes, past its bound of 17751
3 an MCP dry run planned the job without the verdict every MCP run asks for, so it said taken what the run refused (J031) mcp::as_run: the run tools and both dry-run paths plan the same job; dry_run_quantum_job's description says so an_mcp_dry_run_says_what_the_run_tools_take (cap 1 MiB): the most points the MCP dry run takes are run by run_quantum_set to done, and one more is refused fails: 16 points, accepted by the MCP dry run, refused by the run: its result could take up to 1.5 MiB as served
4 the README's 100 points × 30,000 shots were admitted and stopped for memory: the set's worker held every item's result as a tree the worker writes each item's result as text as it ends and drops its tree, so the set holds one item's tree and the text the_readme_s_largest_sweep_is_taken_and_ends_done, at the defaults fails: the job used 656 MiB, over the 258 MiB it was admitted with; it was stopped
5 a client's second long check, on a lent turn, was never cut for its own client's waiting calls, so they waited out 10 s a lent turn goes back once another client's request waits, or once one of its own client's has waited 7 s (offload::OWN_PATIENCE): the quick call takes it 1.5 s or more before its wait ends, and a call of its own that ends sooner (the README burst's decode) frees a turn first a_client_s_quick_calls_pass_two_of_its_own_long_checks: two thresholds of 11 s or more, then four critiques at 4 s, from one client fails: every critique 503 your own 1 turn of this pool is still taken by your other requests after 10 s [J032] at 14.00 to 14.09 s
6, 15 a JSON-RPC id of 1.0, 1e3, or an integer past 64 bits came back as another number http::raw_id and http::mcp_text: every MCP answer writes the id as the text it was sent as (serde_json's raw_value), on every path (an answer, tasks/result's event, a message past the window) an_mcp_id_is_echoed_as_it_was_sent: 1.0, 1e3, 1E3, -0.0, 1.50, 2^64, 123456789012345678901234567890, "1.0", on ping and tools/list fails: ping: sent the id 1.0, answered {"jsonrpc":"2.0","id":1,"result":{}}
7 a verdict lists the reader's notes again, and the bound counted them once the verdict's part counts them, and the repair, again a_failing_intent_trajectories_and_notes_stay_within_their_bounds, 3,000 notes and a verdict fails: 3,000 notes and a verdict: its result is 704773 bytes, past its bound of 409945
8 a set's items' requests were counted at serde's length and a sixteenth; RFC 8785 writes 1e20 in 21 characters receipt::canonical_len (new): a value's length as RFC 8785 writes it, each number in its own form and whole numbers as read, whichever is longer; every request part of a bound uses it, and the provenance's shots and seed as sent are counted a_set_s_bound_counts_its_items_requests_as_served (3 points sharing 20,000 1e20s) fails: served 1327064 bytes, past the bound of 454321
9 a dry run read without keeping the items' requests, so the read-answer weight never refused it the child reads a dry run as its submission (kept), holds that answer to the weight and its text to the answer's length, then gives back the plans and points alone; a reading refused for its size is the dry run's refusal (accepted: false, 422) a_dry_run_that_says_taken_is_taken_by_its_submission (100 points sharing 12,000 and 20,000 1e20s); a_reading_s_answer_is_held_to_what_it_takes_once_read, which held the old behaviour, now holds this fails: pad 20000: the dry run said true (null), the submission 422 ... gave more than 128 MiB once read
10 honest Clifford sweeps were bounded as if every shot were a distinct outcome catalog::outcomes_bound: at most 2^b outcomes for the b bits its measurements write, and on the noiseless stabilizer method 2^d, d its measured qubits and its gates that branch (h, sx, sxdg; the rest permute or phase the basis), each at most doubling the outcomes a tableau gives an_honest_clifford_set_is_taken_by_the_outcomes_it_can_give (100 GHZ24 at 30,000 shots, run to done); a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list fails: its result could take up to 85.1 MiB as served; GHZ24, 30,000 shots, stabilizer: bounded at 887668 bytes, where its result is 940
11 a verdict was charged 512 bytes a gate, so a 124,700-gate loop with nothing to remark on was refused, its reason blaming the counts critique::smells_bound (new): the walk the critique makes (one walk now serves both), each smell it finds at 256 bytes and its qubits, and the idle and unmeasured lists; counted from the program as read, before any compile; a program holding a gate whole keeps the per-gate charge. A refusal names the bound's largest part and what makes it smaller (catalog::Bound, new: counts, verdict, notes, requests, rest) a_long_loop_s_verdict_is_taken_and_a_refusal_names_its_part: the loop over MCP and /v1/run with verdict: true (the Studio's request), and three refusals, each naming its part; a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list fails: the MCP run: its result could take up to 64.7 MiB as served (its counts listing ...); a loop of 124,700 gates, with its verdict: bounded at 67880108 bytes, where its result is 1719
12 a long poll past its client's holds was answered 200 at once, unchanged, so a README client spun answered 429 (its client's) or 503 (everyone's), J032, with Retry-After: 2 and the job's status; the README, the OpenAPI wait parameter and its responses say so; the command line's follow waits and polls again a_long_poll_past_its_client_s_holds_says_when_to_poll_again; another client's poll is held fails: a fifth hold: HTTP/1.1 200 OK
13 the service-wide unread cap counted only ended results, so jobs taken under it ended past it every queued and running job counts at its bound, the new one at its own, before reading and again under the queue's lock as the job is kept (inserted with it, so no two pass on one count) ended_jobs_unread_stay_under_the_unread_cap (30 clients, 2 MiB cap, bound 621,764 each) fails: 30 of 30 taken
14 the reading pool lends no turn at a share of two, yet admitted share + 1 + 8 a client's requests are held to its share, a lent turn where the pool can lend one at that share, and eight waiting: its eleventh at once is refused, of readings and of checks; and no turn is lent while one of the client's own is free or taken by a request still waiting for the pool's, which closes the race the verifier saw (three readings at once in 2 of 14 bursts) offload::tests::a_client_s_eleventh_request_at_once_is_refused_and_eight_wait; and a_client_at_its_share_of_the_readings_is_never_lent_one (50 bursts), a guard only: in-process it passes on d5564748e too (3 of 3 there), so the race is closed by construction and not shown failing here fails: Reading: your own 2 turns of this pool are still taken by your other requests after 10 s (the eleventh waited)
16 the advisories step failed on RUSTSEC-2026-0459 main merged once, at 1929b8436 (rusqlite 0.41, fc67077fc, and the corpus-licence self-test fix, a4766c993); main's own vanity-metric fix for two of its lines (4c5b54a27) is cherry-picked, as 1929b8436 had them the repository checks RUSTSEC-2026-0459 libsqlite3-sys 0.30.1 at d5564748e

The result cap, at admission

A job's or a set's result is bounded before it runs. A request whose bound passes WAI_QSVC_RESULT_MIB (64 MiB) is refused at once, 422 and J031, with the bound, its largest part and what makes that part smaller in the reason, on POST /v1/jobs, POST /v1/run and the MCP run tools. Before, such a job ran and failed as it ended.

  • The bound. It is computed by catalog::plan_bounded (and catalog::plan_bound, by part: catalog::Bound, counts, verdict, notes, requests and rest) and sets::plan_bounded, and for a pulse run by pulse::plan_bounded through Method::plan_bounded. All are new and additive in the engine, and run in the reading child (http::plan_job). Each item's counts list at most the samples it draws (its shots; a trajectories run's trajectories), the outcomes its measured bits can give (2^b for the b bits written; on the noiseless stabilizer method 2^d, d its measured qubits and its branching gates h, sx, sxdg), 65,536 outcomes, or what 16 MiB of bit strings holds. Each outcome is its bit string, quotes, a colon and a comma, and k counts summing to the shots have at most k·(log10(shots/k) + 1) digits. A verdict counts its fields, the reader's notes and the repair again, its critique by the smells the program's gates can draw (critique::smells_bound, the critique's own walk, from the program before any compile; a program holding a gate whole at 512 bytes a standard gate), each intent as given with its four lists of 64 outcomes, and two intents' reasons again in its statement. A sampled intent names at most 64 of the outcomes it rules out (impossible_outcomes says how many). Expectation values, amplitudes, diagnostics, outputs, a repair, a compile, a mitigation and a device target are counted by what each may hold, or at their length as RFC 8785 writes them (receipt::canonical_len: each number in its own form, a whole number as read where that is longer), as are the provenance's shots and seed as sent. 16 KiB covers the rest, plus a sixteenth for the numbers the engine writes. A set adds each item's request (at its served length), its frame and the head. A pulse run is bounded from its schedule as read: its populations by the qubits it simulates, its distribution by the captures it reads (each keyed as wide as the program's bits), its counts by its shots, and each capture with its frame's and bit's names.
  • Where it is published. The README states it beside the set limits (Sets and sweeps) and in the settings table. GET /v1 gives limits.result_bytes beside set_items. So do the dry run's bounds.result_bytes, the MCP description's sets.result_bytes and the OpenAPI document (the set's description, DryRun.result_bytes, every 422). A dry run states the request's own bound as result_bytes, named in schemas/service-tools.v1.json.
  • The backstop. The check as a job ends stays, for one case only: a job kept across a restart that lowered the cap. No built-in method is unbounded now. A method that gives no bound (the trait's default) is counted at the cap and held to it as it ends. These tests check that no admitted job reaches the backstop:
    • engine::bound_tests::no_random_job_s_result_passes_its_bound draws 96 jobs of every kind (failing sampled intents ruling out many wide outcomes, trajectories with their own count, verdicts over thousands of notes and smells with and without a compile, random Clifford circuits of 1 to 30 qubits measured into repeated bits on the stabilizer method or as planned, with and without noise, observables, amplitudes, a mitigation, a repair, a measurement in the middle, a seed sent as a long string or a list of long numbers, device targets, pulse runs, intents carrying numbers RFC 8785 writes long) and holds each result, as either RFC 8785 writer gives it, to its bound; it passed under 40 seeds besides its own. a_failing_intent_trajectories_and_notes_stay_within_their_bounds holds the review's three cases, and a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list that a GHZ24 at 30,000 shots is bounded under 32 KiB and the 124,700-gate loop with its verdict under 256 KiB.
    • engine::run::tests::no_result_passes_the_bound_its_plan_gave runs the worked examples, plus a job for each part a result grows with: 65,536 outcomes, 300-bit outcomes, mid-circuit measurement, 256 observables, 64 amplitudes, a remark on every gate, repair, compile, noise, mitigation and a device target. Every result is within its bound. Where counts make the result, it is within 3% (1,438,608 bytes against 1,471,488).
    • engine::pulse::tests::no_pulse_result_passes_the_bound_its_plan_gave does the same for pulse runs: one capture, one bit of 3,000 captured, nothing captured, and two qubits captured.
    • a_result_past_what_a_job_may_give_is_refused_before_it_runs (service, cap 1 MiB) finds the largest sweep under the cap from dry runs. That sweep must be taken and end done within its bound (16 points: bound 1,037,576 bytes, served about 700,100). One more point must be refused at once on all three routes, and its dry run must say so. A pulse run of 100,000 bits must be refused at once; before, it was taken and failed as it ended. The two cases below are checked at the defaults.
  • The default stays 64 MiB. The published set at its most cannot be covered by any cap a public service should serve. 100 items of 65,536 outcomes at 16 bits have a bound of 147,646,836 bytes, about 150 MB. With wider bit strings an item reaches about 17 MB (16 MiB of bit strings), so 100 items reach about 1.7 GB. Raising the cap to 192 MiB would admit only the 16-bit case, at real cost:
    • such a result is held in the service's memory, unread, for up to 600 s;
    • a client's 256 MiB unread allowance would hold one of them;
    • an MCP read parses it and writes it again on one of the runtime's four threads (round 5 measured 1.3 s for 27 MB);
    • its answer passes the proxy whole.
      So the cap stays, and the README says plainly that such sets are refused and what is taken. At 64 MiB, 100 points of 65,536 outcomes are refused, and 100 points of 16 bits are taken up to about 30,000 shots a point: 30,878 without a verdict (bound 65,262,975 bytes at 30,000) and 30,003 over MCP, whose runs ask for one (67,100,475 at 30,000), measured by dry runs on a local build on both hosts. Both routes run 100 × 30,000 to done (6.6 and 6.8 s on the M3, 29.1 and 28.1 s on the Framework); the_readme_s_largest_sweep_is_taken_and_ends_done runs it at the defaults, and the test above asserts the refusal and the dry run. A client's running jobs count against its unread allowance at their own bounds, not at 64 MiB each.

Known new refusals

Programs main read and this refuses, or answers it gave and this does not. Each is a clean refusal with a code.

  • Real nesting past about 60 levels: parentheses, blocks, nested ifs, a ** or ^ power run, an index chain, a ++ chain, prefix operators (Q006). Flat runs and else if chains are no longer in this list.
  • A recursive subroutine whose body nests more than about seven levels reaches 256 before its 32 calls (Q006).
  • An array of more than 7 dimensions (Q006).
  • One value over 64 MiB, or 512 MiB held in all (Q130).
  • A QEC text naming a qubit or a detector at 2²⁴ or past it.
  • A QIR or jeff condition whose parentheses nest past 30 (Q705), from 32.
  • REPEAT n { H 0 } from about 25,000,000 passes, not 50,000,000 operations.
  • In the service: a check whose answer would take more than 128 MiB once read, such as a critique of a program of 200,000 gates that each draw a remark (422 J031; 150,000 are answered); a client's call on a turn lent beside its own that takes longer than 5 s once another client's request waits, or once one of its own requests has waited 7 s (503 J032, to send again); a client's eleventh request at once, of readings (its share of two and eight waiting; no reading turn is lent at that share) or of checks (its share, one lent turn and eight waiting) (503 J032); a run whose job outlasts the answer's 55 s answers with the job to poll; a decode of more than 1,024 fired detectors; a QEC decode or threshold whose shots, measured as they run, would take more than 25 s (WAI_QSVC_QEC_SECS), or that has more than 21 rounds; a reading that takes more than 45 s (down from 120); a set whose items would take more than 128 MiB once read; a check whose answer runs the budget out (a repair of many fixes to a slow program, an improve of a program near the budget, as each candidate is a new text); a set of readings past the child's caps; a source job resent in a form other than the one it was sent in is read and counted.
  • MCP: a poll of a queued or running job, or of an ended one sent without verdict or intent, gives a verdict with empty diagnostics and critique, and a view on a poll has no drawing; an answer whose verdict found no time left says so.
  • An ended job that has been read (or ended more than 10 minutes ago) is dropped before its WAI_QSVC_RETAIN_SECS once its client's read results keep more than 128 MiB, its oldest first, or all keep more than 4 GiB, the largest holder's first. A client's next job is refused (429) while its unread results, with its running jobs at their bounds and the new one at 64 MiB, would pass 256 MiB; while all unread results, with every job queued or running at its bound and the new one, would pass 8 GiB, new jobs are refused (503).
  • A job or set whose result's bound passes WAI_QSVC_RESULT_MIB (64 MiB) is refused at once, 422 and J031: for example 100 points of 65,536 outcomes (about 150 MB), or 100 points of 16 bits past about 30,000 shots a point (30,878 without a verdict, 30,003 over MCP). A verdict's critique counts about 270 bytes for each smell the program's gates draw (a gate that does nothing, a pair that cancels or merges), so a job asking for a verdict is refused only where its smells reach the cap (about 250,000 of them); a long loop with nothing to remark on is not. A refusal names its largest part and what makes it smaller.
  • A long poll past its client's 4 holds is answered 429 (503 past everyone's) with Retry-After: 2 and the job's status, where it was answered 200 at once. A dry run whose reading, kept as its submission's would be, passes what the service takes back is refused (accepted: false, 422), as its submission is.
  • A client's 33rd request open at once is refused 503 before its body is read; past the connection cap a new connection is answered 503 where none can be closed for it. A client's 121st receipt check in its window is refused 429. A body under an idempotency key the client holds no job under waits for the window like any other.

Tests

tests/depth.rs drives the binary over HTTP alone, so it runs unchanged against older code. The answer, dynamic and editor index tests compile on main unchanged; pulse, QEC and frame need only a literal for a constant.

Test Before (main 4e17e96ef) After
tests/depth.rs: deep programs through /v1/jobs, /v1/run, a dry run, an intent's reference, a set, a sweep, the MCP run tool and a task, five checks and a QEC decode, /v1/health after each fails: the first POST ends the service (empty answer) passes
same file: sixteen heavy references refused in bounded time and counted fails: no answer within the test's 120 s on the iMac, 4 of 4; on Thor a 202 after about 96 s, where 422 was wanted passes
qasm::depth_tests not run on main, whose constants differ; the probe overflowed the constructs it measured passes, debug and release
answer chain, dynamic deep walk, editor index, pulse expression each overflows the stack (SIGABRT) pass, debug and release
QEC blocks fail at 33 levels: nothing refuses them pass, debug and release
QEC empty block, frame blocks hang past 240 s pass, debug and release
the_branches_sum_to_the_exact_mixture passes passes
wai-web wasm, a_program_nested_past_the_reader_s_limit_is_refused_not_trapped traps: RuntimeError: memory access out of bounds, Node exit 1 passes, 23 of 23, on the M4
CLI front door: lint, run, plan, critique, equivalent, instance, qec decode lint ended by a signal (front_door.rs:152) passes, 6 of 6
Python, test_a_program_nested_past_the_readers_limit_raises_and_the_engine_goes_on the interpreter segfaults (exit 139) passes; the whole ci/check.sh passes on the M4, 86 tests, its notebooks against a service started from this branch

The desktop app, on the iMac (macOS x86_64): built in debug, 22 tests pass. Its release build is left to CI: wai-quantum-studio.yml fires on this PR.

Runs at this head (main merged at 1929b8436), every one through rb, each request sent only to a local build on loopback. The service's whole suite (unit tests, depth, restart, tasks, verify) ran under ulimit -Sn 256, on the committed tree:

  • macOS, M3 Ultra Studio (arm64):
    • service: 116 of 116 (unit 71, depth 41, restart, tasks, verify 2); clippy clean;
    • library --features full --lib: 1,436 passed, 0 failed, 18 ignored; tests/reader_stack.rs 1 of 1;
    • CLI: 109 of 109; clippy clean with and without its default features.
  • Linux x86_64, the Framework (load 15 to 40 from other sessions):
    • service: 115 of 115 (unit 69, depth 42); clippy clean;
    • library: 1,435 passed, 1 failed, 18 ignored. The failure was engine::qec::tests::a_time_is_measured_apart_from_setup_and_holds_every_threshold, a QEC call refused for its projected time under that load (its shots would take about 30 s … after 3.1 s, where it took 4.5 s with no time set); this round touches no QEC code, and alone it passed 2 of 2. tests/reader_stack.rs 1 of 1;
    • CLI: 109 of 109, and clippy clean.
  • The library runs were of snapshots whose engine differs from this head only in how two of the new bound tests report (each failing case listed, not the first); the service runs were of this head's tree.
  • The property test ran under 40 seeds besides its own (96 jobs each), on the Framework: every result within its bound.
  • The README's sweep, measured by dry runs and runs on both hosts: 30,878 shots a point taken without a verdict, 30,003 over MCP, and 100 × 30,000 run to done on both routes (6.6 and 6.8 s on the M3, 29.1 and 28.1 s on the Framework).
  • Repository checks at this head on the Framework: lockfiles (67, current), advisories (67 lockfiles, 20 allowed), tests in CI, workflow concurrency, pr-touches-paths, wai-quantum names, the changelog's form and this change's entry, vanity metrics, the public-text self-test, comparative prose, public names (--maintainer-products-only), corpus licences, orphan sources, spec and section references, the conformance lists: each passes, with its self-test. Vanity metrics was red at 1929b8436 on two lines of main's own, fixed on main in 4c5b54a27 and cherry-picked here. The public-name check with the private list is clean on the 188 files this PR changes.
  • wai breaking --against 1929b8436 --text --all (on the M3; the Framework lacks dav1d for the API check's documentation build) exits 0: five breaking changes, each acknowledged in the entries: E::Chain, S::If, Request::smells_bytes, and main's qasm::MAX_NESTING and qasm::MAX_ELSE_IF, which this reader replaces.

Earlier rounds' runs (at e02f5b79a, d5564748e and before) are in the history of this description; each round's fail-first evidence is in its table above.

CI

  • Round 6. The advisories step failed at d5564748e on RUSTSEC-2026-0459 in jouleclaw's lock; main merged at 1929b8436 carries rusqlite 0.41 (fc67077fc), and it passes here. Main's reader nesting bound met this branch's reader in that merge (above). The service workflow's engine step names the three new bound tests, which run in its library filter.

Two reds at 4f44194bf, fixed here:

  • Main merged at e54f28102 (wai-quantum 0.9.0), so the contract's API check passes on the raised 0.y and main's fixes for the shared reds (the conformance corpora, the DVR tests, the native count) come with it. Two things met: main made a for loop's values lazily, as this branch did, and its type is kept; tests/reader_memory.rs nested 100 loops, past this branch's 64 levels (Q006), so it nests as deep as the reader nests loops (parse::MAX_NESTING - 8, 56), as qasm::depth_tests does, under the same memory bound.
  • WAI energy receipts · macos failed at 7586335a7 on a_time_is_measured_apart_from_setup_and_holds_every_threshold, in a debug build beside the library's other tests. A code-capacity point's pilot was one in a hundred of its trials run whole (2,000 trials of colour:15, 11 to 14 s there before the 3 s call was refused); it now runs in doubling batches held to half a second, as a circuit's pilot is, and the test's bound is 5 s, or twice its own decode's time where the host makes that longer (this round: it was refused after 8.2 s on the Framework at load 77, on its pilot). The setup-heavy decode in the same test is timed first and then given twice that and a second, where a fixed 15 s was a release build's figure. On the Studio, the whole library in debug failed this test in two runs of two before, and passed twice after (1,411 passed).
  • WAI breaking refused E::Chain and S::If as unacknowledged; the changelog fragment now carries their Breaking lines, and wai breaking --against <merge base> --text --all exits 0. The wai-quantum contract's major bump comes from main (0.9.0).
  • jouleclaw public surface failed on main (208ffd374), #659 and here within 25 minutes, on three runners. Each log stopped at about 256 KiB, in the middle of the scan's 800 KB report, and the job was failed twelve minutes later; the scan's own summary in each read public surface only: 0, and the same scan exits 0 on the Framework. The step now writes the report to a file and logs its summary, and on a failure the report's list of what is missing: 393 bytes of log on a pass, and an undocumented pub fn planted in a copy still fails it, naming the function.

Pins: wai-web's engine wasm tests are 23, this PR's one and main's one; the pulse tests are 28, native and wasip2 (no_pulse_result_passes_the_bound_its_plan_gave is new); the service workflow's engine step names the two bound tests, and the QEC workflow a_circuit_s_pilot_runs_for_its_floor_within_its_shots. The new tests are in the named lists of the QEC and verdict workflows, and the reader's module filter takes qasm::depth_tests. No workflow names the dynamic walk's two tests: wai-energy-receipts.yml runs them, in debug, in the whole library. The repository checks pass: tests in CI, lockfiles, vanity metrics, comparative prose, orphan sources, workflow concurrency, the changelog's form and public names. The service's Cargo.lock gains hyper and hyper-util as direct dependencies, both already in its tree.

🤖 Generated with Claude Code

**Re-landed on main as one commit, ce1c91131 (2026-11-09), under the land-it model.** Round 6's 16 findings are fixed, each with a fail-first test (b35ae093e). main f6e302665 was merged at 0cf954bfc (local), with wasmQ's verify surfaces brought under the same admission, turns and reading child, and wasmQ's profile stating this reader's nesting bound (Q006). Passed on the merged tree, linux and macOS: the service suite under ulimit -Sn 256, the library's 1,458 tests, the CLI, wai-wasmq and its vectors, clippy, wai breaking, and the repository checks. Main's CI runs on the push. Closed. --- ## What was wrong - The OpenQASM parser recursed once per `(` (about thirteen frames a level), once per statement in a block, once per prefix operator, with no limit, and the elaborator recursed over whatever tree the parser built (a chain such as `0 + 0 + …`, read in a loop, built a tree as deep as it was long). The job service parsed on a tokio worker (2 MiB stack) before any rate limit, so a POST of about 1.3 KB with some 600 nested parentheses in `qasm`, or in an intent's reference, aborted the whole process. - A job's intent references were read three or four times each (`intent_bounds`, `equivalence_work`, `catalog::plan`, `with_intent`) before the count, size and rate checks, and refused requests were never counted, so a few small requests with heavy references held every worker for minutes. ## What is bounded, and where | Reader | What recursed | Bound | Refusal | |---|---|---|---| | `qasm::parse` | statements in blocks and bodies, `if`/`for`/`while`/`switch`/`box`, gate and `def` bodies, parentheses, prefix operators, `**`, binary chains, index chains, `++`, calls, casts, sets, types (`complex[`, `array[`), `durationof`, `measure` | `MAX_NESTING` = 64 levels in the text: recursion depth and each expression tree's height together | `Q006` (new) | | `qasm::elab` | statements, expressions, conditions, tangents, gate expansion, subroutine calls | 256 levels in all (`MAX_NEST`), beside the existing 32 for calls and gate definitions (`Q209`) | `Q006` | | `qasm::instantiate` (unroll) | integer expressions (`(`, prefix, `**`), nested `for`/`if` written out, brace-less statement scans | 64; a flat sum and an `else if` chain are loops, at any length | `Q006` | | pulse reader | calibration expressions (it borrows the parser) | 64, through the parser's own counter | `Q006` | | QEC circuit and DEM text | `REPEAT`/`repeat` blocks; their lowering and model walk | 32 (`MAX_BLOCK_DEPTH`) | on the block's line (`J009` in the service) | | frame lowering / DEM walk | a block repeated 2⁶⁴−1 times that writes nothing ran forever; nested `REPEAT 0` cloned the circuit at each level | every pass counts toward the unroll bound; unrun blocks checked once on one copy | refused | | editor index (LSP, visual edit) | statements | 64; deeper statements passed over without recursion | none (tolerant) | | answer layer | a statement's value through a chain of statements | 32 (`CHAIN_DEPTH`) | error | | flat runs (`a + b - c`, `&&`, `||`), `else if` chains, a switch's case values on measured bits | a tree as deep as the run is long | none: held flat (`E::Chain`, `S::If { arms }`, `Ct::All`, `Ct::Any`) and walked in loops, left to right, at any length, on 1 MiB | none | | array rank | a comma-separated list of dimensions, each a level of the value | 7 (`MAX_ARRAY_DIMS`) | `Q006` | | QEC text indices | a vector as long as an index in a few bytes | 2²⁴ qubits and detectors (`MAX_QUBITS`, `MAX_DETECTORS`), offsets counted | on the line (`J009` in the service) | | dynamic expectation walk | one recursion per two-outcome measurement | a list, same leaf order (same bits) | `MAX_LEAVES` as before | Modifier chains (`inv @ inv @ …`) and `include` lists are iterative and read at any length; a test holds that. Frame sizes were measured on the Framework (Linux x86_64, release and debug, threads of 1 and 2 MiB) with a temporary probe, the limits lifted for it. - On main, on 2 MiB: release overflowed past 304 parentheses, 466 blocks and 1759 chain links; debug past 56 parentheses and 43 blocks. - To make 64 levels safe with room in debug, the parser now reads binary operators by precedence climbing (one frame a level of nesting rather than one a precedence level) and each statement form in a function of its own, and the elaborator splits `exec` and `ev` the same way. - Measured after those splits: release on 1 MiB read at least 380 levels of every construct probed; debug on 2 MiB at least 113 (parentheses 126, blocks 306). Through 30 nested calls, release on 2 MiB held 58 levels of `if` per body, about 1 800 levels of the elaborator. - `atom` was split the same way after that measurement, so these figures are from just before it and were not taken again. The tests below ran on the final code. ## Main's own nesting bound Main took a nesting bound of its own for this reader in 15ae7d457 (`Q130` at 128 levels, a chain of at most 127 operators, at most 1024 `else if` arms), after this branch last merged it. The merge of main at 1929b8436 keeps this branch's reader (its sources, its diagnostics table and its docs), which bounds nesting and memory already, and holds main's tests of that bound to it: `lang_tests::nesting_is_bounded_as_the_text_counts_it` reads each shape inside the bound and refuses it past (`Q006`), a run of 10,000 of one operator and ladders of 1024 and 5000 arms read, and a chain through calls past 256 is refused; `tests/reader_stack.rs` reads every program at the bounds on 1 MiB and 2 MiB threads in release (16 MiB in debug) and refuses the same shapes nested 5000 deep on them; the service's `tests/verify.rs` reads the 2000-term sum and refuses 1000 brackets and 1000 blocks on seven routes. Main's changelog entry says what holds (no release carried its `Q130`). ## The budget primitive and #648 This adopts #648's `qasm::within_steps` as it is (signature, per-thread memo of readings with no inputs, inner budgets run within the outer one, `Q312` past it), so main has one primitive. On top of it: gates written and a generator's loop passes spend from the budget too (statements alone let `pow(k) @` or a sweep's unrolling run outside it), and `qasm::steps_left()` tells a budget refusal from a program's own (`Some(0)`). When #648 merges main, take main's `qasm.rs` and `elab.rs` hunks; `within_steps(1_000_000, …)` in its writer works unchanged, and `steps_left() == Some(0)` after a refused read is the test for "over the budget". ## The service No anonymous request ends the process, holds a thread without limit, or makes the service keep memory without limit, on any route. This is what each request now goes through. The defaults are in the README's settings table. - **Arrival**: every request is answered within `WAI_QSVC_ANSWER_SECS` (55) of its arrival, under a proxy's 60 s. A body is parsed only once its client's window has room. A resend under its key is parsed first, as it is answered before any limit. A large MCP message from a client past its window is refused unread. - **Order by cost** (`http::admit_to`): 1. A resend under its key is matched as received, reading nothing, taking no turn and counting nothing. 2. Refusals that read nothing are not counted: jobs at once, memory held, a full queue, a spent window. 3. Then a turn, then the count, so a request told to try again has spent nothing; there is no refund. 4. Then `cheap_bounds`, then every program read, each once, within 22,000,000 steps. `Retry-After` on a `429` is the window's pace. - **In a child** (`offload.rs`): admission, dry runs, every check's bounds and answer, the engine calls, a set's receipt and the MCP verdicts run in `wai-quantum-service read`, sandboxed as a worker is. Each child is capped: - memory, `WAI_QSVC_READ_GB` (4), polled every 50 ms from a thread of its own; - time, `WAI_QSVC_READ_SECS` (45), and the answer's deadline; - its answer, `WAI_QSVC_READ_ANSWER_MIB` (128) as parsed, about 80 bytes a value. It is killed by a signal to its process group, sent again until it ends. An abort, a stack overflow or a panic there is that request's `500`, and a hang-up ends the child. - **Turns** (`offload::Gate`): four reading turns and four check turns. A client's share is 2 reading turns and 1 check turn (`WAI_QSVC_CLIENT_READS`, `WAI_QSVC_CLIENT_CHECKS`). Past it, one more turn is lent to the client while two stay free for others (in the checks; the reading pool lends none at a share of two), and none while one of the client's own turns is free or held by a request still waiting for the pool's. A lent turn goes back after 5 s once another client's request waits, or once one of its own client's has waited 7 s, so a client's quick calls pass its own slow ones, however many. Up to eight of a client's requests wait behind its turns (its eleventh at once is refused), 32 wait in all, each for 10 s. Past these the request gets `503 J032`, saying whether its own requests or the pool held it up. - **Budget**: a check's answer reads inside the same budget as its bounds. A spent budget is a refusal whatever the code made of it. The QEC calls measure each point's setup and a pilot of its shots before running the rest. They are refused at once past `WAI_QSVC_QEC_SECS` (25) and stopped a fifth past it. - **MCP**: a tool that says nothing about a program takes no turn. A verdict reads only for a call that was counted, and only if time is left within the answer. A poll states what its job holds and reads nothing. A call refused before its count, told to try again, or out of time states a verdict giving the reason, so every answer keeps its schema. - **What ended jobs keep**: the request's canonical text, not its parse, and a set lets go of its items when it ends. Read results are held to `WAI_QSVC_CLIENT_KEPT_MIB` (128) per client, oldest first, and `WAI_QSVC_KEPT_MIB` (4096) in all, the largest holder's first; unread ones are dropped by neither, and hold their client's next job past `WAI_QSVC_CLIENT_UNREAD_MIB` (256) and everyone's past `WAI_QSVC_UNREAD_MIB` (8192), every job queued or running counted at its result's bound. A result is held to `WAI_QSVC_RESULT_MIB` (64) before its job runs, by a bound read from the request (below). - **Long polls**: a client holds at most `WAI_QSVC_CLIENT_STREAMS` (4) long polls and event streams; a long poll past them is answered `429` (`503` past everyone's 512), `J032`, with `Retry-After: 2` and the job's status. - **Connections and restarts**: `http::serve` holds a header section to `WAI_QSVC_HEADER_SECS` (150, above a proxy's 2-minute idle pool) and a body to `WAI_QSVC_BODY_SECS` (30, then `408`). A restart reads no program. Readers and workers start from the service's own binary as it was started, so the file can be replaced before the restart. ## Findings All 33 of the #676 review, with the test that holds each. "Before" is the code before this round (26a001db6) with the test grafted on, on the iMac, unless the row says otherwise. | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1 | QEC index allocations | `MAX_DETECTORS`, `MAX_QUBITS` at parse time, offsets counted | `an_index_past_what_a_text_holds_is_refused_on_its_line`, `an_index_past_what_a_model_holds_…` | fails; on e54 the 79-byte POST ended the process (review run) | | 2 | refused MCP task read inline | reads only if counted, in a child; `J034` before the count reads nothing | `a_refused_mcp_task_reads_nothing_and_holds_no_worker` | fails: the program is read | | 3, 6 | array memory | bytes bound before anything is made (`Q130`), in the earlier commit of this PR | `programs_that_would_hold_without_limit_…`, `what_a_program_would_hold_…` | on e54 the run took the host's 64 GB and was killed | | 4 | array rank | `MAX_ARRAY_DIMS` = 7 in the parser | `an_array_past_its_dimensions_…` | fails | | 5, 7 | verify of a set | counted, in a child, in a budget; signature first, so a forged seal writes nothing out | `a_check_and_a_set_s_receipt_count_before_they_read`, `a_seal_that_does_not_hold_is_not_written_out` | both fail | | 8 | budget refusal read as zero work | `within_budget` refuses on a spent budget whatever `read` returned | `a_budget_spent_is_a_refusal_whatever_the_check_made_of_it` | fails | | 9 | Blossom CPU | `QEC_MAX_FIRED` = 1,024, and the child's deadline | `a_decode_is_held_to_the_detectors_it_is_given` | fails | | 10 | reads hold pools | killable child with a deadline (120 s in round 1, 45 s since round 2, not 300); size charged in steps; a client's share of the turns | `a_reading_past_its_time_is_stopped_and_a_hang_up_ends_it`, `reading_leaves_the_runtime_free_and_a_client_has_its_share`, `a_client_is_held_to_its_share_of_the_turns` | the deadline test passes on 26a001db6, which already had the child; the share test fails: three requests all waited | | 11 | refund buys free reads | no refund; a key is matched against the digest as received, before any count or read | `a_source_job_sent_again_under_its_key_reads_nothing_and_counts_nothing` | new API, not grafted; the review's run gave 17 free reads on e54 | | 12 | MCP reads uncounted | `Reading` says what a verdict may read; a tool with no verdict takes no turn; polls read nothing | `a_call_that_was_not_counted_reads_nothing_and_still_states_a_verdict`, `the_tools_are_answered_with_no_turn_to_be_had`, `a_poll_reads_nothing` | the first fails; the others use new API | | 13 | unbounded waiters | at most 32 wait, a client's share first | `a_wait_for_a_turn_is_bounded_in_time_and_in_number` | new API; the review measured 300 waiters at 34 MB each | | 14 | answer outside the budget | bounds and answer in one budget | `a_check_s_answer_is_held_to_its_budget` | fails: no answer in the test's 120 s | | 15 | Retry-After 5 | refusals that read nothing are not counted; `Retry-After` is the window's pace | `a_client_told_to_retry_spends_nothing_…` | fails on the new header | | 16, 26 | resend refused | key lookup before turn and count, for `source` too | `a_source_job_sent_again_…` (all turns held, window spent) | new API; the review reproduced both on e54 | | 17 | bit-string panic | `Q112`, and any panic is the child's | `a_bit_string_wider_than_an_integer_…`, `programs_that_would_hold_…` | panicked on e54 | | 18 | stuck connections, slow restart | `http::serve` with header and body limits; a restart reads no program | `a_stuck_connection_is_let_go`, `a_restart_reads_no_program` | both fail | | 19 | QIR and jeff conditions | flat runs read at any length; `MAX_COND_DEPTH` 30, what the reader takes | `a_condition_that_fits_reads_back` | fails | | 20 | flat chains | settled: the limit was not needed, so `E::Chain` and `S::If { arms }` hold them flat, any length, on 1 MiB, same results | `a_run_of_operators_and_an_else_if_chain_read_at_any_length` | fails | | 21 | switch case list | `Ct::All`, `Ct::Any`, flat | `a_switch_on_measured_bits_lists_any_number_of_values` | aborts: stack overflow | | 22 | recursion under MAX_NEST | kept, as the skeptic advised; stated in the matrix, the changelog and below | text | not a code change | | 23 | generator disagrees with reader | flat runs read; the writer follows an `else if` chain in a loop | `both_readers_of_a_generator_take_a_flat_sum` | fails | | 24 | answer memo order | a result the limit cut short is kept by its depth, and a kept result carries its chain's length, so order changes nothing and shared references do not multiply | `the_chain_limit_refuses_the_same_statements_in_any_order` | fails | | 25 | own program refused | `READ_STEPS` = `MAX_STEPS` + `MAX_OPS`, 22,000,000; texts corrected | `the_budget_is_as_large_as_one_program_may_be`, `a_job_within_the_reader_s_bounds_reads_here` | both fail | | 27 | no test of a generator's spend | test added, and it covers a fat body too | `a_generator_s_passes_and_what_they_write_spend_the_budget` | passes; with the spend removed (M19) it fails | | 28 | off-runtime and count order untested | tests added | `reading_leaves_the_runtime_free_…`, `a_check_and_a_set_s_receipt_count_…`, `a_request_with_no_turn_is_refused_in_time_and_spends_nothing` | first two fail | | 29 | Q312, J031, J032 rows | reworded as additions; J020 too | table test | text | | 30 | OpenAPI, README | 503, 500, 422 and the bounds row; `/v1/verify`'s refusals | `the_openapi_document_names_every_route` | text | | 31 | changelog | rewritten | | text | | 32 | PR body | this text | | text | | 33 | tests that could only hang | `ends_within`: a thread and a deadline, by name | the two tests | the review's mutants ran to the job's limit | ## Second review: the machinery (7 findings) and the correlated-error carrier "Before" is 264cec659 (this branch with main merged, before these fixes), the new tests grafted on, run on the Framework (Linux x86_64). | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1 | one client held every check turn; at-bound QEC calls ran past the cap by more than 10x | each pool has its own per-client share, smaller than the pool: `WAI_QSVC_CLIENT_READS` (2 of 4) and `WAI_QSVC_CLIENT_CHECKS` (1 of 4, up from a pool of 2). A client's requests past its share wait behind it, 4 at most, rather than being refused. QEC decode and threshold measure their shots as they run and are refused once the rest would take more than `WAI_QSVC_QEC_SECS` (25). Rounds are held to 21 | `one_client_cannot_hold_every_check_turn`, `a_qec_call_at_its_bounds_ends_well_inside_the_reading_s_time`, `a_time_holds_the_shots_sampled_and_decoded`, `a_memory_experiment_is_held_to_its_rounds`, `a_client_holds_no_more_of_a_pool_than_its_share` | the first fails (503 after 10.0 s); the second fails (422 at 120 s); the rest use new API | | 2 | a reading's answer was rebuilt at the child's size in the service, on a runtime worker | the child refuses an answer that would take more than `WAI_QSVC_READ_ANSWER_MIB` (128 MiB, down from 256 MiB of text) once read, value by value, before writing it; the service parses off its runtime and takes values rather than copying them; a dry run gets back plans and points only | `a_reading_s_answer_is_held_to_what_it_takes_once_read` (the service's own peak memory checked on Linux) | fails: the job is accepted, 202 | | 3 | the header timeout closed idle keep-alive connections at 30 s, before Caddy's 2 minutes | `WAI_QSVC_HEADER_SECS` defaults to 150; self-host.md pairs it with the proxy's idle limit, and its example sets `keepalive 60s` | `the_defaults_fit_inside_the_proxy_s` | fails (30 s) | | 4 | clients behind one address are refused at their third concurrent request | per-pool shares, both configurable; requests past a share wait rather than being refused; the README and self-host.md say that one address is one client | `reading_leaves_the_runtime_free_and_a_client_has_its_share` (five parallel calls from one client all answered) | fails: 503 | | 5 | replacing the binary before the restart broke every reading | readers and workers start from `/proc/<pid>/exe` on Linux, and on macOS from a copy kept beside the signing key, named by its digest; self-host.md gives the update procedure | `the_binary_can_be_replaced_before_the_restart` (rename on Linux, in-place copy on macOS; a critique and a run both answered) | fails: 503 J032 "could not start a reader" | | 6 | the memory cap is a 50 ms poll of resident pages, run on the runtime | the poll runs on a thread of its own (`Watch`) and reads the physical footprint on macOS (compressed pages included) and resident plus swap on Linux. On Linux a reader also caps its own address space at twice its cap plus 1 GiB (a failed allocation is answered 422), and readers and workers set `oom_score_adj` 1000. The README states exactly what a poll is and is not; macOS gives an unprivileged process neither kernel limit | covered by the depth tests' memory cases; the thread itself has no test that fails first | not grafted | | 7 | the reading cap (120 s) was longer than Caddy's 60 s response_header_timeout | `WAI_QSVC_READ_SECS` defaults to 45, so the cap plus the 10 s wait for a turn fits inside 60 s; documented with how to lower it behind a shorter proxy | `the_defaults_fit_inside_the_proxy_s` | fails (120 s) | | #675 | the correlated-error instruction adds one carrier qubit after the text's indices are checked | lower() checks the circuit's width, carrier included, against `MAX_QUBITS`, and refuses at the first correlated error's line | `a_correlated_error_with_no_index_left_for_its_carrier_is_refused` | fails, with the check removed | The QEC timing was measured on Thor (Linux arm64, load 12 to 17 on 14 cores) against a local release build with the defaults: | Call | Answer | |---|---| | threshold, surface, distances [7,7,7,7], p 0.04–0.055, 12,500 shots | 422 in 1.0 s (projected 1,230 s) | | decode, memory d=7 r=7 p=0.05, 200,000 shots | 422 in 1.0 s (projected 3,473 s) | | decode, memory d=7 r=21 p=0.01, 200,000 shots | 422 in 1.0 s | | threshold, surface d=7 ×4, p 0.007–0.01, 12,500 shots | 422 in 1.0 s (projected 125 s) | | decode, memory d=7 p=0.005, 200,000 shots | 422 in 0.9 s (projected 91 s) | | threshold with its defaults | 200 in 10.2 s | | decode, memory d=5 p=0.001, 200,000 shots | 200 in 1.0 s | Not done: the macOS memory poll was not tested under memory pressure. Copies of earlier binaries beside the key are not removed automatically. ## Third review (14 findings) "Before" is 4f44194bf (the previous head) with the new tests grafted on, run on the Framework (Linux x86_64). Memory figures are the verifier's harness (`skp_mem.py`), on Thor (Linux arm64) and the M4 (macOS arm64). | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1 | ended jobs kept their parsed request for a day; parsing before the gate | a job keeps its request's canonical text, and a set lets go of its items when it ends; what ended jobs keep is capped per client (`WAI_QSVC_CLIENT_KEPT_MIB`, 128) and in all (`WAI_QSVC_KEPT_MIB`, 2048), oldest first; bodies are parsed after the window check; job reports are spliced from the text; memory is given back after large requests; on macOS the service starts with `MallocLargeCache=0`; `weight` counts 80 B a value | `ended_jobs_keep_little_and_a_restart_keeps_it_little`, `ended_jobs_keep_their_request_as_text_within_their_caps` | fails: 1,072 MiB after 30 jobs | | 2 | MCP runs passed Caddy's 60 s | one deadline from arrival, `WAI_QSVC_ANSWER_SECS` (55): turns, readings and run waits end by it; an MCP verdict is read only if time is left; self-host.md gives each route's arithmetic | `a_run_s_answer_comes_within_the_answer_s_time`, `the_defaults_fit_inside_the_proxy_s` | fails: 202 after 25.1 s | | 3, 8, 13 | a client's quick calls waited behind its own slow one; bursts of 6+ refused; misleading message | one turn is lent past the share while two stay free, for 5 s; 8 may wait per client; the refusal says whether the client's own requests or the pool held it up | `a_client_s_quick_calls_pass_its_own_slow_one_and_its_burst_is_answered`, `a_client_holds_no_more_of_a_pool_than_its_share_and_a_quick_call_passes`; `one_client_cannot_hold_every_check_turn` still holds | fails: 503 J032 | | 4, 7, 9 | the QEC projection multiplied setup; a default ran 23 s before refusal; code capacity was untimed | each point's setup and sampling are timed apart and a pilot of its shots (at most 1 %, up to 0.5 s) measures its rate; every point is measured before the rest run, so a call is refused at once or run; code-capacity points are piloted too; stopped a fifth past the time | `a_time_is_measured_apart_from_setup_and_holds_every_threshold`, `a_time_holds_the_shots_sampled_and_decoded` | fails: a call that fits refused at 0.6 s | | 5 | WAI breaking gate | the two Breaking lines; `wai breaking --against e54f28102 --text --all` exits 0 (and did against 5df2ccbde before main was merged) | the gate | exit 1 | | 6 | `jobs::kill` spawned `kill -9` | `libc::kill` on the child's process group (children get their own), again every 100 ms until it ends; job workers stopped the same way; a small answer is parsed inline, so no thread is needed | `a_reading_is_stopped_when_no_process_can_be_started` (Linux, `prlimit --nproc=1`) | fails: connection dropped | | 10–12, 14 | text | the critique refusal stated (200,000 gates refused, 150,000 answered); this body; OpenAPI statuses on every check route; the poll's empty critique in the changelog and README | | | Memory, one client, 30 jobs of a 999 KB body (the harness's `retain`), then a restart from the same store: | Host | Body | Before (verifier) | After 30 jobs | After restart | |---|---|---|---|---| | Thor, Linux | `[0,0,…]` | 1,055 MiB, 1,036 after restart (Framework) | 41 MiB | 36 MiB | | Thor, Linux | `[{"a":0},…]` | 2,119 MiB, 1,752 after restart (Framework) | 38 MiB | 36 MiB | | M4, macOS | `[0,0,…]` | 2,096 MiB, 1,061 after restart | 4 MiB | 32 MiB | Requests refused at the window, 120 at once (`transient`): Thor 11 MiB peak; the M4 4 MiB peak, where the verifier saw 2,333 MiB. ## Fourth review (11 findings) "Before" is 53676a3e5 (the previous head) with the new depth tests grafted on, run on the Framework (Linux x86_64). | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1, 4 | the open-file limit stayed at 256 and connections had no cap, so held connections took every descriptor | the service raises its soft open-file limit to its hard one as it starts (on macOS to `kern.maxfilesperproc` and 10240 at most) and logs it; it holds at most `WAI_QSVC_CONNECTIONS` (4096) connections, fewer under that limit (a quarter, at least 64, left for pipes and files); past them the connection idle longest, or the one whose body has been arriving longest past a second, is closed for the new one, and with none to close the new one gets `503` at once; a client may have `WAI_QSVC_CLIENT_REQUESTS` (32) requests open, refused `503` before its body is read (a health check exempt) | `held_connections_leave_room_and_health_answers` (hard and soft limit 256: 400 header sections from one client, 400 bodies from 400 clients, 400 bodies from one, health 15 of 15 each time, no `Too many open files`) | fails: one client's 400 held bodies are not refused; the verifier's probe had health 0 of 15 (in this harness the old build still answered health) | | 2 | a result over the client's cap was dropped as it ended; `/v1/run` 202 then 404; MCP `isError: false` with no result | a result is kept as its RFC 8785 text, counted at its length; an ended job not yet read is kept for `WAI_QSVC_KEPT_MIN_SECS` (600) whatever it weighs; while a client's unread results hold its cap its next job is refused `429`, and while everyone's hold the service's, `503`; an MCP run whose job is gone is an error | `a_result_larger_than_its_client_keeps_is_kept_until_it_is_read` (16 qubits, 20,000 shots a point, cap 1 MiB: poll, `/v1/run` and `run_quantum_set`) | fails: 404 0.4 s after submission | | 3 | a check on a lent turn was cut at 5 s after its client's own turn was free | a lent reading moves onto the client's own turn as one comes free; past 5 s it runs on while no other request waits for a turn | `a_documented_qec_call_beside_another_check_from_its_client_is_answered` (a critique and a decode at once; two decodes 30 ms apart) | fails: 503 J032 at 5.0 s | | 5 | global eviction was plain LRU | past the service's cap the client keeping the most loses first, the newest between equals | `ended_jobs_keep_their_request_as_text_within_their_caps` | new rule, unit test | | 6, 7 | a job's bytes depended on the route and changed at a restart | one RFC 8785 writer for every route (poll, events, cancel, MCP job tools; MCP answers written as RFC 8785), request and result spliced in as kept, the receipt's keys sorted | `a_job_reads_the_same_on_every_route_and_after_a_restart` | fails: a report is not RFC 8785 | | 8 | a large MCP message past the window got 429 with id null | read only for id, method, tool and key; answered under its own id with a tool result; a keyed resend of a held job and a receipt check are read whole | `a_large_mcp_message_past_the_window_is_answered_under_its_own_id` | fails: 429, id null | | 9 | any Idempotency-Key skipped the window; `/v1/verify` parsed with no limit | the window is skipped only for a key the client holds a job under; receipt checks have their own window, `WAI_QSVC_CLIENT_VERIFIES` (120), asked before the body is read | `a_body_is_read_only_once_its_window_or_its_key_allows` | fails: 400 (parsed) past the window | | 10 | `tasks/result` held 50 s whatever the deadline | held for what is left of the answer's time | `a_task_s_result_is_held_within_the_answer_s_time` (`WAI_QSVC_ANSWER_SECS` 10) | fails: answered after 50.0 s | | 11 | no 408 in the OpenAPI document | 408, 413 and the pre-handler 503 on every route | `the_openapi_document_names_every_route` | no route listed 408 (the verifier's probe) | ## Fifth review (8 findings) "Before" is a0804db72 with this head's `tests/depth.rs` copied in (a worktree of a0804db72), run through `rb` on the Framework (Linux x86_64) and the M3 Ultra Studio (macOS arm64), each test alone, under `ulimit -Sn 256` and under `ulimit -n 256`. Findings 1 and 2 are now tested by the round-5 verifier's own probes and timing. The earlier versions of these two tests passed on a0804db72 because they were not those probes. The first held its polls on `long_job()`, not the verifier's pump, mixed a third of them with event streams, sent them from 8 addresses with no pacing, and checked health after 1 s; it never checked that the job still ran or that any poll was held, so which of these let it pass was not isolated. The second raised `WAI_QSVC_QEC_SECS`, `WAI_QSVC_READ_SECS` and `WAI_QSVC_ANSWER_SECS` and sent the README's threshold as it is, which takes about 6 s alone on these hosts, inside the 10 s a waiting call is given; the critiques waited for it and were answered, and the test asked only for 200s, not when. | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1 | long polls and event streams, never evictable, could fill the connection cap and health got 503 | four connections past the cap are held for health checks alone (one request each, 2 s); long polls and event streams are held to `WAI_QSVC_CLIENT_STREAMS` (4) a client and `WAI_QSVC_STREAMS` (512, a quarter of the connections at most) in all; past them a long poll is answered at once with the job as it is (round 6: `429` with `Retry-After`), and an event stream is refused 503 | `long_polls_and_streams_leave_a_health_check_answered`, the verifier's probe (`_sk/probe.py`): its pump (a T layer and a CX ladder, 30 times, one shot; 24 qubits where it had 22, so a faster host still runs it past every wait), then 210 `?wait=25` polls with the probe's own `X-Forwarded-For` values (`10.0.k.m`), 3 ms apart, under a hard limit of 256 (192 connections), then 25 health checks; then the same with event streams. It checks that the job still runs and that polls were held, then asks for 25 of 25. Under `ulimit -n 256` the test process itself holds 64, and lowers the service's cap to 58 | **fails** on both hosts and both limits: `health answered 0 of 25 beside 210 long polls, 192 of them held: open files: 256, the hard limit`; under `ulimit -n 256`, `0 of 25 beside 64 long polls, 58 of them held`. Head: 25 of 25, polls and streams, on both hosts and both limits | | 2 | a long check moved onto its client's own turn kept the lent one, so the client's quick calls waited 10 s and were refused | a reading that moves onto its client's own turn gives the lent turn back, so the next quick call is lent one | `the_readme_s_burst_from_one_client_is_answered`, the verifier's burst (`_skp/lm.py`) at the service's own limits, nothing raised: the README's decode at 0 ms, a threshold at 30 ms, six Bell critiques from 60 ms, 30 ms apart, from one client, over HTTP and then over MCP. The threshold is made long as the verifier made it (it raised the shots to 30,000): shots raised half again at a time to 33,333, then its rounds, until it takes 11 s alone, past a waiting call's 10 s. Every call must be answered, and each critique at least 1 s before the threshold | **fails** on both hosts and both limits: Framework, threshold alone 12.9 s and 14.1 s, every critique `503 … your own 1 turn of this pool is still taken by your other requests after 10 s [J032]` at 10.07 to 10.21 s; M3 Ultra, alone 18.8 s and 11.1 s, refused at 10.07 to 10.22 s. Head: every critique answered, at 0.2 to 3.8 s, each at least 1 s before the threshold, HTTP and MCP | | 3, 4, 5 | unread results could 429 a shared address's co-tenants for long, largest-holder eviction dropped read results early, about 16 identities could hold every next job with a renewable 503 | read and unread results counted apart; only read ones are dropped before their time; a client's next job waits while its unread results, with each running job at its own result's bound and the new one at `WAI_QSVC_RESULT_MIB` (64), would pass `WAI_QSVC_CLIENT_UNREAD_MIB` (256); every next job waits while all unread hold `WAI_QSVC_UNREAD_MIB` (8192, 32 clients' whole allowances); `WAI_QSVC_KEPT_MIB` is 4096; a result's size is held to `WAI_QSVC_RESULT_MIB` before it runs (below); the README states the bounds that remain | `read_results_go_before_unread_ones_and_unread_ones_hold_their_client_s_next_job`, `a_result_s_bound_is_held_to_the_cap_and_running_jobs_count_their_own`, `a_result_past_what_a_job_may_give_is_refused_before_it_runs` | new rules | | 6 | MCP job reads were about twice main's time; tasks/get built the whole report | a task's state is read from the report without its request and result; MCP answers are written by serde, with RFC 8785's numbers, not sorted again | by reading | not measured again | | 7 | MCP ids past 2^53 came back changed | integers within 64 bits are written exactly as read (round 6: every id as the text it was sent as) | `an_mcp_id_is_echoed_exactly` | fails: 9007199254740993 answered as 9007199254740992 | | 8 | self-host.md put the 192 cap on the macOS default | corrected: launchd's default hard limit is unlimited and the service raises its soft limit itself; 192 only at a hard limit of 256 | text | text | ## Sixth review (16 findings) "Before" is d5564748e with this head's new tests copied in (a worktree of d5564748e: `engine::bound_tests`, the new tests of `tests/depth.rs` and the changed `a_reading_s_answer_is_held_to_what_it_takes_once_read`, and the two offload unit tests), run through `rb` on the Framework (Linux x86_64), each service test alone under `ulimit -Sn 256`. Every one fails there and passes here. | # | Finding | Fix | Test | Before | |---|---|---|---|---| | 1 | a failing sampled intent's fit listed every outcome it rules out, in `impossible`, its `note` and twice in its `why`; the bound counted 64 | `stats::goodness_of_fit` names at most 64 of them and gives how many (`impossible_outcomes`, new, in the verdict schema); the reason says the note once; the bound counts each intent's four lists at 64 outcomes each, and the statement's two reasons | `engine::bound_tests::a_failing_intent_trajectories_and_notes_stay_within_their_bounds`, and the property test `no_random_job_s_result_passes_its_bound` | **fails**: `a sampled intent ruling out 4,095 outcomes: its result is 2713555 bytes, past its bound of 149528`; the property test's first: `job 24 (seed 676), sampled intents that fail: its result is 245762 bytes, past its bound of 145949` | | 2 | a `trajectories` run lists `options.trajectories` samples; the bound took the shots | `catalog::samples_listed`: the trajectories (or the shots, the larger) for a trajectories run, on a device target too | the same test, `one shot, 30,000 trajectories` | **fails**: `one shot, 30,000 trajectories: its result is 71578 bytes, past its bound of 17751` | | 3 | an MCP dry run planned the job without the verdict every MCP run asks for, so it said taken what the run refused (J031) | `mcp::as_run`: the run tools and both dry-run paths plan the same job; `dry_run_quantum_job`'s description says so | `an_mcp_dry_run_says_what_the_run_tools_take` (cap 1 MiB): the most points the MCP dry run takes are run by `run_quantum_set` to `done`, and one more is refused | **fails**: `16 points, accepted by the MCP dry run, refused by the run: its result could take up to 1.5 MiB as served` | | 4 | the README's 100 points × 30,000 shots were admitted and stopped for memory: the set's worker held every item's result as a tree | the worker writes each item's result as text as it ends and drops its tree, so the set holds one item's tree and the text | `the_readme_s_largest_sweep_is_taken_and_ends_done`, at the defaults | **fails**: `the job used 656 MiB, over the 258 MiB it was admitted with; it was stopped` | | 5 | a client's second long check, on a lent turn, was never cut for its own client's waiting calls, so they waited out 10 s | a lent turn goes back once another client's request waits, or once one of its own client's has waited 7 s (`offload::OWN_PATIENCE`): the quick call takes it 1.5 s or more before its wait ends, and a call of its own that ends sooner (the README burst's decode) frees a turn first | `a_client_s_quick_calls_pass_two_of_its_own_long_checks`: two thresholds of 11 s or more, then four critiques at 4 s, from one client | **fails**: every critique `503 your own 1 turn of this pool is still taken by your other requests after 10 s [J032]` at 14.00 to 14.09 s | | 6, 15 | a JSON-RPC id of `1.0`, `1e3`, or an integer past 64 bits came back as another number | `http::raw_id` and `http::mcp_text`: every MCP answer writes the id as the text it was sent as (`serde_json`'s `raw_value`), on every path (an answer, `tasks/result`'s event, a message past the window) | `an_mcp_id_is_echoed_as_it_was_sent`: `1.0`, `1e3`, `1E3`, `-0.0`, `1.50`, 2^64, `123456789012345678901234567890`, `"1.0"`, on `ping` and `tools/list` | **fails**: `ping: sent the id 1.0, answered {"jsonrpc":"2.0","id":1,"result":{}}` | | 7 | a verdict lists the reader's notes again, and the bound counted them once | the verdict's part counts them, and the repair, again | `a_failing_intent_trajectories_and_notes_stay_within_their_bounds`, `3,000 notes and a verdict` | **fails**: `3,000 notes and a verdict: its result is 704773 bytes, past its bound of 409945` | | 8 | a set's items' requests were counted at serde's length and a sixteenth; RFC 8785 writes `1e20` in 21 characters | `receipt::canonical_len` (new): a value's length as RFC 8785 writes it, each number in its own form and whole numbers as read, whichever is longer; every request part of a bound uses it, and the provenance's `shots` and `seed` as sent are counted | `a_set_s_bound_counts_its_items_requests_as_served` (3 points sharing 20,000 `1e20`s) | **fails**: `served 1327064 bytes, past the bound of 454321` | | 9 | a dry run read without keeping the items' requests, so the read-answer weight never refused it | the child reads a dry run as its submission (kept), holds that answer to the weight and its text to the answer's length, then gives back the plans and points alone; a reading refused for its size is the dry run's refusal (`accepted: false`, 422) | `a_dry_run_that_says_taken_is_taken_by_its_submission` (100 points sharing 12,000 and 20,000 `1e20`s); `a_reading_s_answer_is_held_to_what_it_takes_once_read`, which held the old behaviour, now holds this | **fails**: `pad 20000: the dry run said true (null), the submission 422 ... gave more than 128 MiB once read` | | 10 | honest Clifford sweeps were bounded as if every shot were a distinct outcome | `catalog::outcomes_bound`: at most `2^b` outcomes for the `b` bits its measurements write, and on the noiseless `stabilizer` method `2^d`, `d` its measured qubits and its gates that branch (`h`, `sx`, `sxdg`; the rest permute or phase the basis), each at most doubling the outcomes a tableau gives | `an_honest_clifford_set_is_taken_by_the_outcomes_it_can_give` (100 GHZ24 at 30,000 shots, run to `done`); `a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list` | **fails**: `its result could take up to 85.1 MiB as served`; `GHZ24, 30,000 shots, stabilizer: bounded at 887668 bytes, where its result is 940` | | 11 | a verdict was charged 512 bytes a gate, so a 124,700-gate loop with nothing to remark on was refused, its reason blaming the counts | `critique::smells_bound` (new): the walk the critique makes (one walk now serves both), each smell it finds at 256 bytes and its qubits, and the idle and unmeasured lists; counted from the program as read, before any compile; a program holding a gate whole keeps the per-gate charge. A refusal names the bound's largest part and what makes it smaller (`catalog::Bound`, new: counts, verdict, notes, requests, rest) | `a_long_loop_s_verdict_is_taken_and_a_refusal_names_its_part`: the loop over MCP and `/v1/run` with `verdict: true` (the Studio's request), and three refusals, each naming its part; `a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list` | **fails**: `the MCP run: its result could take up to 64.7 MiB as served (its counts listing ...)`; `a loop of 124,700 gates, with its verdict: bounded at 67880108 bytes, where its result is 1719` | | 12 | a long poll past its client's holds was answered 200 at once, unchanged, so a README client spun | answered `429` (its client's) or `503` (everyone's), `J032`, with `Retry-After: 2` and the job's `status`; the README, the OpenAPI `wait` parameter and its responses say so; the command line's `follow` waits and polls again | `a_long_poll_past_its_client_s_holds_says_when_to_poll_again`; another client's poll is held | **fails**: `a fifth hold: HTTP/1.1 200 OK` | | 13 | the service-wide unread cap counted only ended results, so jobs taken under it ended past it | every queued and running job counts at its bound, the new one at its own, before reading and again under the queue's lock as the job is kept (inserted with it, so no two pass on one count) | `ended_jobs_unread_stay_under_the_unread_cap` (30 clients, 2 MiB cap, bound 621,764 each) | **fails**: `30 of 30 taken` | | 14 | the reading pool lends no turn at a share of two, yet admitted share + 1 + 8 | a client's requests are held to its share, a lent turn where the pool can lend one at that share, and eight waiting: its eleventh at once is refused, of readings and of checks; and no turn is lent while one of the client's own is free or taken by a request still waiting for the pool's, which closes the race the verifier saw (three readings at once in 2 of 14 bursts) | `offload::tests::a_client_s_eleventh_request_at_once_is_refused_and_eight_wait`; and `a_client_at_its_share_of_the_readings_is_never_lent_one` (50 bursts), a guard only: in-process it passes on d5564748e too (3 of 3 there), so the race is closed by construction and not shown failing here | **fails**: `Reading: your own 2 turns of this pool are still taken by your other requests after 10 s` (the eleventh waited) | | 16 | the advisories step failed on RUSTSEC-2026-0459 | main merged once, at 1929b8436 (rusqlite 0.41, fc67077fc, and the corpus-licence self-test fix, a4766c993); main's own vanity-metric fix for two of its lines (4c5b54a27) is cherry-picked, as 1929b8436 had them | the repository checks | `RUSTSEC-2026-0459 libsqlite3-sys 0.30.1` at d5564748e | ## The result cap, at admission A job's or a set's result is bounded before it runs. A request whose bound passes `WAI_QSVC_RESULT_MIB` (64 MiB) is refused at once, `422` and `J031`, with the bound, its largest part and what makes that part smaller in the reason, on `POST /v1/jobs`, `POST /v1/run` and the MCP run tools. Before, such a job ran and failed as it ended. - **The bound.** It is computed by `catalog::plan_bounded` (and `catalog::plan_bound`, by part: `catalog::Bound`, counts, verdict, notes, requests and rest) and `sets::plan_bounded`, and for a pulse run by `pulse::plan_bounded` through `Method::plan_bounded`. All are new and additive in the engine, and run in the reading child (`http::plan_job`). Each item's counts list at most the samples it draws (its shots; a `trajectories` run's trajectories), the outcomes its measured bits can give (`2^b` for the `b` bits written; on the noiseless `stabilizer` method `2^d`, `d` its measured qubits and its branching gates `h`, `sx`, `sxdg`), 65,536 outcomes, or what 16 MiB of bit strings holds. Each outcome is its bit string, quotes, a colon and a comma, and `k` counts summing to the shots have at most `k·(log10(shots/k) + 1)` digits. A verdict counts its fields, the reader's notes and the repair again, its critique by the smells the program's gates can draw (`critique::smells_bound`, the critique's own walk, from the program before any compile; a program holding a gate whole at 512 bytes a standard gate), each intent as given with its four lists of 64 outcomes, and two intents' reasons again in its statement. A sampled intent names at most 64 of the outcomes it rules out (`impossible_outcomes` says how many). Expectation values, amplitudes, diagnostics, outputs, a repair, a compile, a mitigation and a device target are counted by what each may hold, or at their length as RFC 8785 writes them (`receipt::canonical_len`: each number in its own form, a whole number as read where that is longer), as are the provenance's `shots` and `seed` as sent. 16 KiB covers the rest, plus a sixteenth for the numbers the engine writes. A set adds each item's request (at its served length), its frame and the head. A pulse run is bounded from its schedule as read: its populations by the qubits it simulates, its distribution by the captures it reads (each keyed as wide as the program's bits), its counts by its shots, and each capture with its frame's and bit's names. - **Where it is published.** The README states it beside the set limits (Sets and sweeps) and in the settings table. `GET /v1` gives `limits.result_bytes` beside `set_items`. So do the dry run's `bounds.result_bytes`, the MCP description's `sets.result_bytes` and the OpenAPI document (the set's description, `DryRun.result_bytes`, every `422`). A dry run states the request's own bound as `result_bytes`, named in `schemas/service-tools.v1.json`. - **The backstop.** The check as a job ends stays, for one case only: a job kept across a restart that lowered the cap. No built-in method is unbounded now. A method that gives no bound (the trait's default) is counted at the cap and held to it as it ends. These tests check that no admitted job reaches the backstop: - `engine::bound_tests::no_random_job_s_result_passes_its_bound` draws 96 jobs of every kind (failing sampled intents ruling out many wide outcomes, trajectories with their own count, verdicts over thousands of notes and smells with and without a compile, random Clifford circuits of 1 to 30 qubits measured into repeated bits on the stabilizer method or as planned, with and without noise, observables, amplitudes, a mitigation, a repair, a measurement in the middle, a seed sent as a long string or a list of long numbers, device targets, pulse runs, intents carrying numbers RFC 8785 writes long) and holds each result, as either RFC 8785 writer gives it, to its bound; it passed under 40 seeds besides its own. `a_failing_intent_trajectories_and_notes_stay_within_their_bounds` holds the review's three cases, and `a_ghz_state_and_a_long_loop_are_bounded_by_what_they_can_list` that a GHZ24 at 30,000 shots is bounded under 32 KiB and the 124,700-gate loop with its verdict under 256 KiB. - `engine::run::tests::no_result_passes_the_bound_its_plan_gave` runs the worked examples, plus a job for each part a result grows with: 65,536 outcomes, 300-bit outcomes, mid-circuit measurement, 256 observables, 64 amplitudes, a remark on every gate, repair, compile, noise, mitigation and a device target. Every result is within its bound. Where counts make the result, it is within 3% (1,438,608 bytes against 1,471,488). - `engine::pulse::tests::no_pulse_result_passes_the_bound_its_plan_gave` does the same for pulse runs: one capture, one bit of 3,000 captured, nothing captured, and two qubits captured. - `a_result_past_what_a_job_may_give_is_refused_before_it_runs` (service, cap 1 MiB) finds the largest sweep under the cap from dry runs. That sweep must be taken and end `done` within its bound (16 points: bound 1,037,576 bytes, served about 700,100). One more point must be refused at once on all three routes, and its dry run must say so. A pulse run of 100,000 bits must be refused at once; before, it was taken and failed as it ended. The two cases below are checked at the defaults. - **The default stays 64 MiB.** The published set at its most cannot be covered by any cap a public service should serve. 100 items of 65,536 outcomes at 16 bits have a bound of 147,646,836 bytes, about 150 MB. With wider bit strings an item reaches about 17 MB (16 MiB of bit strings), so 100 items reach about 1.7 GB. Raising the cap to 192 MiB would admit only the 16-bit case, at real cost: - such a result is held in the service's memory, unread, for up to 600 s; - a client's 256 MiB unread allowance would hold one of them; - an MCP read parses it and writes it again on one of the runtime's four threads (round 5 measured 1.3 s for 27 MB); - its answer passes the proxy whole. So the cap stays, and the README says plainly that such sets are refused and what is taken. At 64 MiB, 100 points of 65,536 outcomes are refused, and 100 points of 16 bits are taken up to about 30,000 shots a point: 30,878 without a verdict (bound 65,262,975 bytes at 30,000) and 30,003 over MCP, whose runs ask for one (67,100,475 at 30,000), measured by dry runs on a local build on both hosts. Both routes run 100 × 30,000 to `done` (6.6 and 6.8 s on the M3, 29.1 and 28.1 s on the Framework); `the_readme_s_largest_sweep_is_taken_and_ends_done` runs it at the defaults, and the test above asserts the refusal and the dry run. A client's running jobs count against its unread allowance at their own bounds, not at 64 MiB each. ## Known new refusals Programs main read and this refuses, or answers it gave and this does not. Each is a clean refusal with a code. - Real nesting past about 60 levels: parentheses, blocks, nested `if`s, a `**` or `^` power run, an index chain, a `++` chain, prefix operators (`Q006`). Flat runs and `else if` chains are no longer in this list. - A recursive subroutine whose body nests more than about seven levels reaches 256 before its 32 calls (`Q006`). - An array of more than 7 dimensions (`Q006`). - One value over 64 MiB, or 512 MiB held in all (`Q130`). - A QEC text naming a qubit or a detector at 2²⁴ or past it. - A QIR or jeff condition whose parentheses nest past 30 (`Q705`), from 32. - `REPEAT n { H 0 }` from about 25,000,000 passes, not 50,000,000 operations. - In the service: a check whose answer would take more than 128 MiB once read, such as a critique of a program of 200,000 gates that each draw a remark (422 J031; 150,000 are answered); a client's call on a turn lent beside its own that takes longer than 5 s once another client's request waits, or once one of its own requests has waited 7 s (503 J032, to send again); a client's eleventh request at once, of readings (its share of two and eight waiting; no reading turn is lent at that share) or of checks (its share, one lent turn and eight waiting) (503 J032); a run whose job outlasts the answer's 55 s answers with the job to poll; a decode of more than 1,024 fired detectors; a QEC decode or threshold whose shots, measured as they run, would take more than 25 s (`WAI_QSVC_QEC_SECS`), or that has more than 21 rounds; a reading that takes more than 45 s (down from 120); a set whose items would take more than 128 MiB once read; a check whose answer runs the budget out (a repair of many fixes to a slow program, an improve of a program near the budget, as each candidate is a new text); a set of readings past the child's caps; a `source` job resent in a form other than the one it was sent in is read and counted. - MCP: a poll of a queued or running job, or of an ended one sent without `verdict` or `intent`, gives a verdict with empty diagnostics and critique, and a view on a poll has no drawing; an answer whose verdict found no time left says so. - An ended job that has been read (or ended more than 10 minutes ago) is dropped before its `WAI_QSVC_RETAIN_SECS` once its client's read results keep more than 128 MiB, its oldest first, or all keep more than 4 GiB, the largest holder's first. A client's next job is refused (429) while its unread results, with its running jobs at their bounds and the new one at 64 MiB, would pass 256 MiB; while all unread results, with every job queued or running at its bound and the new one, would pass 8 GiB, new jobs are refused (503). - A job or set whose result's bound passes `WAI_QSVC_RESULT_MIB` (64 MiB) is refused at once, `422` and `J031`: for example 100 points of 65,536 outcomes (about 150 MB), or 100 points of 16 bits past about 30,000 shots a point (30,878 without a verdict, 30,003 over MCP). A verdict's critique counts about 270 bytes for each smell the program's gates draw (a gate that does nothing, a pair that cancels or merges), so a job asking for a verdict is refused only where its smells reach the cap (about 250,000 of them); a long loop with nothing to remark on is not. A refusal names its largest part and what makes it smaller. - A long poll past its client's 4 holds is answered `429` (`503` past everyone's) with `Retry-After: 2` and the job's status, where it was answered `200` at once. A dry run whose reading, kept as its submission's would be, passes what the service takes back is refused (`accepted: false`, `422`), as its submission is. - A client's 33rd request open at once is refused `503` before its body is read; past the connection cap a new connection is answered `503` where none can be closed for it. A client's 121st receipt check in its window is refused `429`. A body under an idempotency key the client holds no job under waits for the window like any other. ## Tests `tests/depth.rs` drives the binary over HTTP alone, so it runs unchanged against older code. The answer, dynamic and editor index tests compile on main unchanged; pulse, QEC and frame need only a literal for a constant. | Test | Before (main 4e17e96ef) | After | |---|---|---| | `tests/depth.rs`: deep programs through `/v1/jobs`, `/v1/run`, a dry run, an intent's reference, a set, a sweep, the MCP run tool and a task, five checks and a QEC decode, `/v1/health` after each | **fails**: the first POST ends the service (empty answer) | passes | | same file: sixteen heavy references refused in bounded time and counted | **fails**: no answer within the test's 120 s on the iMac, 4 of 4; on Thor a 202 after about 96 s, where 422 was wanted | passes | | `qasm::depth_tests` | not run on main, whose constants differ; the probe overflowed the constructs it measured | passes, debug and release | | answer chain, dynamic deep walk, editor index, pulse expression | each overflows the stack (SIGABRT) | pass, debug and release | | QEC blocks | fail at 33 levels: nothing refuses them | pass, debug and release | | QEC empty block, frame blocks | hang past 240 s | pass, debug and release | | `the_branches_sum_to_the_exact_mixture` | passes | passes | | wai-web wasm, `a_program_nested_past_the_reader_s_limit_is_refused_not_trapped` | traps: `RuntimeError: memory access out of bounds`, Node exit 1 | passes, 23 of 23, on the M4 | | CLI front door: `lint`, `run`, `plan`, `critique`, `equivalent`, `instance`, `qec decode` | `lint` ended by a signal (`front_door.rs:152`) | passes, 6 of 6 | | Python, `test_a_program_nested_past_the_readers_limit_raises_and_the_engine_goes_on` | the interpreter segfaults (exit 139) | passes; the whole `ci/check.sh` passes on the M4, 86 tests, its notebooks against a service started from this branch | The desktop app, on the iMac (macOS x86_64): built in debug, 22 tests pass. Its release build is left to CI: `wai-quantum-studio.yml` fires on this PR. Runs at this head (main merged at 1929b8436), every one through `rb`, each request sent only to a local build on loopback. The service's whole suite (unit tests, `depth`, `restart`, `tasks`, `verify`) ran under `ulimit -Sn 256`, on the committed tree: - **macOS, M3 Ultra Studio (arm64)**: - service: 116 of 116 (unit 71, `depth` 41, `restart`, `tasks`, `verify` 2); clippy clean; - library `--features full --lib`: 1,436 passed, 0 failed, 18 ignored; `tests/reader_stack.rs` 1 of 1; - CLI: 109 of 109; clippy clean with and without its default features. - **Linux x86_64, the Framework** (load 15 to 40 from other sessions): - service: 115 of 115 (unit 69, `depth` 42); clippy clean; - library: 1,435 passed, 1 failed, 18 ignored. The failure was `engine::qec::tests::a_time_is_measured_apart_from_setup_and_holds_every_threshold`, a QEC call refused for its projected time under that load (`its shots would take about 30 s … after 3.1 s, where it took 4.5 s with no time set`); this round touches no QEC code, and alone it passed 2 of 2. `tests/reader_stack.rs` 1 of 1; - CLI: 109 of 109, and clippy clean. - The library runs were of snapshots whose engine differs from this head only in how two of the new bound tests report (each failing case listed, not the first); the service runs were of this head's tree. - The property test ran under 40 seeds besides its own (96 jobs each), on the Framework: every result within its bound. - The README's sweep, measured by dry runs and runs on both hosts: 30,878 shots a point taken without a verdict, 30,003 over MCP, and 100 × 30,000 run to `done` on both routes (6.6 and 6.8 s on the M3, 29.1 and 28.1 s on the Framework). - Repository checks at this head on the Framework: lockfiles (67, current), advisories (67 lockfiles, 20 allowed), tests in CI, workflow concurrency, `pr-touches-paths`, wai-quantum names, the changelog's form and this change's entry, vanity metrics, the public-text self-test, comparative prose, public names (`--maintainer-products-only`), corpus licences, orphan sources, spec and section references, the conformance lists: each passes, with its self-test. Vanity metrics was red at 1929b8436 on two lines of main's own, fixed on main in 4c5b54a27 and cherry-picked here. The public-name check with the private list is clean on the 188 files this PR changes. - `wai breaking --against 1929b8436 --text --all` (on the M3; the Framework lacks `dav1d` for the API check's documentation build) exits 0: five breaking changes, each acknowledged in the entries: `E::Chain`, `S::If`, `Request::smells_bytes`, and main's `qasm::MAX_NESTING` and `qasm::MAX_ELSE_IF`, which this reader replaces. Earlier rounds' runs (at e02f5b79a, d5564748e and before) are in the history of this description; each round's fail-first evidence is in its table above. ## CI - **Round 6.** The advisories step failed at d5564748e on RUSTSEC-2026-0459 in jouleclaw's lock; main merged at 1929b8436 carries rusqlite 0.41 (fc67077fc), and it passes here. Main's reader nesting bound met this branch's reader in that merge (above). The service workflow's engine step names the three new bound tests, which run in its library filter. Two reds at 4f44194bf, fixed here: - **Main merged at e54f28102** (wai-quantum 0.9.0), so the contract's API check passes on the raised 0.y and main's fixes for the shared reds (the conformance corpora, the DVR tests, the native count) come with it. Two things met: main made a `for` loop's values lazily, as this branch did, and its type is kept; `tests/reader_memory.rs` nested 100 loops, past this branch's 64 levels (`Q006`), so it nests as deep as the reader nests loops (`parse::MAX_NESTING - 8`, 56), as `qasm::depth_tests` does, under the same memory bound. - **WAI energy receipts · macos** failed at 7586335a7 on `a_time_is_measured_apart_from_setup_and_holds_every_threshold`, in a debug build beside the library's other tests. A code-capacity point's pilot was one in a hundred of its trials run whole (2,000 trials of colour:15, 11 to 14 s there before the 3 s call was refused); it now runs in doubling batches held to half a second, as a circuit's pilot is, and the test's bound is 5 s, or twice its own decode's time where the host makes that longer (this round: it was refused after 8.2 s on the Framework at load 77, on its pilot). The setup-heavy decode in the same test is timed first and then given twice that and a second, where a fixed 15 s was a release build's figure. On the Studio, the whole library in debug failed this test in two runs of two before, and passed twice after (1,411 passed). - **WAI breaking** refused `E::Chain` and `S::If` as unacknowledged; the changelog fragment now carries their Breaking lines, and `wai breaking --against <merge base> --text --all` exits 0. The wai-quantum contract's major bump comes from main (0.9.0). - **jouleclaw public surface** failed on main (208ffd374), #659 and here within 25 minutes, on three runners. Each log stopped at about 256 KiB, in the middle of the scan's 800 KB report, and the job was failed twelve minutes later; the scan's own summary in each read `public surface only: 0`, and the same scan exits 0 on the Framework. The step now writes the report to a file and logs its summary, and on a failure the report's list of what is missing: 393 bytes of log on a pass, and an undocumented `pub fn` planted in a copy still fails it, naming the function. Pins: wai-web's engine wasm tests are 23, this PR's one and main's one; the pulse tests are 28, native and wasip2 (`no_pulse_result_passes_the_bound_its_plan_gave` is new); the service workflow's engine step names the two bound tests, and the QEC workflow `a_circuit_s_pilot_runs_for_its_floor_within_its_shots`. The new tests are in the named lists of the QEC and verdict workflows, and the reader's module filter takes `qasm::depth_tests`. No workflow names the dynamic walk's two tests: `wai-energy-receipts.yml` runs them, in debug, in the whole library. The repository checks pass: tests in CI, lockfiles, vanity metrics, comparative prose, orphan sources, workflow concurrency, the changelog's form and public names. The service's `Cargo.lock` gains `hyper` and `hyper-util` as direct dependencies, both already in its tree. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wai-quantum: the reader holds its nesting (Q006), and the job service reads each request once, within one budget, off its request threads
Some checks failed
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m36s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 13m24s
wai-quantum studio and desktop / desktop app · linux (pull_request) Failing after 30m32s
wai-quantum studio and desktop / studio · browser (pull_request) Failing after 30m59s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 18m50s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 17m7s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 4m4s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 1m43s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 34s
wai-quantum service / service energy · linux packages (pull_request) Successful in 1m39s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 34s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 20m46s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 4m23s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 40s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 14m36s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 32s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 48s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m38s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 23m37s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 7m56s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 53s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m32s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m2s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 32s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 19m22s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 2m35s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 3m48s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m12s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 21m56s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h38m5s
e54462b788
A POST of about 1.3 KB with some 600 nested parentheses in `qasm`, or in an
intent's reference, ran the reader's recursion out of a tokio worker's 2 MiB
stack and aborted the whole service. And a job's references were read three or
four times each before any count, size or rate check, so a few small requests
with heavy references held every worker for minutes, uncounted.

- The reader's nesting. The parser refuses past 64 levels in a program's text
  (qasm::parse::MAX_NESTING: statements, parentheses, operators, brackets,
  calls, casts and types together, each expression tree as deep as its
  statement leaves room for, so a chain built in a loop is bounded too), the
  elaborator past 256 in all through the gates and subroutines it calls; both
  with the new code Q006. Binary operators are read by precedence climbing and
  each statement, expression and elaboration form by a function of its own, so
  a level stacks a few small frames.
- Every other reader of a client's text: instantiate's integer expressions,
  loops and conditions (an else-if chain now followed in a loop), the pulse
  reader through the parser, the error-correction texts' REPEAT blocks (at most
  32 deep; every pass counts toward the unroll bound; unrun blocks checked once
  on one copy), the editor's index (passed over past the limit), the answer
  layer's statement chains (32), and the dynamic expectation walk (a list, not
  a recursion, in the same order).
- A budget of reading: qasm::within_steps (as branch #648 introduced it, read
  once per program, one budget of steps), extended to count gates and a
  generator's loop passes, and qasm::steps_left to tell its refusal.
- The service admits by cost: a key's resend; the count (every later refusal
  counts); what reads nothing (at most 16 intents, 100 set items, exact
  numbers); then every program read once within 20 000 000 steps for the whole
  request (422, J031, past it); then the queue. Checks are counted before their
  bounds read anything. Admission, the checks and the MCP tools' verdicts run on
  threads of their own with 64 MiB stacks, four at a time, a panic failing only
  its request.
- Tests: every construct nested thousands deep on a 2 MiB thread, refused;
  inside the limit, read; a seeded property test; the service binary started
  in a test and sent such programs through every door, answering /v1/health
  after each; heavy references refused in bounded time and counted; the wasm
  engine, the CLI and the Python package refusing cleanly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The 33 findings of #676's review, each with a test that failed first.

- QEC texts: a detector or qubit index past 2^24 is refused on its line,
  offsets counted, where 79 bytes asked for 2.4 PB.
- Reading: a flat run of operators, an else-if chain and a switch's case values
  are held flat and walked in loops, at any length, left to right, so they read
  on 1 MiB and the generator's writer agrees; an array has at most 7 dimensions.
- Every read of a client's program is counted first and runs in a child capped
  in memory, time and answer: admission, dry runs, each check's bounds and answer
  in one budget, the engine calls, a set's receipt, and MCP verdicts.
- Turns: a client's share, a bound on those waiting, and no refund. A resend
  under its key is matched as received, reading nothing, and comes back past the
  window and with no turn to be had. A budget spent is a refusal whatever a check
  made of it. Retry-After is the pace that would not spend the window.
- MCP: a tool that says nothing of a program takes no turn; a poll reads nothing;
  a call not counted states why, so every answer keeps its verdict and its schema.
- Connections are held to a time for headers and for a body. A restart reads no
  program: a job keeps its plan and a set its items.
- Answers: the chain limit refuses the same statements in any order. A decode
  is held to 1,024 fired detectors. Two tests that could only hang now fail by name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum-service: a reading cut off for its answer is refused however its child ends
Some checks failed
wai-quantum OpenCL member / member · OpenCL GPU and CPU (thor) (pull_request) Successful in 1m14s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 6m17s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 12m2s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 8m44s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 10m8s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 23m40s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 23m30s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 27m44s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m32s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 4m52s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 8m14s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 3m20s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 3m22s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 11m19s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 12m47s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 3m15s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 2m51s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 4m29s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 3m6s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 1m1s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 3m17s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 26s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Failing after 16m51s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 3m58s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 19m55s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 2m37s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m52s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 8m56s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (pull_request) Successful in 7m44s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 31m23s
de48547537
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dcharlot changed title from wai-quantum: the reader holds its nesting (Q006), and the job service reads each request once, within one budget, off its request threads to wai-quantum: no anonymous request ends the job service or holds it without limit 2026-11-07 02:52:19 -05:00
Co-Authored-By: Claude Opus 5.5 <[email protected]>

# Conflicts:
#	.github/workflows/wai-quantum-lang.yml
#	.github/workflows/wai-quantum-qec.yml
#	wai/wai-quantum/docs/qec.md
#	wai/wai-quantum/src/qasm/elab.rs
#	wai/wai-quantum/src/quantum_frame.rs
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum-qec workflow: name the test that holds a call's shots to its time
Some checks failed
wai-quantum command line / command line · linux (pull_request) Successful in 38m12s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 10m22s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (pull_request) Successful in 4m30s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (pull_request) Successful in 4m21s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 6m25s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 6m37s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (pull_request) Successful in 9m13s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Successful in 4m24s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 9m10s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m29s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 8m15s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 8m5s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 11m8s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m26s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 4m45s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 9m37s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 14m29s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 13m33s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m36s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 9m27s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 43s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 34m20s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 24m0s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m34s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 32s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 2m37s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 20m49s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 4m28s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 18m25s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 10m15s
4f44194bf0
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Third review of #676, 14 findings:

- A job keeps its request's canonical text, never its parse; a set lets
  go of its items when it ends; what ended jobs keep is capped per client
  (WAI_QSVC_CLIENT_KEPT_MIB, 128) and in all (WAI_QSVC_KEPT_MIB, 2048),
  oldest first. Bodies are parsed after the window check, reports are
  spliced from the text, memory is given back after large requests, and
  on macOS the service starts with the allocator's large cache off.
- One deadline from arrival, WAI_QSVC_ANSWER_SECS (55): turns, readings,
  run waits and MCP verdicts end by it; self-host.md gives each route's
  arithmetic under the proxy's 60 s.
- Past its share a client is lent one turn while two stay free, for 5 s;
  eight of its requests may wait; the refusal says whether its own
  requests or the pool held it up.
- QEC calls time each point's setup apart from a pilot of its shots,
  every threshold kind included, so a call is refused at once or run.
- Children are stopped by libc::kill on their process group, again
  every 100 ms until they end.
- The Breaking lines for E::Chain and S::If; OpenAPI statuses on every
  check route; the poll's empty critique in the changelog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The scan writes an 800 KB report at once. On 2026-11-07 three runs of it
(main at 208ffd374, #659 and #676), on three runners, kept a log that
stopped at 256 KiB mid-report, and each job was failed twelve minutes
later, though the scan's summary read "public surface only: 0". The
summary stays in the log, and on a failure so does the report's list of
what is missing; a pass logs 393 bytes. The error lines are single-quoted,
so their backticks are no longer run as commands.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge main (e54f28102, wai-quantum 0.9.0) into wai/quantum-parse-depth
Some checks failed
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 4m11s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 7m27s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Successful in 4m51s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 9m19s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 5m8s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
wai-quantum command line / command line · linux (pull_request) Has been cancelled
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Has been cancelled
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Has been cancelled
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Has been cancelled
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Has been cancelled
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Has been cancelled
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Has been cancelled
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Has been cancelled
WAI Studio / Studio in WebKit · linux (pull_request) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
7586335a77
elab.rs: main made a for loop's values lazily (LoopValues), as this branch
did (Values); main's type is kept, with its bits read one at a time, and
this branch's own type is dropped.

tests/reader_memory.rs nested 100 loops, past the reader's 64 levels on
this branch (Q006); it now nests as deep as the reader nests loops,
parse::MAX_NESTING - 8, as qasm::depth_tests does, and holds the same
memory bound at each depth.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum qec: a code-capacity threshold's pilot is held to half a second, and the timing test is held to its own build
Some checks failed
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Failing after 19m42s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (thor) (pull_request) Successful in 1m54s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 13m22s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 14m32s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 32m15s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 20m5s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 4m42s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 1m2s
wai-quantum command line / command line · linux (pull_request) Successful in 38m54s
wai-quantum service / service energy · linux packages (pull_request) Successful in 3m7s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 53s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 5m47s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 25s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 23m50s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 9m51s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 1m31s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m40s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m36s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 28m40s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m35s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 26m46s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 46s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m14s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Failing after 16m38s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 36s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 2m46s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 4m9s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m31s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 49m48s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
53676a3e51
A code-capacity point runs whole, so its pilot was one in a hundred of
its trials, run whole: 2,000 trials of colour:15 took 11 to 14 s in a
debug build beside the library's other tests (the macOS energy-receipts
leg at 7586335a7, and the Studio twice), before the call could be
refused for its 3 s. The pilot now runs in batches, each twice the one
before, while piloting() holds it, as a circuit's is: at least 8 trials,
then up to half a second. A trial has no setup of its own, so a batch's
time over its trials is the point's rate. The test's refusal bound is
5 s, from 10.

The setup-heavy decode in the same test is timed first with no time
set, then given twice that and a second: a fixed 15 s was a release
build's figure and refused it in a debug build under load.

Before, on the Studio, the whole library in debug: the test failed in
two runs of two (at the decode, and at the colour call after 14.5 s).
After: 1,411 passed, 0 failed, twice; Thor release 1,416 passed, and its
seven QEC tests in debug.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fourth review of #676, 11 findings:

- The service raises its open-file limit to its hard one as it starts
  (on macOS to kern.maxfilesperproc and 10240 at most) and logs it. It
  holds at most WAI_QSVC_CONNECTIONS (4096) connections, fewer under that
  limit; past them the connection idle longest, or the one whose body
  has been arriving longest, is closed for the new one, so a health check
  is always answered, and with none to close the new one gets 503 at
  once. A client may have WAI_QSVC_CLIENT_REQUESTS (32) requests open,
  refused 503 past them before a body is read.
- A job keeps its result as RFC 8785 text, counted at its length. An
  ended job not yet read is kept for WAI_QSVC_KEPT_MIN_SECS (600)
  whatever it weighs; while a client's unread results hold its cap its
  next job is refused (429), and while everyone's hold the service's,
  503. Past the service's cap the client keeping the most loses its
  results first, the newest between equals. An MCP run whose job is no
  longer known is an error, never a success without a result.
- A reading on a lent turn moves onto the client's own turn when one
  comes free, and runs past 5 s while no other request waits.
- Every route writes a job as one RFC 8785 text, its request and result
  spliced in as kept, the receipt's keys sorted, the same after a
  restart; MCP answers are written as RFC 8785 too.
- A large MCP message past the window is read only for its id, method,
  tool and key, and answered under its own id; a keyed resend of a job
  the client holds and a receipt check are read whole.
- A body is read before its window only for a resend under a key the
  client holds a job under; receipt checks have their own window,
  WAI_QSVC_CLIENT_VERIFIES (120), asked before the body is read.
- tasks/result holds its answer within what is left of the answer's
  time; the OpenAPI document lists 408, 413 and the pre-handler 503 on
  every route.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum-service: no more accepted than the descriptors let go, a lent deadline moved ahead of its watcher, and a job trimmed before its waiters read it
Some checks failed
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 5m50s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 11m47s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 23m19s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 2m27s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 1m12s
wai-quantum service / service energy · linux packages (pull_request) Successful in 2m54s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 38s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 6m15s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 8m59s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Failing after 8m21s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 7m40s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 2m0s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Failing after 12m5s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (pull_request) Successful in 10m15s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 2m0s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 2m4s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m23s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 31m43s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Failing after 15m46s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Failing after 12m57s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 23m35s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 6m45s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 3m24s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 1m17s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 3m12s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 24m54s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 3m40s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 4m59s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m38s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 33m6s
a0804db72a
- Past the connection cap, a few connections at a time are answered 503
  and the rest closed at once, and nothing more is accepted until those
  closed for room have let their descriptors go: under a limit of 256,
  400 connections at once never ran the service out of them.
- A lent reading's deadline is moved a second at a time, half a second
  before it is due, so the watcher never stops it at a time about to move.
- A finished job is trimmed before its waiters are woken, so a run's or
  a tool's own read of it does not leave it to be dropped as read.
- The report's placeholders are named outside the registry's name space;
  the receipt corpus test has a window for its receipts; the OpenAPI
  test reads operations, not path parameters; the lent-turn test gives a
  slow host the time its decode takes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum-service: health checks held past the connection cap, long polls and streams capped, a moved lent turn given back, read and unread results counted apart, and MCP ids echoed exactly
Some checks failed
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 30m58s
wai-quantum command line / command line · linux (pull_request) Successful in 32m43s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 23m33s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 1m52s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 48s
wai-quantum service / service energy · linux packages (pull_request) Successful in 2m20s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 20s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m52s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 17m3s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 6m11s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Failing after 6m8s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 11m22s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 54s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 12m50s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 3m39s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 16s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 46s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m57s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 20m40s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Failing after 15m50s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m22s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 37s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m10s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 29s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 2m50s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 4m14s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 3m20s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 8m36s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (pull_request) Successful in 7m39s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 2h12m2s
e02f5b79af
Fifth review of #676:

- Four connections past the cap are held for health checks alone, one
  request each for 2 s; long polls and event streams are held to
  WAI_QSVC_CLIENT_STREAMS (4) a client and WAI_QSVC_STREAMS (512, a
  quarter of the connections at most) in all; past them a long poll is
  answered at once with the job as it is and a stream is refused 503.
- A reading that moves from a lent turn onto its client's own gives the
  lent one back, and is stopped past 5 s only when other clients wait.
- Read and unread results are counted apart: only read ones are dropped
  before their time, the largest holder's first; a client's next job
  waits while its unread results, with WAI_QSVC_RESULT_MIB (64) for each
  running job and the new one, would pass WAI_QSVC_CLIENT_UNREAD_MIB
  (256), and every next job while all unread hold WAI_QSVC_UNREAD_MIB
  (8192); a result past 64 MiB fails its job, saying so; KEPT_MIB 4096.
- A task's state is read from the lean report; MCP answers are written
  by serde with RFC 8785's numbers, so ids are echoed exactly and a job
  keeps its report's bytes.
- self-host.md: the 192 cap holds only at a hard limit of 256.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A job's or a set's result is bounded before it runs, from the request as
read (catalog::plan_bounded and sets::plan_bounded, new in the engine): each
item's counts at most its shots, 2^bits and 65,536 outcomes, each its bit
string, quotes, a colon, a comma and its count's digits; expectation values,
amplitudes, diagnostics, outputs, a repair, a compile, a target, a
mitigation and a verdict by what each may hold or at their size as read; a
set's every item request and frame beside. A request whose bound passes
WAI_QSVC_RESULT_MIB (64) is refused at once, 422 and J031, with the bound
and the cap in the reason, on every route that takes a job; a dry run gives
the bound as result_bytes, and GET /v1, the dry run's bounds, the MCP
description and the OpenAPI document publish the cap beside the set limits.
At 64 MiB, 100 points of 65,536 outcomes (about 150 MB) are refused and
100 points of 16 bits at 30,000 shots are taken. A client's running jobs
are counted against its unread allowance at their own bounds, not the cap.
The check as a result ends is kept only as a backstop.

The round-5 verifier's probes are the tests for its first two findings:
210 long polls on a running job from 210 addresses, 3 ms apart, under a
hard limit of 256 (health 0 of 25 on a0804db72), and the README's decode,
a threshold made long enough to outlast a waiting call's 10 s, and six
critiques at the service's own limits (refused J032 at 10.07 s on
a0804db72). Both pass here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pulse run reads its own program, so its result was not bounded before
it ran: it was counted at WAI_QSVC_RESULT_MIB and held to it only as it
ended. It is bounded now from its schedule as read (pulse::plan_bounded,
through Method::plan_bounded, a default method on the registry's trait
that gives no bound): the populations by the qubits it simulates, the
distribution by the captures it reads, each keyed as wide as the
program's bits, the counts by its shots (run::counts_bound_among), and
each capture with its frame's and bit's names. A pulse run of 100,000
bits declared and one captured, about 1.4 MB as served, is refused at
once under a 1 MiB cap, where it had been taken and failed as it ended;
100 bits are taken. No built-in method is now unbounded, so the check as
a result ends is only a backstop, for a job kept across a restart that
lowered the cap. engine::pulse::tests::no_pulse_result_passes_the_bound_its_plan_gave
runs one capture, one bit of 3,000 captured, nothing captured and two
qubits captured, each within its bound and within a few times it.

The dry run's result_bytes, an integer or {"bytes": null, "why"}, is
named in schemas/service-tools.v1.json and the CLI's snapshot of the
service's tools; every_tool_s_answer_follows_its_output_schema failed on
it on both hosts at 15a1bc9ed.

A circuit's QEC pilot goes on past its one in a hundred until 200 ms,
its shots allowing (PILOT_FLOOR). Its shots are the call's own, decoded
once and counted in its result, so it costs nothing; 20 ms of a light
decode, read through one pause of a loaded host's scheduler, projected a
decode that took 2.7 s alone at 7 s against a 6 s limit and refused it
(a_time_is_measured_apart_from_setup_and_holds_every_threshold, the M3
Ultra at load 40). The workflows name the new tests, and the pulse
tests are 28, native and wasip2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum-service tests: a just-written binary started again while busy, the README's burst sent again when its threshold could not be measured, and a QEC refusal's time held to the host's
Some checks failed
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Has been cancelled
wai-quantum views in agent hosts / views · a scripted host (pull_request) Has been cancelled
wai-quantum WebGPU member / does this change touch it (pull_request) Has been cancelled
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Has been cancelled
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been cancelled
wai-sink-android native sink library / does this change touch it (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Has been cancelled
wai-sink-swift native sink package / does this change touch it (pull_request) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
WAI Studio / does this change touch it (pull_request) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Has been cancelled
WAI Studio / Studio in WebKit · linux (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-int-module WAISM module of record / does this change touch it (pull_request) Successful in 16s
wai-int-module WAISM module of record / linux · both checkers, wasmtime (Cranelift, Winch), Wasmi and Node (pull_request) Has been skipped
wai-int-module WAISM module of record / linux-arm64 · both checkers, wasmtime (Cranelift, Winch), Wasmi and Node (pull_request) Has been skipped
wai-int-module WAISM module of record / the module of record in Chromium (pull_request) Has been skipped
d5564748e5
Two depth tests failed on the Framework in the whole suite under
`ulimit -n 256`, at load 43, beside the other tests:

- the_binary_can_be_replaced_before_the_restart: the copy it had just
  written could not be started, "Text file busy" (ETXTBSY). A child that
  another test forked between the copy's open and its close holds the file
  open for writing until it execs, close-on-exec or not. launch() now
  starts it again, every 50 ms for up to 10 s, on that error alone.
- the_readme_s_burst_from_one_client_is_answered: its threshold, 18.8 s
  alone on the loaded host, was measured beside the burst at 58 s and
  refused at once (J031, past the 25 s an error-correction call may
  take), so the burst tested nothing. On that refusal alone the threshold
  is measured again and the burst sent again, three times at most; every
  other answer is asserted as before. On a0804db72 the threshold is
  answered and the critiques refused (J032), so this changes nothing
  there.

And one library test, in the same run's --features full --lib (1,418
passed, 1 failed), at load 77 and 302 s where the M3 Ultra took 167 s:
a_time_is_measured_apart_from_setup_and_holds_every_threshold asked a
colour-code threshold to be refused within 5 s and it was refused after
8.2 s, on its pilot (the call refused, J031). The code-capacity pilot is
unchanged this round. The test's first half already measures its own
decode with no time set, because a time here is a build's and a host's;
the refusal is now asked within 5 s or twice that decode's time,
whichever is longer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main took a nesting bound of its own for the OpenQASM reader in 15ae7d457
(Q130 at 128 levels, a chain of at most 127 operators, at most 1024
`else if` arms). This branch's reader bounds nesting and memory already
(Q006 at 64 levels in the text and 256 through calls, operator runs and
`else if` ladders flat at any length; Q130 the reader's memory), so the
reader's sources, its diagnostics table and its docs are this branch's.
Main's tests of that bound now hold this reader to it: lang_tests reads
each shape inside the bound and refuses it past; tests/reader_stack.rs
reads every program at the bounds on 1 MiB and 2 MiB threads and refuses
the same shapes nested 5000 deep on them; the service's tests/verify.rs
reads the 2000-term sum and refuses the brackets and blocks on seven
routes. Main's changelog entry says what holds.

Main also brings the RUSTSEC-2026-0459 fix (rusqlite 0.41 in jouleclaw)
and the corpus-licence self-test fix.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sixth review of #676:

- The bound on a result is a true upper bound, part by part
  (catalog::Bound: counts, verdict, notes, requests, rest). A sampled
  intent's fit names at most 64 of the outcomes it rules out and says how
  many (impossible_outcomes), and its reason says them once; the bound
  counts each intent's lists and the statement's reasons. A trajectories
  run's counts are bounded by its trajectories. A verdict counts the
  reader's notes and the repair again. Every number is counted at the
  length RFC 8785 writes it (receipt::canonical_len), so a set's items'
  requests and the provenance's shots and seed are counted as served.
- Where the method knows better, the bound is close: at most 2^b outcomes
  for the b bits measured, and on the noiseless stabilizer method 2^d, d
  its measured qubits and its branching gates (a GHZ state's two). A
  verdict's critique is bounded by the smells its program's gates can draw
  (critique::smells_bound, the critique's own walk), not 512 bytes a gate:
  100 GHZ24 at 30,000 shots, and a 124,700-gate loop with its verdict, are
  taken. A refusal names the largest part and what makes it smaller.
- A dry run applies the run's checks: over MCP it plans the job as the run
  tools run it, with its verdict, and its reading is held to what the
  submission's would take back (refused as accepted: false).
- A set's worker writes each item's result as text as it ends, so the
  README's 100 points of 30,000 shots run to the end within their memory.
- A turn lent to a client goes back once one of its own requests has
  waited 7 s, as once another client's waits, so two long checks never
  hold its quick calls past their wait. A client's eleventh request at
  once is refused, of readings as of checks, and no turn is lent while
  one of its own is free or held by a request waiting for the pool.
- Every MCP answer echoes the JSON-RPC id as the text it was sent as.
- A long poll past its client's holds is answered 429 (503 past
  everyone's) with Retry-After and the job's status; the command line's
  follow waits and polls again.
- Every job queued or running counts against the service-wide unread cap
  at its bound, checked again under the queue's lock.
- README, OpenAPI, the tool schema and the changelog say what holds; the
  MCP tool snapshot and the command line's docs are regenerated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
5df961d52 wrote 'a 320 × 180 test pattern' in browser-sink.md §14.6 and
compose/README.md; scripts/check-no-vanity-metrics.sh reads '180 test' as a
test count, which turned repository checks red on main (run 26220 at
1929b8436). Same words, reordered.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 4c5b54a272)
wai-quantum: the change's entry acknowledges Request::smells_bytes and the two reader constants of main's it replaces
Some checks failed
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 4m9s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 4m59s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 5m27s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 8m31s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 32s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 8m49s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 16m40s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m21s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m50s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m27s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 1m37s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 38s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 18m26s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 38s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 8m40s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 11m12s
wai-quantum service / service energy · linux packages (pull_request) Successful in 1m52s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m5s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 9m37s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 28s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 33s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Failing after 2m4s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 3m51s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 20m48s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m38s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 21m51s
wai-quantum command line / command line · linux (pull_request) Successful in 38m14s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 23m2s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 28m2s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Failing after 2h5m15s
b35ae093e6
`wai breaking --against 1929b8436` named three changes: the public field
a verdict's critique bound is carried in, and main's `qasm::MAX_NESTING`
and `qasm::MAX_ELSE_IF` (15ae7d457, in no release), which this branch's
reader replaces with `qasm::parse::MAX_NESTING` and flat `else if`
ladders. Each is intended, and the entry says what a user does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
dcharlot closed this pull request 2026-11-09 15:16:18 -05:00
Some checks failed
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 24s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (pull_request) Successful in 59s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 43s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Successful in 1m5s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 53s
wai-quantum energy-landscape jobs / does this change touch it (pull_request) Successful in 6s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 41s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m46s
wai-quantum energy-landscape GPU members / does this change touch it (pull_request) Successful in 51s
wai-quantum energy-landscape GPU members / members · jetson-hub (pull_request) Has been skipped
wai-quantum energy-landscape GPU members / members · mac-studio (pull_request) Has been skipped
wai-quantum energy-landscape GPU members / members · framework-128gb (pull_request) Has been skipped
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Failing after 56s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m10s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 3m42s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Failing after 1m54s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 4m33s
wai-quantum on every processor / emulated · armv7-unknown-linux-gnueabihf (pull_request) Successful in 3m27s
wai-quantum on every processor / emulated · loongarch64-unknown-linux-gnu (pull_request) Successful in 3m52s
wai-quantum energy-landscape jobs / the same bits · macos (pull_request) Successful in 3m31s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m42s
wai-quantum on every processor / emulated · i686-unknown-linux-gnu (pull_request) Successful in 4m29s
wai-quantum on every processor / emulated · riscv64gc-unknown-linux-gnu (pull_request) Successful in 5m37s
wai-quantum on every processor / emulated · aarch64-unknown-linux-gnu (pull_request) Successful in 6m44s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 7m51s
wai-quantum on every processor / emulated · powerpc64-unknown-linux-gnu (pull_request) Successful in 7m45s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (pull_request) Failing after 7m24s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (pull_request) Successful in 7m39s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (pull_request) Successful in 7m47s
wai-quantum on every processor / emulated · sparc64-unknown-linux-gnu (pull_request) Successful in 8m29s
wai-quantum on every processor / emulated · powerpc64le-unknown-linux-gnu (pull_request) Successful in 10m40s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 10m28s
wai-quantum on every processor / emulated · s390x-unknown-linux-gnu (pull_request) Successful in 11m41s
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 12m10s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 12m49s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 16m34s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 15s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Has been skipped
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Has been skipped
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 14s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 15s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 13s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been skipped
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 15s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Has been skipped
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Has been skipped
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 12s
wai cross-architecture byte-equality / does this change touch it (pull_request) Successful in 13s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Has been skipped
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Has been skipped
WAI conformance / does this change touch it (pull_request) Successful in 12s
WAI operation catalogue / does this change touch it (pull_request) Successful in 13s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Has been skipped
wai cross-architecture byte-equality / windows-x86_64 (pull_request) Has been skipped
wai-fleet service / does this change touch it (pull_request) Successful in 13s
WAI energy receipts / does this change touch it (pull_request) Successful in 10s
wai-fleet service / service · linux (pull_request) Has been skipped
wai-fleet service / service · macos (pull_request) Has been skipped
wai formal verification (integer core) / does this change touch it (pull_request) Successful in 13s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 14s
wai formal verification (integer core) / every harness in one shard (pull_request) Has been skipped
wai formal verification (integer core) / kani (arith) (pull_request) Has been skipped
wai formal verification (integer core) / kani (isqrt512) (pull_request) Has been skipped
wai formal verification (integer core) / kani (plan) (pull_request) Has been skipped
wai formal verification (integer core) / kani (sound) (pull_request) Has been skipped
wai formal verification (integer core) / kani (stage) (pull_request) Has been skipped
wai formal verification (integer core) / kani (rans64-t2) (pull_request) Has been skipped
wai formal verification (integer core) / kani (stage-long) (pull_request) Has been skipped
wai formal verification (integer core) / kani (integer-core) (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been skipped
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been skipped
wai-key-proxy / does this change touch it (pull_request) Successful in 11s
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 13s
wai-key-proxy / proxy · linux (pull_request) Has been skipped
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been skipped
wai-ledger service / does this change touch it (pull_request) Successful in 12s
wai key · Windows / does this change touch it (pull_request) Successful in 14s
wai-ledger service / service · linux (pull_request) Has been skipped
wai-ledger service / service · macos (pull_request) Has been skipped
wai key · Windows / wai key and the toolchain's tests · windows (pull_request) Has been skipped
WAI neural decoders end-to-end / does this change touch it (pull_request) Successful in 10s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Has been skipped
WAI neural sink gates / does this change touch it (pull_request) Successful in 10s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Has been skipped
WAI neural sink gates / the neural-processor path refuses a bit-exact decode (macOS) (pull_request) Has been skipped
WAI neural sink gates / the exact JPEG-AI decoder reports its path (pinned inputs, required) (pull_request) Has been skipped
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 12s
wai-ops-npu integer operators on neural processors / does this change touch it (pull_request) Successful in 12s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Has been skipped
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been skipped
wai-ops-npu integer operators on neural processors / neural processor (framework-128gb silicon) (pull_request) Has been skipped
wai-ops integer operators / does this change touch it (pull_request) Successful in 9s
wai-ops-npu integer operators on neural processors / builds · no device is not a pass (linux-arm64) (pull_request) Has been skipped
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 9s
wai-ops integer operators / linux-aarch64 (pull_request) Has been skipped
wai-ops integer operators / linux-x86_64 (pull_request) Has been skipped
wai-ops integer operators / macos-arm64 (pull_request) Has been skipped
wai-ops integer operators / wasm32-wasip2 (wasmtime) (pull_request) Has been skipped
wai-ops integer operators / wasm32 under Node (pull_request) Has been skipped
wai-ops integer operators / independent checker (Python) (pull_request) Has been skipped
wai-quantum command line / does this change touch it (pull_request) Successful in 16s
wai-quantum CUDA member / does this change touch it (pull_request) Successful in 14s
wai-quantum contract / does this change touch it (pull_request) Successful in 17s
wai-quantum dataflow member / does this change touch it (pull_request) Successful in 14s
wai-quantum signal processor / does this change touch it (pull_request) Successful in 20s
wai-quantum editors / does this change touch it (pull_request) Successful in 16s
wai-quantum resource estimation / does this change touch it (pull_request) Successful in 15s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 24s
wai-quantum other formats / does this change touch it (pull_request) Successful in 9s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 14s
wai-quantum HIP member / does this change touch it (pull_request) Successful in 16s
wai-quantum-landscape-fpga reference design / does this change touch it (pull_request) Successful in 10s
wai-quantum FPGA member / does this change touch it (pull_request) Successful in 15s
wai-quantum-landscape-fpga reference design / the member against the model of the logic, and the logic against the model · linux, release (pull_request) Has been skipped
wai-quantum-landscape-fpga reference design / the board's code on the board's processor's architecture · linux-arm64, release (pull_request) Has been skipped
wai-quantum OpenQASM reader / does this change touch it (pull_request) Successful in 15s
wai-quantum Metal member / does this change touch it (pull_request) Successful in 14s
wai-quantum neural-processor member / does this change touch it (pull_request) Successful in 12s
wai-quantum npu-cores member / does this change touch it (pull_request) Successful in 16s
wai-quantum OpenCL member / does this change touch it (pull_request) Successful in 14s
wai-quantum pulse and dynamics / does this change touch it (pull_request) Successful in 14s
wai-quantum for Python / does this change touch it (pull_request) Successful in 14s
wai-quantum error correction and mitigation / does this change touch it (pull_request) Successful in 13s
wai-quantum extension points / does this change touch it (pull_request) Successful in 12s
wai-quantum-qpu / does this change touch it (pull_request) Successful in 14s
wai-quantum service / does this change touch it (pull_request) Successful in 13s
wai-quantum matrix-extension member / does this change touch it (pull_request) Successful in 13s
wai-quantum tensor-engine member / does this change touch it (pull_request) Successful in 13s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 13s
wai-quantum portable tensor program / does this change touch it (pull_request) Successful in 9s
wai-quantum variational and algorithm library / does this change touch it (pull_request) Successful in 9s
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 11s
wai-quantum verdicts and repairs / does this change touch it (pull_request) Successful in 12s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 9s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 15s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 13s
wai-quantum views in agent hosts / does this change touch it (pull_request) Successful in 9s
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 13s
wai-vector vector graphics / does this change touch it (pull_request) Successful in 16s
WAI Studio / does this change touch it (pull_request) Successful in 16s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 16s
wai-vector vector graphics / linux-aarch64 (pull_request) Has been skipped
wai-vector vector graphics / linux-x86_64 (pull_request) Has been skipped
wai-vector vector graphics / macos-arm64 (pull_request) Has been skipped
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Has been skipped
wai-vector vector graphics / wasm32 under Node (pull_request) Has been skipped
wai-vector vector graphics / wasm32 in three browser engines (pull_request) Has been skipped
wai-vector vector graphics / fuzz, peak allocation, comparison count (pull_request) Has been skipped
wai-vector vector graphics / independent checker (Python) (pull_request) Has been skipped
wai-web wasm32 / does this change touch it (pull_request) Successful in 10s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 25s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m42s
WAI conformance / cargo-class corpora · macos (pull_request) Failing after 1m33s
WAI energy receipts / energy receipts · macos (pull_request) Failing after 1m21s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 2m22s
wai-quantum dataflow member / member · dataflow device absent (macos) (pull_request) Successful in 17s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m17s
wai-quantum command line / command line · macos (pull_request) Successful in 1m56s
wai-quantum editors / editors · macos (pull_request) Successful in 1m21s
wai-pack / wai-pack · macos (pull_request) Successful in 3m14s
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Successful in 1m1s
wai-quantum Metal member / member · Metal (macos) (pull_request) Successful in 38s
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Successful in 28s
wai-quantum-qpu / credentials in the keychain (macos) (pull_request) Successful in 35s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Failing after 32s
wai-quantum pulse and dynamics / the same bits · macos (pull_request) Successful in 1m16s
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Successful in 16s
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Successful in 19s
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Successful in 19s
wai-quantum HIP member / member · HIP (framework-128gb) (pull_request) Successful in 1m5s
wai-quantum for Python / python · macos (pull_request) Successful in 2m29s
wai-quantum npu-cores member / member · npu-cores, neural processor (framework-128gb silicon) (pull_request) Successful in 1m16s
wai-quantum neural-processor member / member · neural processor (macos) (pull_request) Successful in 5m14s
wai-quantum OpenCL member / member · OpenCL GPU (framework-128gb) (pull_request) Successful in 26s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 1m55s
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Successful in 13s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 2m35s
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Successful in 19s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m55s
wai-quantum service / service · macos (pull_request) Successful in 5m46s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 6m35s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 2m36s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 1m11s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Failing after 1m47s
wai-quantum contract / the policy is linked, and the deprecations page reads (pull_request) Successful in 8s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m18s
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Successful in 1m39s
WAI conformance / cargo-class corpora · linux (pull_request) Failing after 5m36s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 11s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 9m0s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 4m50s
WAI energy receipts / energy receipts · linux (pull_request) Failing after 4m8s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 8m13s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:401e) (pull_request) Successful in 2m39s
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Successful in 33s
wai-pack / wai-pack · linux (pull_request) Successful in 4m45s
wai-quantum editors / editors · linux (pull_request) Successful in 2m52s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 11m15s
wai-quantum Metal member / member · Metal absent (linux) (pull_request) Successful in 27s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m15s
wai-quantum neural-processor member / member · neural processor absent (linux) (pull_request) Successful in 21s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 10m4s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m0s
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Successful in 46s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 5m47s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 4m10s
wai-quantum contract / schemas and the committed tool lists · linux (pull_request) Successful in 6m49s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (pull_request) Failing after 1m34s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 48s
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Successful in 47s
wai-quantum OpenQASM reader / reader and corpus · linux (pull_request) Successful in 4m0s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m48s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m13s
wai-quantum contract / API compatibility · linux (pull_request) Successful in 8m8s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:b140) (pull_request) Successful in 7m36s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 3m33s
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Successful in 25s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 16s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 5m33s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 7m0s
wai-quantum other formats / readers, writers and the set · linux (pull_request) Failing after 6m21s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m6s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Successful in 45s
wai-quantum extension points / the registry and the example · linux (pull_request) Successful in 3m14s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (pull_request) Failing after 1m44s
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Successful in 2m8s
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 59s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 29s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 24s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (pull_request) Failing after 4m5s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 1m22s
wai-quantum for Python / python · linux (pull_request) Successful in 4m54s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 1m12s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m50s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m25s
wai-quantum resource estimation / estimation and calibration · linux, release (pull_request) Failing after 9m9s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 4m43s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m7s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (pull_request) Successful in 3m1s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Failing after 3m54s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Successful in 3m8s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 3m32s
wai-quantum service / service · linux (pull_request) Failing after 6m22s
wai-quantum CUDA member / member · CUDA (thor) (pull_request) Successful in 1m59s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (thor) (pull_request) Successful in 53s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m47s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 4m9s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 4m59s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 5m27s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 8m31s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 32s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 8m49s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 16m40s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m21s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m50s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m27s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 1m37s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 38s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 18m26s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 38s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 8m40s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 11m12s
wai-quantum service / service energy · linux packages (pull_request) Successful in 1m52s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 2m5s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 9m37s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 28s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 33s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Failing after 2m4s
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Successful in 3m51s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 20m48s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 4m38s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 21m51s
wai-quantum command line / command line · linux (pull_request) Successful in 38m14s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 23m2s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 28m2s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Failing after 2h5m15s

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!676
No description provided.