open-standards/wai/claim-conformance/README.md
Transaction Science ad283d9281
All checks were successful
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 12m16s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 1m33s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 2m18s
wai cross-architecture byte-equality / does this change touch it (pull_request) Successful in 6s
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 cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Has been skipped
WAI conformance / does this change touch it (pull_request) Successful in 6s
WAI energy receipts / does this change touch it (pull_request) Successful in 6s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 6s
WAI neural decoders end-to-end / does this change touch it (pull_request) Successful in 6s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Has been skipped
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 8s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been skipped
WAI neural sink gates / does this change touch it (pull_request) Successful in 6s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 7s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 21s
wai-quantum command line / does this change touch it (pull_request) Successful in 5s
wai-quantum command line / command line · linux (pull_request) Has been skipped
wai-quantum command line / command line · macos (pull_request) Has been skipped
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 7s
wai-quantum dataflow member / does this change touch it (pull_request) Successful in 5s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:b140) (pull_request) Has been skipped
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:401e) (pull_request) Has been skipped
wai-quantum dataflow member / member · dataflow device absent (macos) (pull_request) Has been skipped
wai-quantum CUDA member / does this change touch it (pull_request) Successful in 7s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Has been skipped
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Has been skipped
wai-quantum signal processor / does this change touch it (pull_request) Successful in 6s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Has been skipped
wai-quantum FPGA member / does this change touch it (pull_request) Successful in 6s
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Has been skipped
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Has been skipped
wai-quantum Metal member / does this change touch it (pull_request) Successful in 5s
wai-quantum Metal member / member · Metal (macos) (pull_request) Has been skipped
wai-quantum HIP member / does this change touch it (pull_request) Successful in 7s
wai-quantum Metal member / member · Metal absent (linux) (pull_request) Has been skipped
wai-quantum HIP member / member · HIP (framework-128gb) (pull_request) Has been skipped
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Has been skipped
wai-quantum npu-cores member / does this change touch it (pull_request) Successful in 6s
wai-quantum neural-processor member / does this change touch it (pull_request) Successful in 6s
wai-quantum npu-cores member / member · npu-cores, neural processor (framework-128gb silicon) (pull_request) Has been skipped
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Has been skipped
wai-quantum neural-processor member / member · neural processor (macos) (pull_request) Has been skipped
wai-quantum neural-processor member / member · neural processor absent (linux) (pull_request) Has been skipped
wai-quantum-qpu / does this change touch it (pull_request) Successful in 5s
wai-quantum OpenCL member / does this change touch it (pull_request) Successful in 7s
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Has been skipped
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Has been skipped
wai-quantum OpenCL member / member · OpenCL GPU (framework-128gb) (pull_request) Has been skipped
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Has been skipped
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Has been skipped
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 49s
wai-quantum matrix-extension member / does this change touch it (pull_request) Successful in 5s
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Has been skipped
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Has been skipped
wai-quantum service / does this change touch it (pull_request) Successful in 7s
wai-quantum service / service · linux (pull_request) Has been skipped
wai-quantum service / service · macos (pull_request) Has been skipped
wai-quantum tensor-engine member / does this change touch it (pull_request) Successful in 6s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 8s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Has been skipped
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been skipped
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m24s
wai-quantum portable tensor program / does this change touch it (pull_request) Successful in 6s
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Has been skipped
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 6s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Has been skipped
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 6s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 8s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been skipped
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Has been skipped
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been skipped
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been skipped
wai-web wasm32 / does this change touch it (pull_request) Successful in 7s
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Has been skipped
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Has been skipped
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been skipped
WAI energy receipts / energy receipts · macos (pull_request) Successful in 6m38s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 11m12s
wai-pack / wai-pack · macos (pull_request) Successful in 3m10s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 8m12s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m46s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 19m23s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 17s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 27s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m48s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 5m11s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 7m54s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 15m46s
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Successful in 5m25s
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Successful in 6m3s
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Successful in 5m32s
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Successful in 5s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Successful in 31m43s
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Successful in 2m10s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m9s
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Successful in 31m23s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m39s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 18s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m21s
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Successful in 31m36s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m1s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h2m52s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 3m39s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 6s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
wai-quantum on every processor / emulated · aarch64-unknown-linux-gnu (pull_request) Successful in 1m2s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m42s
wai-quantum on every processor / emulated · armv7-unknown-linux-gnueabihf (pull_request) Successful in 1m6s
wai-quantum on every processor / emulated · loongarch64-unknown-linux-gnu (pull_request) Successful in 1m44s
wai-quantum on every processor / emulated · powerpc64-unknown-linux-gnu (pull_request) Successful in 2m26s
wai-quantum on every processor / emulated · powerpc64le-unknown-linux-gnu (pull_request) Successful in 4m10s
wai-quantum on every processor / emulated · riscv64gc-unknown-linux-gnu (pull_request) Successful in 2m33s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 10m46s
wai-quantum on every processor / emulated · s390x-unknown-linux-gnu (pull_request) Successful in 2m33s
wai-quantum on every processor / emulated · sparc64-unknown-linux-gnu (pull_request) Successful in 4m33s
wai-quantum on every processor / emulated · i686-unknown-linux-gnu (pull_request) Successful in 2m40s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 6s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m22s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 35s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 9m43s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m43s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 2m58s
wai-pack / wai-pack · linux (pull_request) Successful in 1m36s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m48s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m48s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m53s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m50s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m49s
Merge origin/main into train/pack-stack (Train C and wai-quantum 0.6.15)
Six files conflicted; both sides kept in each:
- claim-conformance: main's span vectors stay where main published them, and
  this train's fabric-record vectors follow them, so main's 615 vectors are an
  unchanged prefix of the 636. vectors.json regenerated with
  `wai_claim_vectors gen`; it verifies, and claim_verify.py passes all 636.
- jwp-receipts.md: a claim's two optional-member exceptions, the hybrid
  pq-signatures members and a measurement claim's `fabrics`.
- wai-ops-gpu: both modules (`semantics`, `splat`); the workflow header names
  both suites.
- CHANGELOG: this train's sections above Train C's, newest first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 02:28:39 -04:00

