open-standards/wai/claim-conformance
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science 4e7155b955
Some checks failed
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Successful in 32s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 4m8s
standards workspaces build + test / aac-rs (pull_request) Successful in 3m7s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m4s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 52s
standards workspaces build + test / map-rs (pull_request) Successful in 1m10s
standards workspaces build + test / mesh-rs (pull_request) Successful in 57s
standards workspaces build + test / openpay (pull_request) Successful in 4m31s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Successful in 59s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 1m59s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 59s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m11s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m32s
standards workspaces build + test / eoc-rs (pull_request) Successful in 2m50s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m55s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m34s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m12s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m13s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m20s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m22s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m22s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 28s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 6m51s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 4m15s
wai-ops integer operators / linux-aarch64 (pull_request) Successful in 1m3s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Failing after 1m29s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 8m40s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 12s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 48m7s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 25s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 51s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m57s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 7s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m16s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Successful in 1m13s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m15s
wai-ops integer operators / linux-x86_64 (pull_request) Successful in 25s
wai-ops integer operators / wasm32-wasip2 (wasmtime) (pull_request) Successful in 24s
wai-ops integer operators / wasm32 under Node (pull_request) Successful in 20s
wai-ops integer operators / independent checker (Python) (pull_request) Successful in 11s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 4s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 16m41s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 4s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m35s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 5s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m3s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 31m19s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 1m1s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 23m0s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 33s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m37s
wai-ops integer operators / macos-arm64 (pull_request) Successful in 27s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m3s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Successful in 38m28s
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Successful in 38m16s
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Successful in 4m35s
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Successful in 5m21s
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Successful in 4m30s
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Successful in 4s
wai-pack / wai-pack · macos (pull_request) Successful in 2m11s
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Successful in 2m21s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m11s
wai-pack / wai-pack · linux (pull_request) Successful in 1m13s
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Successful in 37m30s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 22s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 1m57s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m23s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Failing after 2m9s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m14s
Merge wai/reach-builds (with #197 and main) into wai/d1-record-true
Brings in #205 and #197 and, through them, main as of #200 and #206.

- wai/extensions/quantum-sim.md: both sides rewrote the status line's
  reference implementation. Kept main's two capabilities (circuit2 WQC2,
  circuit WQC1 frozen) and this branch's wording of where the reference
  lives (the `quantum` feature of wai-quantum, forwarded by wai-rs).
- wai/http-conformance/falsify/run.sh: #200 rewrote the single-run script
  into shards; took the sharded runner unchanged and ported this branch's
  additions into it: manifest-limit-dropped.patch sits with the other
  patches with its must-fail cases on its first line (it applies to the
  merged head.rs unchanged), and the Python mutation manifest-limit-dropped
  is declared in falsify/mutations.txt, after reload-walked where the old
  list had it, with the cases it must fail:
  head_malformed_manifest_too_large_O5 and _OD. The 3/3 shards cover
  134 patches and 78 mutations, each once.
- wai/http-conformance/cases.json: regenerated with wai_http_vectors gen;
  the generator's output equals the merged text (1049 cases), and both
  wai_http_vectors verify and http_verify.py give every outcome.
- wai/CHANGELOG.md: no textual conflict, but git left this branch's entry
  below "Staged measurement"; moved "The record made true" to the top of
  v0.4.0, newest first, above #205's and #197's entries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:35:03 -04:00
..
.gitattributes wai: typed receipt classes and the soft binding class, reading OER through the OER module 2026-09-30 01:42:45 -04:00
_blake3.py wai: http_verify.py, the BLAKE3 tree in _blake3.py, and OH with chaining values 2026-10-05 07:51:43 -04:00
_ed25519.py Python verifiers: one strict Ed25519 rule, _ed25519.py 2026-10-07 09:22:27 -04:00
claim_verify.py Merge wai/reach-builds (with #197 and main) into wai/d1-record-true 2026-10-10 17:35:03 -04:00
differential.py wai: the recorder derives the classical length; a malformed sha256 refused behind encryption 2026-10-08 03:02:27 -04:00
README.md wai: state the ranged-WAI2 classical length as a limitation of this revision 2026-10-08 05:23:56 -04:00
vectors.json wai: the recorder derives the classical length; a malformed sha256 refused behind encryption 2026-10-08 03:02:27 -04:00

claim-conformance

Conformance for typed receipt classes and the soft binding class (§2.4.3–§2.4.12, §2.6, §3.1–§3.2): claim receipts of class energy, lawful-basis, log-inclusion, soft-binding, 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.

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.

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 §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: 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

# 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

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.