571 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# claim-conformance
Conformance for [typed receipt classes and the soft binding class](../extensions/jwp-receipts.md)
(§2.4.3–§2.4.12, §2.6, §3.1–§3.2): `claim` receipts of class `energy`,
`lawful-basis`, `log-inclusion`, `soft-binding`, `soft-binding-span`, `binding-recovery`, `measurement`,
`operating-point`, `stage`, `delivery-gap` and `reconstruct-session`, and recovery
through a soft binding under the ancestor rule.
**Provenance.** Generated here by `wai_claim_vectors`, from fixed seeds and fixed values (below). No media.
Each vector declares **one outcome**: `accept` — with the claim's `link_hash`, which
pins the canonical form, or for a recovery the ancestor it attests — or `reject` at a
named §3.1 step with a named reason. A verifier that rejects for another reason, or at
another step, does not conform.
## Files
| file | what it is |
|---|---|
| `vectors.json` | The corpus. Every vector declares one outcome, and every reason of §3.1 is exercised at least once. |
| `claim_verify.py` | A second verifier, written from the extension text, that shares no code with `wai-rs`. |
| `differential.py` | Generates inputs the corpus does not hold, runs both verifiers on them, and reports every input on which their verdicts differ. |
Two procedures appear. `claim` verifies one receipt (§3.1), with optional context the
verifier holds: `previous` (this signer's previous claim, step 5), `object` (the
delivery receipt's `content_hash` and `joules_micro`), `oer` (an OER receipt and its
signer keys), `declaration` (the claim a decision or a recovery record names),
`publisher` (for a recovery record: the key of a verified delivery receipt for the
declaration's subject — §2.6.3 makes the declaration context only together with it, so
a vector that gives a recovery record one without the other is malformed), and
`exact_match_algs` (the soft-binding algorithms the verifier has a matcher for; the
corpus's matcher is byte equality), and `figure` (the receipt that carries the figure a
`measurement` or `operating-point` claim names), and `gap_object` (the object receipt at
a `delivery-gap` claim's coordinates, as the verifier holds it once verified, by
inclusion at least: `{track_alias, group_id, object_id, content_hash}`), and, for a
`reconstruct-session` claim, `outputs` (its presentations, `{unit, layers, decoder,
out_digest}`), `redecoded` (what a verifier that decoded presentations again found,
`{index, out_digest}`, the index into `outputs`), `objects` (each object received,
`{content_hash, path, counted, transferred, presented}`, `counted` `null` for an object
not countable, each (content hash, path) once), `envelopes` (each `WAI2` envelope the
session received and any `WAI1` envelope the verifier holds, `{envelope}`, its bytes in
hex, inert bytes included; an object's `counted` is checked against its envelope's
counted bytes, a decoder entry naming one as its `object` is checked against it, a held
layer's against what a sink can apply and its `first_unit` against its base's entry's, a
presentation through such an entry against the layer count and the units it gives, and
the classical rendition is read from each held `WAI2` envelope, readable or encrypted, by
§2.4.12's rule 9 — envelopes held in full: a `WAI2` a sink received only as byte ranges is
not among them, and a session cannot yet state its classical rendition) and
`stage_claims` (the wire forms of the `stage`
claims it names). An `object` context may also carry the
class its leaf is labelled with (jwp-receipts §2.2), in energy-measurement §2.7's JSON
members, read as that section reads them. A `figure` context is one of two forms:
`claim`, an `energy` claim's wire form, viewed under the `moq-jwp:claim-link` profile;
or `view`, a receipt the verifier has already verified, given as `{profile, identity,
group, figures: [{index, joules_micro, energy_* members}], signer}`, so that neither
verifier needs to parse every receipt family a figure profile names. `recover` runs §3.2 from a `declaration`, the
`publisher` key taken from a verified delivery receipt, the `observed_value`, and the
object in hand's `observed_hash` (or `null`). Both procedures report
`recovery_signer_mismatch` for a declaration the ancestor's publisher did not sign:
`recover_signer_mismatch` and `binding_recovery_declaration_not_publishers` are the same
defect, in a recovery performed and in one recorded.
**Span bindings** (§2.6.4). A `soft-binding-span` declaration is context, for a recovery
(`recover`) and for a recovery record (`claim`), together with `receipt`, the delivery
receipt the verifier verified for an object of the span, given as it holds it:
`{signer, track_alias, group_id, object_id, content_hash}`, in place of `publisher`. A
`recover` vector with `receipt` runs §3.2 through a span and reports the anchor, the
declaration's subject, as the ancestor it names the span by; without `receipt` it runs the
whole-object procedure, under which a span declaration is `context_mismatch`, as it is for a
recovery record given a span declaration and a key alone. The span vectors are appended after
every other vector, which are unchanged.
Everything is synthetic. Keys are Ed25519 from fixed seeds (0x11 publisher, 0x22 meter,
0x33 operator, 0x44 sink, 0x55 resolver, 0x66 relay) and attest nothing; outputs,
parameter sets and envelope payloads are synthetic bytes; figures, uncertainties and
calibration references are invented; algorithm identifiers, registries, endpoints, logs
and calibration certificates use reserved example domains; vocabulary terms are
illustrative and no verifier here checks them against a vocabulary.
## The acquisition class
An `energy` claim carries its class as energy-measurement §2.7 writes a class in JSON:
`energy_provenance`, with `energy_uncertainty`, `energy_counter_filtering` and
`energy_calibration_ref` present and `null` where the class has no such evidence.
These vectors are about the class and its evidence:
- **Accepted:** an `OnChipCounter` figure with a declared uncertainty (2 % over 1 s) and
its filtering state `Unknown`, and with `Off`; a `CalibratedInstrument` figure with a
declared uncertainty (0.5 % over 1 s) and a calibration reference; a counter graded
`derived`, below what it supports; a 1 µJ figure declared at exactly the resolution
floor (288,676 ppm); and a counter graded `measured` that names an OER receipt grading
the same figure `measured`.
- **Rejected at step 4:** legacy `HwShunt`, alone and with evidence beside it
(`energy_legacy_acquisition`); a label in another case (`energy_unknown_acquisition`);
and, as `energy_acquisition_evidence`, a counter without its uncertainty or without its
filtering state (`null` is not `Unknown`), with a state outside `On`/`Off`/`Unknown`, or
with a calibration reference; an instrument without its reference, with a reference of
white space only (a space and U+3000), or with a filtering state; a `ModelBased` figure
with an uncertainty; an uncertainty of zero ppm, of a zero window, or of 2³² ppm; and a
1 µJ figure at 288,675 ppm, one below the floor. An `Estimator` graded `simulated` is
`energy_grade_exceeds_acquisition`.
- **Rejected at step 2:** an uncertainty without its window (`missing_required_field`),
with a coverage factor beside it (`unknown_member`), with its relative part as a
string, written as an array, and a filtering state written as a number (`field_type`).
- **Rejected at step 3:** a `ModelBased` claim rewritten in transit to `OnChipCounter`
with an uncertainty and a filtering state: the class and its evidence are signed.
## Reports about a figure: `measurement` and `operating-point`
A `measurement` claim publishes how one figure was measured — its bracket's values as
integers, with the liveness facts of each bracket, the build and the power reader — and
an `operating-point` claim records the operating point it was spent at. Both name the
receipt that seals the figure by a figure profile (energy-measurement §6.1) and restate
the figure with its class. The vectors cover:
- **The arithmetic of energy-measurement §6.2:** declarations at the least a bracket
supports and one ppm below it; a figure at a half microjoule, which rounds up; brackets
whose products exceed 128 and 200 bits; a calibrated instrument declaring more than the
least; and the declaration a sealing path computes from unrounded samples, which the
rule refuses. Those values are checked by `claim_verify.py` with Python's integers, and
the bracket table `wai-rs` pins in `energy_report` is recomputed by it too.
- **Liveness (§3.1):** a bracket whose rail advanced in too few samples; a frozen rail
refreshed once, which gives a flat idle bracket and an infinite ratio; a reader holding
a stale non-zero value, stepped once by a batch refresh, with its honest facts and with
facts claiming every sample live; a flat idle bracket whose facts claim the rail live
(a repeated sample is not an advance, so a bracket with `sd_uw` 0 is never live); a
refresh published in two steps in each bracket, which passes a rule of two consecutive
advances; facts that describe no sequence; and, accepted, a `ModelBased` figure whose
bracket shows a dead rail, since liveness binds the hardware classes only.
- **The power reader (§2.4.8 rule 4):** a hardware figure's report that names no reader
is refused, with a bracket and without one; accepted, an `OnChipCounter` report with no
bracket that names its reader.
- **Every other rule of §2.4.8 rule 5 and §2.4.9,** each with its reason, and two defects
at once.
- **Step 2:** derived quantities are never carried (a bracket with its ratio is
`unknown_member`); the opening and build-profile enumerations; integers, terms and
absolute URIs where the tables require them.
- **Step 6:** against an `energy` claim and against views of a provenance receipt, a
delivered object (named by its leaf index and its group), and an energy binding, which
no Ed25519 key signs — a `measurement` claim about it is `figure_signer_unknown`, an
`operating-point` claim about it is accepted; a report by a key other than the figure's
signer; and an operating point recorded against an unmetered or unlabelled figure. A
`measurement` claim checked without the receipt that carries its figure is
`figure_not_held`, whether a stranger or the figure's own signer signed it: only step 6
shows a report is the figure signer's.
- **The labelled delivery leaf:** an `energy` claim against an object whose leaf carries
a class, agreeing, and disagreeing in figure, class or evidence; an `OnChipCounter`
label with no filtering state reads as `Unknown`.
- **The fabric record (§2.4.8 rule 6, energy-measurement §6.3),** appended after every
earlier vector, which a test pins by digest: accepted, a figure divided between the CPU
(`task`) and a GPU (`rail`) over steps that ran on each; at step 2, a record written
`null` (a report without one has no member), a step with a member it does not define,
an empty backend and a share written as a string; at step 4, a fabric and an
attribution outside §6.3, a share divided by GPU time (which names no attribution),
steps out of order, a fabric twice, no step, a step on an unmetered fabric, a share on
a fabric no step ran on, and shares that do not sum to the figure.
## What a party observed of a stage set: `stage` and `delivery-gap`
A `stage` claim (§2.4.10) is signed by the party that observed a stage set
([staged-delivery](../extensions/staged-delivery.md) §10.2) about its base. A
`delivery-gap` claim (§2.4.11) is signed by a relay about an object it did not forward.
The vectors cover:
- **Accepted:** a set requested, committed, delivered and presented at stage 2, four
per-frame first layers and a second layer over the four frames; the second layer
dropped at a relay, its receipt verified by inclusion, so the stage delivered is 1 and
a reader derives that the commitment was not met; a base whose receipt was not
verified first, whose layers applied with no evidence and which states no delivered
stage, so rule 6 does not apply, and a layer over such a base applied on inclusion
evidence; a second layer delivered and late for one frame (`partial`,
`deadline-missed`); two first layers for one frame, both refused and the frame frozen;
the base alone at `entropy-consistency`, with no stage hash; both layers delivered and
refused, which meets a commitment to stage 2 with stage 0 presented; a span past the
set refused `refines-malformed`, which lists its units as stated; and such a layer
delivered over its bytes, which delivers nothing to the set.
- **Rejected at step 4,** each rule with its reason: the vocabulary, a point with a
second member, above 255 or empty, and a request with no delivered stage; a stage hash
without `decode-equivalence` and the converse; no unit, a last stage with no unit, a
presented stage other than the lowest with units, more misses than units; objects out
of order or listed twice, outside the units (and a span past the set refused for a
reason decided after it was checked), at layer 0, with a reason where none belongs or
none where one does, a reason staged delivery does not define, an object applied on
inclusion alone or with no receipt beside a stated delivered stage, a dropped object
said to be delivered or counted with bytes, objects that arrived with no bytes, more
object bytes than the set's or all of them, and one layer applied twice to a unit;
units at a stage no applied object of that layer reaches, a partial first layer
counted as though applied to all four frames (with all four at stage 1 or at stage 2),
and a second layer applied over frames the first never reached
(`stage_units_unsupported`), with the honest partial layer, late for one frame,
accepted beside them; a partial object over one unit;
and a delivered stage the objects do not give, among them one over objects that leave
a unit uncovered.
- **Rejected at steps 1–3:** a subject that is a receipt; a status, an evidence or a tier
outside its enumeration; `presented` null; an object without `reason`; units with a
member they do not define; a count written as a string; two defects, read in the
table's order; and `presented` rewritten after signing.
- **`delivery-gap`:** accepted against the object receipt at its coordinates, without
it, and chained to the relay's previous gap; refused when that receipt names another
object (`gap_subject_mismatch`), when the receipt given is at other coordinates or is
given to a claim that is not a gap (`context_mismatch`), for a reason outside its
enumeration, a date that does not exist, or a subject that is a receipt.
## What a sink reconstructed: `reconstruct-session`
A `reconstruct-session` claim (§2.4.12) is signed by a sink (key 0x44) about the entry
object of one session. Most vectors edit one model session: six units. Four are a stage
set over a pinned base — the session's entry object, a `WAI1` envelope holding a
four-frame `WIV1` clip — refined to up to three layers, with two late layer objects never
presented; each decoder entry names the object it decoded, so each layer applied has an
entry of its own. One unit is decoded from the first rendition of a `WAI2` envelope, a
one-frame clip, built by `wai-rs`'s container module, whose classical rendition the sink
did not need. The sixth is decoded on the fallback path after a switch and presented late.
Each object's figures are what its envelope gives, where the envelope is held: its
counted bytes (staged-measurement §3), its length, and whether an entry names it.
The vectors cover:
- **Accepted:** the model unmetered and metered under `OnChipCounter`, each against every
context: its presentations, re-decodes of byte-exact presentations that match, the
objects received, its base's `WAI1` envelope and the `WAI2` envelope, and the `stage`
claim it names; the model with no
context, and with its `stage` claim but neither presentations nor objects; a session
with no classical length beside an envelope whose second rendition is WAI's own; a
classical rendition of two other video capabilities; a base forgotten under the record
budget and decoded again with no layer sent again (`staged-conformance/`'s
`record_budget`, its envelopes and the `stage` claim of each epoch as the sink's
observer states them), naming both epochs' claims and only the first's; the base
fetched twice and listed once; an encrypted `WAI2` envelope received and named by no
entry, uncounted, and one named by an entry, decoded by a sink holding its key; inert
bytes no entry names; entries naming a `WAI2` rendition by its exploded envelope's
content hash; bases carrying a stray `refines`; refinements beside a conforming
companion; a session holding its base's and a
layer's envelopes, and a layer with no span through which a unit of its base is
presented, with its base held and not; output trees
of 1 to 33 leaves, half through a
decoder whose replicate class makes it byte-exact and half through one checked for
membership only, each re-decoded (the second with another digest, which is not
checked); the caps of switches and missed presentations, written in full and up to the
cap; and a `measurement` claim on a metered session's figure, under
`moq-jwp:claim-link`, by the session's key.
- **Rejected at step 4,** each rule with its reason: units, leaves and paths that do not
add up, a root without leaves or leaves without a root, three workers, a decoder
identifier of the wrong grammar, a tier without a pin or one SPEC §3.1 does not define,
module statuses that do not fit, a rendition past 65535, an entry twice or with no
leaves, a refinement entry pinned above its class's tier, switches between one path and
itself, for an unlisted reason, out of order, or written in another number than their
total (or 1024), missed presentations with no deadline, above the leaves, written in
another number than `missed` (or 256), zero microseconds late or out of order, a stage
set listed twice, more bytes discarded than counted, a classical length without its
source or with an unregistered one, and a figure of zero or of every acquisition-class
defect.
- **Rejected at steps 1–3:** a subject that is a receipt; a missing and an unknown member;
`media`, `framing`, a host profile and a decoder path outside their grammar or
enumeration; a count written as a string; the host read before the decoders; an energy
object missing a member; and a figure written into an unmetered session after signing.
- **Rejected at step 6:** a presentation whose output differs, a list one short, a
(unit, layers) twice (`session_output_mismatch`); leaves moved between entries, a
presentation naming an entry that does not exist, entries out of first-use order, paths
and units that the presentations do not give, a missed presentation never made
(`session_output_inconsistent`); a byte-exact re-decode that differs
(`session_output_irreproducible`), and a byte-exact entry relabelled to a class checked
for membership only by each route — the base's `WAI1` entry, a `WAI2` entry with its
rendition kept or nulled, a capability another received envelope has — refused against
the envelope of the object it names, as is an entry naming a rendition whose envelope is
not held (`session_source_mismatch`); an entry object never received
(`session_entry_not_received`); an entry's object, or an envelope held, the session did
not receive (`session_source_not_received`); objects whose counted, uncounted or
discarded bytes differ, and an object's counted bytes stated above what its held
envelope gives (`session_counted_mismatch`); an object listed twice
(`session_object_duplicate`); an object a decoder entry names marked not presented, and
one no entry names marked presented, the sums restated to agree
(`session_presented_mismatch`); a `WAI1` envelope with no manifest members and bytes
of neither magic, each named by an entry (`session_envelope_unreadable`); an
encrypted envelope named by one, checked against its manifest and rendition table in
the clear, refused for a classical length unstated, a rendition out of its table,
another capability, no rendition, a base at two layers, a companion-only capability,
a nonconforming refinement and a rendition whose `sha256` is malformed (uppercase, 63
digits, not hex) named by its rendition and by its exploded envelope's content hash,
and accepted with a well-formed `sha256` its payload does not match; a held inert
envelope listed with counted bytes 0
(`session_counted_mismatch`); entries a sink cannot have decoded through — a `WAI2`
companion, a rendition or refinement whose payload does not match its `sha256`, a
`WAI1` whose capability is companion-only, a fallback that is
(`session_source_mismatch`); held layers a sink cannot apply, each row of the table
below that names rule 5 (`session_layer_not_applicable`); a layer's entry at another
`first_unit` than its base's entry's, as a `WAI1` layer, as a `WAI2` refinement
component and across a `WAI2` component and its exploded envelope, and the entries of
a `stage` claim's base and of a layer it lists at another than the unit `stage_sets`
names the claim at (`session_first_unit_mismatch`); a unit presented at more layers
than its layer envelope, its refinement component or its base gives
(`session_layers_mismatch`); a unit past the base's frames, the same past the frames
of a base decoded through its manifest's `fallback`, in the fallback's format where
the capability's is another, outside a layer's span, a `WAI2` rendition's frame, a
refinement component's base or a `WAI2` base a `WAI1` layer refines, and through an
entry whose `first_unit` is moved (`session_unit_outside_object`); a second rendition of a capability the session decoded,
of another media, WAI's own, a companion, a record, a parameter set, a neural codec, an
unregistered string, or a text capability in a text envelope — none a classical
rendition the session did not need — and a length one byte off
(`session_classical_mismatch`); a `stage`
claim whose signature is broken (`context_invalid`), signed by another key, or not named
(`context_mismatch`); a `stage` claim counting more units at stage 2 than the session
presented there, a set placed so that one of its units was never presented, units
presented at stages no applied layer reaches — among them units presented through a
first and a second layer the claim lists as late and a first layer it lists as
refused — a `stage` claim about a base the session never received, objects received
that hold none of the layers it says arrived, and the forgotten base's session naming
only its last epoch's claim, which lists no layer, or presenting a unit, through a
layer it did not hold whole, past both epochs' layers (`session_stage_disagrees`); a session context given to a `stage` claim;
and a
`measurement` claim on an unmetered session, which carries no figure.
### The sink's refusals, and what the session claim does with each
Every refusal a sink makes that decides whether a presentation can happen —
staged-delivery §3, §4.1 (each step), §4.3–§4.5 and §9; SPEC §4 (step 5, the fallback),
§5 and §7.1; and `WaiMulti::check_component_roles`'s refinement codes — is either refused
by a rule of jwp-receipts §2.4.12 against the envelopes, with a vector here that reaches
it, or left to the signer's statement by the sentence of §2.4.12 that names it ("So,
deliberately, is each refusal a sink makes that the rules above do not check"). A row
with neither is a hole; there is none.
| source | refusal | §2.4.12 | vectors |
|---|---|---|---|
| staged-delivery §3 | `refines` malformed: member types and ranges, `prev`'s forms, adjacent runs of one producer, an array of one run, a producer other than `base` at layer 1, runs that do not sum to the span | rule 5, `session_layer_not_applicable` | `session_refines_layer_zero`, `session_refines_span_empty`, `session_wai2_refines_id_malformed` |
| staged-delivery §3 | `refines` absent on a refinement-only capability | rule 5 | `session_refines_absent` |
| staged-delivery §3, SPEC §4 | `refines` on a manifest or rendition whose capability is not refinement-only: a sink routes by capability, so it is a base and the `refines` is ignored (in a `WAI2`, a broken component: no refinement of that envelope is offered) | rules 4 and 7 read it as a base | accepted: `session_stray_refines_base_accept`, `session_stray_refines_layer_over_it_accept`, `session_stray_refines_wai2_rendition_accept`; refused: `session_refines_on_a_base_capability` (at two layers, `session_layers_mismatch`), `session_wai2_refines_on_a_rendition` |
| staged-delivery §3, §4.1 step 2 | with the base: a span outside its units, runs that do not sum to the units covered | rule 5 | `session_refines_span_past_base_alone`, `session_wai2_refines_span_past_base_alone` (the payload's count right, so the span check alone decides), `session_refines_span_past_base`, `session_wai2_refines_span_past_base`, `session_refines_runs_short`; accepted: `session_refines_runs_whole_accept` |
| §4.1 step 1 | `capability-not-refinement`: a layer with a fallback (an envelope of any other capability is a base, the row above) | rule 5 | `session_layer_with_fallback` |
| §4.1 step 2 | `base-absent`: no base decoded with the content hash `refines.base` | where the base is held and is a layer, rule 5; otherwise the signer's statement | `session_refines_base_is_a_layer`, `session_wai2_refines_base_is_a_refinement` |
| §4.1 step 3 | `base-via-fallback` | rule 5 (applied over an entry decoding the base through its own capability); rule 6 ties the layer only to such entries | `session_layer_base_via_fallback`, `session_layer_tied_to_its_base_not_the_fallback` |
| §4.1 step 4 | `base-not-admitted` (SPEC §5, *Base admission*) | rule 5 | `session_layer_base_not_admitted`, `session_layer_base_not_admitted_unnamed`, `session_wai2_layer_base_not_admitted` |
| §4.1 step 5 | `refinement-unattested` | the signer's statement: no context carries a delivery receipt | — |
| §4.1 step 6 | `payload-malformed`: the payload does not parse, or holds another number of units than the layer covers | rule 5, where the count of units covered is read | `session_layer_payload_not_wir1`, `session_layer_payload_units_short` |
| §4.1 step 6 | `payload-malformed` for a stream that does not decode canonically; `cost-over-limit`; `chain-over-limit` | the signer's statement; a re-decode of a byte-exact presentation checks its output | — |
| §4.1 steps 7–10 | `layer-conflict`, `prev-mismatch`, `layer-gap`, `geometry-mismatch`, `deadline-missed` | the signer's statement; rules 7 and 8 bound a layer's presentations to its count and units, and the `stage` claims the stages a unit reached | `session_layers_past_the_object`, `session_unit_outside_span`, `session_stage_beyond_applied` |
| §4.1, after step 10 | a unit presented from a layer that failed for it | rules 7 and 8 | as above |
| §4.3–§4.5 | the hold bound, batches, the chain budget for a base | the signer's statement | — |
| staged-delivery §9 | a `WAI2` component and its exploded envelope are one object | rules 5, 6 and 8 read a base in either form, and rules 4–8 read an entry naming a rendition by its exploded envelope's content hash, with only the `WAI2` held, as naming the rendition; the exploded envelope's own bytes, held, are a separate delivery, and whether they were received on their own is the signer's statement | `session_alias_layer_on_component_accept`, `session_alias_layer_first_unit_not_its_base`, `session_alias_layer_unit_past_the_base`, `session_alias_exploded_held_first_unit`, `session_alias_component_layer_named_as_exploded`, `session_alias_entry_accept`, `session_alias_entry_relabelled`, `session_alias_entry_unit_past_the_base`, `session_alias_refinement_entry_first_unit`, `session_alias_refinement_entry_not_admitted`; a separate delivery: `session_roles_exploded_layer_delivered_alone_accept` |
| SPEC §4 steps 3, 5 | a `WAI1` decoded through its `capability`, else its `fallback`, and nothing else | rule 4, `session_source_mismatch` | `session_relabel_wai1_base`, `session_relabel_av1` |
| SPEC §4 | a capability that is not dispatchable never taken; a `WAI1` whose capability is companion-only inert, its fallback never taken | rule 4 | `session_wai1_fallback_not_dispatchable`, `session_wai1_companion_only_capability` |
| SPEC §4 step 5 | the fallback decodes the same payload | rule 8 reads the units in the payload's own format | `session_unit_fallback_past_the_title`, `session_unit_fallback_format_past_the_title`, `session_unit_fallback_format_audio`; accepted: `session_unit_fallback_accept`, `session_unit_fallback_format_accept` |
| SPEC §3.1, §4 | a pin that does not resolve, a module not held, a key log's revocation | the signer's statement, which a decoder entry's `pin` and `tier` state | — |
| SPEC §6.4 | an encrypted envelope is inert at a sink without its key; one holding the key decrypts and dispatches it (step 3), its manifest and rendition table in the clear | rule 3 admits an entry naming it; rules 4–9 read its manifest and table as any held envelope's, a `sha256` that is not well formed included, and what needs a payload — whether a payload matches a well-formed `sha256`, a base's units, a layer's `WIR1` payload — is the signer's statement. Rule 9 reads the classical length from `WAI2` envelopes held in full: of one a sink received only as byte ranges it holds the head and not the envelope, and this revision cannot yet state that envelope's classical rendition (a limitation; recording such a session is HTTP session work still to come) | `session_envelope_encrypted`, `session_envelope_encrypted_layer_accept`, `session_envelope_encrypted_sha256_unmatched_accept`; refused: `session_envelope_encrypted_sha256_uppercase`, `session_envelope_encrypted_sha256_63_digits`, `session_envelope_encrypted_sha256_not_hex` (each `_exploded` too), `session_envelope_encrypted_classical_unstated`, `session_envelope_encrypted_rendition_out_of_range`, `session_envelope_encrypted_relabelled`, `session_envelope_encrypted_no_rendition`, `session_envelope_encrypted_base_layers`, `session_envelope_encrypted_companion_only`, `session_envelope_encrypted_roles` |
| SPEC §5 | a refinement-only capability is never a `capability` or `fallback` a sink dispatches, a selectable rendition or a companion | rules 4 and 5 | `session_layer_with_fallback`, `session_wai2_refines_on_a_rendition` |
| SPEC §7.1, *Selection algorithm* | a companion, or a capability that is not dispatchable, is never picked | rule 4 | `session_wai2_entry_names_a_companion` |
| SPEC §7.1, *Component identity* | `component_digest_mismatch`: a rendition whose payload does not match its `sha256` is skipped, a refinement is not offered | rule 4 | `session_wai2_rendition_digest_mismatch`, `session_wai2_refinement_digest_mismatch` |
| SPEC §7.1, *Component identity* | an `id`, `sha256` or `companion_of_id` that is not a string: the reader rejects the envelope | rule 3 | `session_wai2_component_id_not_a_string` |
| SPEC §7.1, *Component identity* | a component, in any role, whose capability is a record or a derivation (`record-or-derivation-as-component`): the envelope does not conform | rule 5, the whole envelope | `session_roles_record_rendition`, `session_roles_record_companion`, `session_roles_derivation_companion`; `pin-conformance/selection.json`: `record_as_rendition`, `derivation_as_rendition`, `record_as_companion`, `derivation_as_companion` |
| SPEC §7.1, *Refinement components*, *Selection* (every `WaiMulti::check_component_roles` code: the refinement codes, `refines-on-non-refinement`, `refinement-only-as-component`, the companion codes, `component-id-malformed`, `duplicate-component-id`) | a sink offers a `WAI2`'s refinements only when every component conforms | rule 5, the whole envelope | `session_wai2_refinement_identity_missing`, `session_wai2_refines_base_missing`, `session_wai2_refines_base_companion`, `session_wai2_layer_base_not_admitted`, `session_wai2_refinement_prev_missing`, `session_wai2_refinement_before_selectable`, `session_roles_refinement_before_selectable_exploded`, `session_wai2_refines_on_a_rendition`, `session_roles_base_without_sha256`, `session_roles_layer_before_its_prev`, `session_roles_unbound_companion`, `session_roles_record_companion`, `session_roles_derivation_companion`, `session_roles_record_rendition`; accepted: `session_roles_companion_and_refinement_accept`, `session_roles_companion_by_id_and_refinement_accept` |
| SPEC §7.1, *Companion components* | a companion that breaks rules 1–3 or its `companion_of_id`; an enhancement layer whose base is not unique | for the rendition picked, no bearing: a sink does not apply the companion and may still present the rendition, the output's digest holding whatever was applied; for the envelope's refinements, the row above | `session_roles_unbound_companion` |
## Steps 1 and 2: types, and the order a body is read in
§3.1 step 2 checks exactly the type a member table gives, member by member in the
table's order; whatever a class's rules add to a type is step 4's, with the rule's own
reason. Step-4 vectors pin that boundary from the rules' side: a zero figure
(`energy_zero_figure`), a zero work count (`energy_zero_work`), an empty basis
(`lawful_basis_purpose_without_basis`) and an `http` endpoint
(`soft_binding_endpoint_not_https`). Others pin what the text settles on the other
side, each signed again after its edit so the edit is its only defect:
- **Step 1:** a byte-order mark before the text is `malformed` (RFC 8259 lets a parser
ignore one); a `class` written as a number is `field_type`, not `unknown_class`.
- **Step 2, types:** an empty `alg` is not a term; an endpoint with no scheme is not an
absolute URI; a work unit holding U+3000, a White_Space character outside ASCII, is not
a term. Each is `field_type` at step 2: `alg` is a term, not a string left whole to
step 4's registry form; an endpoint's type is an absolute URI, and only `https` is step
4's; a term's white space is Unicode's White_Space, not ASCII's.
- **Step 2, order:** a lawful-basis body whose `record` names neither record is
`field_type`, since `record` is read first; a declaration whose vocabulary has no scheme
and whose purpose has no basis is `field_type`, since the table reaches `vocabulary`
first and its encoding is checked as it is read; and one whose first purpose's term
holds a space and whose second carries an extra member is `field_type`, since each
purpose is read whole before the next. A reader that checks every purpose's members
before the other members, or every encoding after every member, reports these two as
`missing_required_field` and `unknown_member`.
## The OER receipts
The OER receipts in `oer` contexts are sealed with `wai-rs`'s OER module (its canonical
form and content address), and read and verified by each verifier's own OER verifier
under [oer-receipts](../extensions/oer-receipts.md): the reference verifier
(`../oer-conformance/oer2_verify.py`) reports the valid ones valid, the one altered after
sealing as `receipt_id_mismatch` and `receipt_hash_mismatch`, and as `malformed` the six
whose figure is not a quantity oer-receipts §4 reads or whose body holds a number §4
forbids: `joules_total` as a decimal string of 35 significand digits, as the JSON number
`0.0120005`, as `-0.0`, as the integer 2⁵³, spelt `+0.012000`, and as `true`. Two valid
ones state the claim's figure and grade but are not about its object (an unrelated
object, and the object's hash spelt in uppercase); step 6 rejects both.
Seven vectors state a figure step 6 reads at its edges, as oer-receipts §4 reads a
quantity and compares it exactly: `0.0120005`, exactly half-way, which admits 12,000 and
12,001 µJ; the same decimal written `1.20005e-2`; the integer `1`, one joule; and two
figures written with 34 significand digits, the most a decimal string holds — one exactly
half-way, and one (`0.0120004` followed by twenty-six nines) below half-way only in its
last digits, which is 12,000 µJ and not 12,001, although rounding it to 28 significant
digits would make it a tie.
## Signatures
Seven vectors test §3.1 step 3 beyond an ordinary bad signature.
- **Six satisfy the verification equation** — the cofactorless one, and so the
cofactored one — with the key decoded leniently (`y` reduced mod p, the sign bit
ignored when x = 0) and `S` reduced mod L. The generator asserts this for each. Step 3
refuses them on two different grounds:
- *RFC 8032's own rules*, which step 3 restates: the identity key written with its sign
bit set, and written with `y` = p + 1, each with a signature made without a private
key (§5.1.3 decoding refuses both encodings; the point they spell is also of small
order, so step 3's choice refuses them too); and `S` replaced by `S` + L (§5.1.7
requires `S` < L).
- *The choices step 3 fixes*: the identity key and a key of order 8, each "signed" with
no private key, and a genuine key signing with `R` the identity. An RFC 8032 verifier
that accepts keys and `R` of small order accepts these three.
- **One does not** (`signer_key_y_not_reduced`): a key of large order written with
`y` = p + j. Nobody can sign for that point, so the vector carries another key's
signature, which fails the equation under any decoding (the generator asserts that
too). It pins only the reason — `bad_signer_key`, not `signature_verify_failed` — and
does not show that a lenient verifier would accept a forgery.
The `cryptography` package's Ed25519 on its own is more lenient than RFC 8032's
decoding. Called on each vector's key, signature and signing input (`cryptography`
50.0.1 over OpenSSL 3.6.4, Python 3.14.7, macOS on arm64), it accepted five of the six
that satisfy the equation — both non-canonical spellings of the identity and the three
vectors of the second group — and of the seven refused only `S` + L and the `y` = p + j
vector. `claim_verify.py` therefore checks no signature with `cryptography`: step 3 is
`_ed25519.py`, WAI's one Ed25519 rule (SPEC §7 *Signatures*) written from RFC 8032's
definitions with the standard library, which the verifiers of the JWP, signer, HTTP and
packaging corpora call too.
## Running
```sh
# wai-rs: the canonical fixture, byte-identical regeneration, and every vector
cargo run --no-default-features --features receipt_claims --bin wai_claim_vectors -- verify ../claim-conformance
# the second verifier (needs Python 3 and `cryptography`, with which it signs the fixture)
python3 claim_verify.py
# the two verifiers' verdicts on generated inputs the corpus does not hold
cargo build --no-default-features --features receipt_claims --bin wai_claim_vectors
python3 differential.py <target-dir>/debug/wai_claim_vectors [--random N] [--seed S] [--keep FILE]
```
CI runs the first on the conformance matrix (Linux x86_64, Linux arm64, macOS arm64), and the second
and third on the linux runner, with a fixed seed, in `.github/workflows/wai-conformance.yml`.
`differential.py` writes its inputs in the corpus's format with no declared outcome
and runs each verifier's `verdicts` mode (`wai_claim_vectors verdicts <file>`,
`claim_verify.py verdicts <file>`), which prints one verdict per input. Its families:
JSON numbers and strings at the limits of what parsers accept, nesting depth, calendar
dates, URI authorities, acquisition classes with every combination of label,
uncertainty, filtering state and calibration reference around energy-measurement
§2.3–§2.5's rules (calibration references of every White_Space character, and of
U+001C, U+001F, U+200B and U+FEFF, which are not White_Space), OER figures near a
half-microjoule and at the edges of oer-receipts §4's quantities, OER receipts whose sums
and floors sit at §6's tolerances, OER receipts holding numbers §4 forbids in a body
(`-0`, fractions, exponents, integers beyond I-JSON's range) in members of every kind, a
delivered object's microjoules spelt as the corpus never gives them, context receipts
that do not themselves verify, bodies of every class with one defect from a fixed list
and with every pair of those at two different positions (a member, or a member of a
nested object), where the order §3.1 step 2 reads a body in decides the reason, and, with
`--random N`, a seeded random sample of dates, endpoints, figures, sums and class
combinations, and of sessions: each built from a random presentation list as an honest
sink states it, then given a defect or two in the body or in a context (units, leaves and
paths off by one, switches and missed presentations past their caps, a `null` deadline
with misses, presences that disagree, decoder indices out of range, leaves moved between
entries, entries out of first-use order, outputs dropped or changed, re-decodes that match
and that do not, objects counted otherwise, listed twice or marked presented otherwise,
counted bytes that are not a held envelope's, a `first_unit` moved, inert bytes received
and named by an entry or not, every acquisition class), checked against its presentations,
objects, WAI2 envelopes and a `stage` claim; and sessions holding their envelopes — a base
of one to six frames, a `WAI1` envelope whose manifest may name a fallback its entry
decodes through, of the capability's format or another, or a companion-only capability,
the base component of a `WAI2` envelope, named by its envelope or by its exploded
envelope, held or not, or such a component that `WAI1` layers refine by its exploded
envelope's content hash, and layers refining it at
layer 1 or 2 over a span or all of its units, as `WAI1` envelopes or refinement
components, now and then naming another base or a layer as their base, at layer 0, with
an empty span or one past the base, with no `refines`, of a capability that does not
admit the base, with a `WIR1` payload a unit short, or, as components, with no `sha256`,
no role or another payload's digest, over a base that is a companion — with presentations through
each at one to three layers, within what each envelope covers and past it, entries whose
`first_unit` is the base's or one off, the base's and the layers' each, and objects whose
figures are each envelope's or one told otherwise, listed twice, fetched again, or beside
inert bytes, the base held or not, and entries naming a `WAI2` base or refinement by its
exploded envelope's content hash, its own bytes held or not; and, over every registered
capability and an unregistered string, a refinement beside a companion of that
capability bound to the base, and beside an HDR10+ descriptor and an enhancement layer
bound to a rendition of it (SPEC §7.1's component rules, which decide whether the
envelope offers its refinements); and the model session with its `WAI2` envelope
encrypted, its `security` well formed or not, the entry decoding it naming a rendition
in its table, out of it or none, its capability or another, the named rendition's
`sha256` kept, absent, well formed and unmatched, or malformed, and the classical length
stated as the table gives it or not. Further session families:
sessions that pass steps 1–5 against a `stage` claim whose first layer applied to some
units and arrived late for others, presented at one to three layers, with objects that
hold or lack the set's base and layers (both bounds of §2.4.12's check), the base's or
the layer's entry now and then at another first unit than the set's; sessions naming
one to three `stage` claims of one set, as a sink that forgot its base names one per
epoch, each listing first and second layers of every status, sometimes one named a unit
further on, presented within and past what the claims holding each unit applied, and an
entry now and then at another first unit than the claims'; the missed
presentations and switches written at, around and past their caps; WAI2 envelopes whose
manifests readers may read otherwise (members of the wrong type, pins that are not pins,
`security` members, duplicate members, numbers and nesting past every range, byte-order
marks, lone surrogates) and whose framing is short or long, and the base's WAI1 envelope
likewise, with a capability and a fallback the base's entry does or does not name — each
with the session signed again so the entry names that envelope as its object; random
sessions whose entries name their envelopes, a layer of their own or another object, with
envelopes and objects that hold them or not; and every registered capability as an
envelope's second rendition, in the session's media and in its own. Separately, each value that makes a whole file unreadable under
oer-receipts §4 — a number beyond the double range, `NaN`, an unpaired surrogate escape,
a repeated member name, nesting past 64 levels — goes in a file of its own beside
readable twins, and the two verifiers must both refuse the file or give it the same
verdicts. It exits 0 only if the two verdict lists are identical, every file is read or
refused alike, and neither verifier crashed; any difference exits 1, at whatever step.
Both verifiers read a corpus-format file as oer-receipts §4 reads a vector document,
since it holds OER receipts: `wai-rs` with `oer::load`, `claim_verify.py` with the OER
reference's `load`. A difference where one side reports `energy_oer_invalid` is marked,
since its cause is in the two OER verifiers rather than the claim rules, but it counts
the same. Those two are compared on their own by `../oer-conformance/fuzz_differential.py`.
## What each check can and cannot show
The expected outcomes are declared per vector in the generator (`wai-rs`,
`claim_receipt::corpus`), not copied from a verifier's output. The signed bytes,
though, are produced by the same module that verifies them, so a canonicalisation
defect would be baked into every signature and link hash and then confirmed. Two
checks close that:
- **The canonical fixture.** An `OnChipCounter` claim given with members out of order at
three levels, whitespace, and `\u` escapes in the input text. Its canonical bytes,
signature and link hash were computed by `claim_verify.py fixture` — Python's JSON
serialiser with members ordered by UTF-16 code units, `cryptography`'s Ed25519 signing, and
BLAKE3 written from its specification — and are pinned as literals in `wai-rs`
(`check_canonical_form`), which `wai_claim_vectors verify` checks first. Reversing
the member order, or changing the signing domain, fails it.
- **The second verifier.** `claim_verify.py` reports every vector's declared outcome and
every pinned link hash, including a claim whose canonical form spans three BLAKE3
chunks, and checks that the fixture it computes equals the literals in `wai-rs`. Its
§3.1 step 3 — RFC 8032 key decoding, the order of the key and of `R`, `S` < L and the
cofactorless equation — is `_ed25519.py`, written from the curve's definition. Its
acquisition-class rule is
written from energy-measurement §2.3–§2.5, where `wai-rs` admits the class through
`crate::energy_class`.
The corpus shows the two verifiers agree on its vectors and nothing more.
`differential.py` checks agreement on inputs no vector declares an outcome for: that
the two read the same inputs the same way. It does not show that either reading is the
extension's, and it covers only the families it generates.
The second verifier and the differential were written by the same author as `wai-rs`,
in a different language with no shared code. They catch disagreements in canonical
form, hashing, signatures and the reading of JSON, dates, hosts, classes and figures;
they are weaker evidence against a misreading of the extension that both verifiers share.
Two of the order vectors above (`lawful_basis_vocabulary_before_purposes`,
`lawful_basis_purposes_in_order`) exist because of one: both verifiers once read a
declaration's purposes before its other members, which §3.1 step 2 does not say, and
agreed with each other throughout. It was found by reading the text against the code;
neither check could have shown it.
## Regenerating
```sh
cargo run --no-default-features --features receipt_claims --bin wai_claim_vectors -- gen ../claim-conformance
```
Ed25519 signing is deterministic and the seeds are fixed, so the output is
byte-identical on every machine; `verify` fails if the pinned file differs from it.