Some checks failed
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 1m4s
wai-ops integer operators / does this change touch it (push) Successful in 5s
wai-quantum studio and desktop / does this change touch it (push) Successful in 5s
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 1m25s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 2m5s
wai-ops integer operators / linux-aarch64 (push) Successful in 1m1s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 6s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 3m0s
wai-ops integer operators / linux-x86_64 (push) Successful in 57s
wai-ops integer operators / wasm32-wasip2 (wasmtime) (push) Successful in 29s
wai-ops integer operators / wasm32 under Node (push) Successful in 21s
wai-ops integer operators / independent checker (Python) (push) Successful in 14s
wai-quantum studio and desktop / studio · browser (push) Successful in 2m23s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-ops integer operators / macos-arm64 (push) Successful in 1m31s
7470 lines
556 KiB
Markdown
7470 lines
556 KiB
Markdown
# WAI — changelog
|
||
|
||
Notable changes to the WAI standard and the reference implementation.
|
||
Newest at the top. WAI is a draft standard; until v1.0 freezes, the
|
||
wire format may change and such changes are recorded here.
|
||
|
||
## v0.4.0 — current
|
||
|
||
### `wai.media.upscale`: super-resolution as an exact derivation, residuals against it, and an account of sink-made pictures
|
||
|
||
- **The extension** ([exact-upscale](extensions/exact-upscale.md)) registers the record
|
||
`wai.media.upscale` and two integer derivations, `wai-upscale:sr2` and `wai-upscale:sr4`: the
|
||
pinned upscale of integer-operators §5E E11 plus a detail an integer network computes from the
|
||
picture, clamped to the sample range. The network is a `WSR1` prior (§3.1), one file a sink
|
||
holds by its SHA-256 under SPEC §3.1: up to eight convolutions of signed-byte weights, each with
|
||
a per-channel multiplier, a shift and `half_up` rounding, the hidden ones clamped, the last one's
|
||
twelve channels shuffled to the two sub-positions of each colour. Colour is `RGB8`, the channels
|
||
together; the base repeats the last row and column, and the network reads mid-grey outside the
|
||
picture; `sr4` is `sr2` twice under one prior.
|
||
- **Through the integer operator set.** The lowering `sr2/1` (§3.4; integer-operators §13.7)
|
||
writes one `WHG1` graph of `rescale`, `conv`, `clamp`, `pixel_shuffle` and `add`, which loads under
|
||
`acc64` with its bounds certificate, and under `acc32` where the certificate meets it: every
|
||
operator is in `wai-ops/1-i32`, and the GPU path runs it. Every sink presents the same bytes,
|
||
so the high-resolution picture is a function of the transmitted bytes and the prior.
|
||
- **Residual against the known upscale.** An origin can send a `WIR1` payload of staged-delivery
|
||
Appendix A with the identity predictor, decoded over the upscaled picture, and sign an
|
||
**upscale record** (`wai-upscale`, §4.2) stating the input, the prior, the upscale, the output
|
||
and the target with the bound within which the output lies: 0 for the target itself, ⌊q/2⌋ for
|
||
a residual of step q.
|
||
- **Per-title priors** (§5): a prior fine-tuned on one title, carried by digest like any other,
|
||
and sent only where the bytes it saves exceed its own. `tools/wai_int_sr_train.py` trains priors
|
||
in floating point from fixed seeds and quantises them to `WSR1`; its training is not claimed to
|
||
regenerate the same bytes on another machine, and the priors' bytes are what is pinned.
|
||
`wai_upscale_measure` codes the full-resolution picture directly with `WIV1`, through staged
|
||
delivery's up2 layer, and through the derivation and a residual, at matched quality (every
|
||
sample within ⌊q/2⌋), on the pictures the tool generates.
|
||
- **Accounting for sink-made pictures** (§6). A sink's **presentation statement**
|
||
(`wai-presentation`) lists, for a span, each picture it presented as `transmitted` (decoded, or
|
||
refined by a residual to the origin's target), `derived` (an exact derivation with no residual),
|
||
`asserted` (an edit no one can recompute, such as a float upscale) or `synthesized` (generated,
|
||
such as a frame between two others), with its pixels counted by category and the span's totals.
|
||
Each picture is held to its origin claim: an upscale claimed captured, or a generated frame
|
||
claimed assistive, is refused (`made_understated`). A generated picture names the model that
|
||
made it, in the slot `made_by` of the contribution record `wai.media.contribution`.
|
||
- **Synthesis** ([synthesis](extensions/synthesis.md)): an origin claim's subject may be a
|
||
picture by the digest of its samples, for output a sink presents and does not publish; replicate
|
||
evidence may name `wai-upscale:sr2` or `wai-upscale:sr4` with `{"prior"}`, proved by
|
||
recomputation from the input's samples and the prior a verifier holds (§4.3, §4.4, §11.1); and a
|
||
pinned integer derivation over the decode keeps a presentation path's tier (§9.2;
|
||
determinism-tiers §2). Its corpus gains a sink's upscale proved, one whose recomputation differs,
|
||
one under a prior that does not read, and one claimed about other samples.
|
||
- **Reference and corpus.** `wai-rs` module `int_sr` (feature `neural_int`), module
|
||
`exact_upscale` (feature `exact_upscale`), and the GPU test `tests/exact_upscale_gpu.rs`
|
||
(features `ops_gpu`, `exact_upscale`). `live-conformance/upscale/`, written from fixed seeds by
|
||
`wai_upscale_vectors` around two trained priors pinned by SHA-256: upscales under the generic,
|
||
a seeded and a per-title prior, residuals exact and to a bound, a span of four kinds of
|
||
picture, and every reason of §8 refused. Checked by `tests/exact_upscale.rs` and by
|
||
`live-conformance/upscale_verify.py`, a second implementation that shares no code with `wai-rs`,
|
||
runs the network with `live-conformance/_int_sr.py`, and requires every mutated copy of the
|
||
corpus to fail. The registry lists the record, the magic `WSR1` and the document kinds
|
||
`wai-upscale` and `wai-presentation`.
|
||
|
||
### `wai-mark:tent64`: the second marking layer, a watermark recomputed from its payload and read with no key
|
||
|
||
- **The scheme** ([synthesis](extensions/synthesis.md) §7.4, §7.5) fills the scheme slot of §7.4: a
|
||
mark in the luma of RGB8 pictures and video frames, registered by a term and the SHA-256 of its
|
||
definition. Each frame is cut into a 64 × 64 grid that follows the picture through a rescale; a
|
||
separable tent checkerboard in each cell is zero-mean, so brightness leaves a cell's statistic
|
||
unchanged and contrast scales it. A public SHA-256 stream shuffles the cells, signs them and
|
||
masks the code; the 8-byte payload is a BCH(255, 71) codeword shortened to 248 bits, each bit
|
||
carried by 16 cells scattered over the frame. Each bit gets the least amplitude, up to 12 luma
|
||
levels, that gives its statistic the sign the codeword asks with a margin of 3/2 of its weight
|
||
mass, so a frame is changed only as far as the margin asks. A video carries the whole payload
|
||
in every frame.
|
||
- **No other channel.** The embedding is a pinned integer derivation of the unmarked frames and
|
||
the payload: a verifier holding the unmarked frames recomputes the marked ones
|
||
(`mark_recompute_mismatch`), so the mark carries exactly its payload, a function of the claim,
|
||
and no viewer or session identifier. A sink that generates content marks it as part of the
|
||
reconstruction its tier holds (§7.8). The marking record gains `embedding` (the frames' size
|
||
and the digests of the unmarked and the marked frames).
|
||
- **Key-free detection** in the 8 quarter turns and mirrors, decoding within 29 bits; a
|
||
false-positive bound of 2^−61.39 derived from the code and checked against the words read from
|
||
unmarked pictures, which match the uniform model. The detection record gains `read` (the
|
||
frames it read), and a verifier that holds them runs the detector again
|
||
(`detection_irreproducible`). A conversion that does not mark again carries its input's mark,
|
||
and its detection agrees by inheritance (`agree-inherited`).
|
||
- **Robustness measured, not asserted.** An attack battery of integer attacks (re-encoding with
|
||
the crate's own `wai.video.int_motion` encoder and a staged base, the pinned derivations of §4,
|
||
area resampling, crops, quarter turns and mirrors, small rotations, noise from a fixed stream,
|
||
brightness, contrast, a local blur; for clips, inter coding and dropped frames), over
|
||
measurement sets the crate generates, gives the table of §7.5.8; the scheme claims to survive
|
||
only what every set and a set of photographs survived. It claims no crop and no small rotation.
|
||
PSNR and SSIM per picture, and a PSNR floor that depends on the frame's size alone.
|
||
- **Signpost and resolver** (§7.7): the marking's C2PA statement (`c2pa.watermarked.bound` and a
|
||
`c2pa.soft-binding` assertion naming the scheme and its digest), the openly disclosed mark
|
||
itself, and `/.well-known/wai-mark/<name>`, proposed, for the definition and for claims by
|
||
payload.
|
||
- **New codes**: `scheme_mismatch`, `mark_evidence_invalid`, `mark_input_mismatch`,
|
||
`mark_recompute_mismatch`, `detection_irreproducible`, `layer_not_proved`; the policy member
|
||
`second_layer`. `wai-mark:conformance` stays, as the scheme with no algorithm.
|
||
- **Reference and corpus**: `synthesis::mark` (feature `synthesis`) and `synthesis_mark` (feature
|
||
`synthesis_mark`), with `wai_synthesis_mark_vectors gen|verify|measure`;
|
||
[`live-conformance/synthesis-mark/`](live-conformance/synthesis-mark/), and its second
|
||
implementation `synthesis_mark_verify.py` with `_wai_mark.py`, which mutates the corpus and
|
||
weakens its own verifier in turn. Audio is not reached in this revision.
|
||
|
||
### `wai.media.exploration`: archives that replay, default-distance, a homogenization meter, and taste as a declared, owned objective
|
||
|
||
- **The extension** ([exploration](extensions/exploration.md)) registers the record
|
||
`wai.media.exploration`. It builds on the exactness of `wai.generate.spectral`: what elsewhere
|
||
can only be estimated about a generator's outputs is recomputed here.
|
||
- **Descriptors.** Six integer derivations over a picture's `RGB8` samples, each from 0 to 1024:
|
||
mean luma, chroma, detail (the mean luma step between neighbours), palette diversity (the
|
||
Gini–Simpson index of a 64-colour quantisation) and the two coordinates of the luma centroid.
|
||
No learned model: they place a picture and judge nothing. A grid of one to four axes bins them
|
||
into niches.
|
||
- **Archives.** A generation request may ask for an archive instead of one picture:
|
||
MAP-Elites over a declared grid, through the prompt and up to eight modifiers the person
|
||
declares, run by the pinned schedule `wai-exploration:map-elites/1` from a declared search
|
||
seed. Each elite names its genome (seed, steps, modifier mask) and regenerates; the archive
|
||
replays bit for bit, so an elite dropped or chosen after the fact is refused
|
||
(`archive_replay_mismatch`). The placement rule is `none` (the first trial to reach a niche
|
||
keeps it) or a taste prior.
|
||
- **Default-distance.** A prompt's default set is seeds 0 to 15 at four steps. A work's distance
|
||
from it, in descriptors and in samples (0 exactly when the work is a default), is a signed
|
||
`measured` figure for the exact generator, an `estimate` for any other, and `unknown` with
|
||
every figure `null`, never 0.
|
||
- **Collection statements.** The descriptor coverage of a grid and the distribution of pairwise
|
||
descriptor distances of a gallery, a room's outputs, a session's candidates or an archive,
|
||
recomputed from the samples.
|
||
- **Taste priors.** An integer ranking model over the descriptors (`rise`, `fall`, `near` and
|
||
`far` terms, unsigned throughout), a one-file parameter set pinned as SPEC §3.1 pins one,
|
||
signed by its owner (`taste_not_owner` otherwise) and carrying the owner's `wai.ai.usage`
|
||
statement; a ranking under it reproduces, and states the use it made of the prior, which the
|
||
owner's statement may disallow. Personal and community priors are the same kind of object, and
|
||
none is a default.
|
||
- **Disclosure.** Every archive, ranking and pick that an aesthetic or reward model steered names
|
||
it by digest or states `none`. A missing disclosure is reported undisclosed, never `none`; a
|
||
policy may require it (`objective_undisclosed`), and a pick from an archive a prior steered must
|
||
name that prior (`objective_mismatch`). A pick from an archive inherits the elite's genome: the
|
||
slot the contribution record fills to point a `candidates` step at an archive.
|
||
- **Reference and corpus.** `wai-rs` feature `exploration` (`src/exploration.rs`), and
|
||
`live-conformance/exploration/`, written from fixed seeds by `wai_exploration_vectors gen`:
|
||
generations that check the generator's restatement, descriptor vectors, archives, distances,
|
||
collections, rankings and picks, accepted and refused at each rule. Checked by
|
||
`tests/exploration.rs` and by `live-conformance/exploration_verify.py`, a second implementation
|
||
that shares no code with `wai-rs`, implements the generator from the extension's Appendix A,
|
||
requires every mutated copy of the corpus to fail and every weakened copy of itself to be
|
||
caught. `wai_exploration_vectors measure` runs an archive against plain seed sampling and
|
||
against uniform sampling of the same genome space at equal compute. The three priors of the
|
||
corpus are written in code with hand-set weights; nothing is trained on anyone's choices.
|
||
|
||
### Privacy Pass admission end to end: one scope per room, admission by the room's service over HTTP, ratings over MOQT, and the serving rule at the relay
|
||
|
||
- **One scope per room** ([live-rooms](extensions/live-rooms.md) §10.4). The subscribing scopes
|
||
§10.4 named did not fit the 255 bytes `draft-ietf-moq-privacy-pass-auth-03` gives the scopes of
|
||
one MoQAuthorizationInfo: each scope restates its namespace match, 69 bytes for the room id, so
|
||
three fit and §10.2 step 5's four at the least (320 bytes) do not. A room's scope is now one
|
||
MoQAuthScope, the actions SUBSCRIBE_NAMESPACE, SUBSCRIBE, FETCH and TRACK_STATUS on
|
||
`MATCH_PREFIX` of the room id's tuple and on every track name, 78 bytes; what it does not carry
|
||
is applied from the room's own records. A verifier reads a challenge's `origin_info` by the
|
||
draft's grammar and refuses one that does not read `scope_malformed`, and one that reads and is
|
||
not the room's scope `scope_mismatch`, the previous revision's encoding by these names
|
||
([age-assurance](extensions/age-assurance.md) §5.2 step 2, §11).
|
||
- **Redemption contexts per window** (age-assurance §5.1, §5.3). A challenger may fix a
|
||
challenge's context for a room and a window of time, accepting the current window's and the
|
||
one before, so a token may be obtained before it is presented; it keeps the nonces of the two
|
||
windows it accepts and drops a window's when it stops accepting it, bounds them per window, and
|
||
past the bound redeems no further token rather than forget one.
|
||
- **Admission by the room's service over HTTP** (age-assurance §5.6). A room's service is a
|
||
Privacy Pass origin of RFC 9577: it answers a grant request without a token `401` with a
|
||
`PrivateToken` challenge for each key of the room's policy, its `origin_info` RFC 9577's list
|
||
of server names and its context the room's window; checks a token by §5.2 against the room's
|
||
age; and states the threshold in the grant's `age` (§5.4). Over HTTP a challenge's
|
||
`origin_info` must list the service's own name.
|
||
- **Ratings over MOQT** (age-assurance §5.7). A rating claim rides as the property `WAI_RATING`
|
||
(`0x3A4D`), a Track Property of a PUBLISH or SUBSCRIBE_OK and an Object Property of any object;
|
||
a relay holds what §3.6 lets it honour from the instant it reads it.
|
||
- **The corpus** (`live-conformance/age/`) gains the scope cases (the previous revision's three
|
||
and four scopes, another room's, an empty one, an action outside the draft's table, a byte
|
||
after the scope, a list of server names over MOQT) and the HTTP binding's (admitted under the
|
||
service's name and under a list holding it; refused under another origin, an empty list and
|
||
the MOQT scope), and RFC 9578 Appendix A.2's issuance (its nonce, blind, salt, TokenRequest
|
||
and TokenResponse), which both verifiers check: the blinded message is (*s*·*r*)^e mod *n*
|
||
and the response *s*·*r* mod *n*.
|
||
- **The reference stack.** `joule-moq` reads and writes rating claims as `wai-rs` does, and
|
||
agrees with the corpus on every claim and serving request. `joule-moq-relay` holds the claims
|
||
it is carried, bounded per room, per track and in all, and serves each session by the
|
||
thresholds of the grants its SETUP proved: a live object of a group above it is not
|
||
forwarded, the streams of groups a newly held claim raises are ended, and a SUBSCRIBE, a FETCH
|
||
or a fill is refused `below_threshold` at its start or answered up to the first refused group.
|
||
`joule-moq-room` (`[age]`) is the HTTP origin of §5.6 with `wai-rs`'s checks, keeps its nonces
|
||
per window, counts admissions by window, issuer and threshold alone, and signs
|
||
`wai-age-admissions` receipts with the room's clock (`GET /age/receipt`, and `receipts_out`
|
||
as each window closes). The room page answers the challenges with a token it obtains by blind
|
||
issuance (RFC 9578 §6, RFC 9474 §4.2 to §4.4) from the issuer's directory (RFC 9578 §4),
|
||
carries the room's rating on every track it publishes and, with `rate`, its own rating of its
|
||
video, and shows what the relay withholds. Its end-to-end scenario `age` runs it all against a
|
||
test issuer over the corpus's published keys.
|
||
|
||
### `wai.age.assurance`: content ratings carried through derivations, admission by an age threshold without identity, and the minors' profile
|
||
|
||
- **The extension** ([age-assurance](extensions/age-assurance.md)) registers the record
|
||
`wai.age.assurance` and three JSON records signed as synthesis' are: a rating claim
|
||
(`wai-rating`), an age policy (`wai-age-policy`) and an admission receipt
|
||
(`wai-age-admissions`).
|
||
- **Rating claims.** A claim rates an object, a track from a group on, a span of a track or a
|
||
room under one to eight schemes, each named by its vocabulary URI, with the value it gives and
|
||
the minimum age that value means; several national schemes sit side by side and WAI adds none.
|
||
An object's age is the greatest age of the claims that cover it, so a claim only raises, and a
|
||
span rated after its first group went out holds from that group whenever the claim arrives
|
||
(§3.5): a relay serves none of the span below its age from then on, live, by FETCH, from the
|
||
cache or from the DVR window. A derived object's claim names its inputs' claims by link hash,
|
||
and a derivation that names no claim for a rated input, or drops or lowers a scheme an input
|
||
is rated under, is refused `rating_dropped` (§3.4), as synthesis §3.4 carries an origin class.
|
||
A room's tracks are rated by its room keys and each track by its owner, and a claim another key
|
||
signs is refused `rating_not_authorised`.
|
||
- **Admission by threshold.** A room's age policy, signed by its authority, names Privacy Pass
|
||
issuers (RFC 9576, RFC 9577, RFC 9578) with one key per threshold, from 1 to 21, so a token of
|
||
the publicly verifiable type 0x0002 says "at or over *T*" and nothing else. A relay checks a
|
||
token in six steps (§5.2): it reads, its challenge is one the relay sent the session, its key
|
||
is the named issuer's, its RSASSA-PSS verifies, its nonce was not redeemed, and its key's
|
||
threshold is at least the track's age; it learns the issuer and the threshold alone. A grant
|
||
states the threshold its authority admitted the principal under in the attribute `age`
|
||
(live-rooms §4.4).
|
||
- **No return path.** On a one-way bearer a rating is a sink-evaluated predicate: the device's age
|
||
state is the greatest threshold of a credential it holds under an anchor provisioned on it,
|
||
each credential a token issued for the issuer's device challenge, and a player presents an
|
||
object only at or above its age, evaluates again as claims arrive, and sends nothing (§6).
|
||
- **Evidence without identity.** An admission receipt, signed by a room key, counts admissions
|
||
per window, issuer and threshold; a cell under 50 is suppressed, as live-metering's QoE cell is
|
||
under delivery-telemetry §8 (its paragraph 8.4); and a member beyond its table, at its top or in a cell, is
|
||
refused `receipt_identifies`, whatever it holds. Given the admissions, a verifier recomputes the
|
||
cells. Where a regime asks the operator for a lawful basis, the operator states it in a
|
||
`lawful-basis` declaration (jwp-receipts §2.4.6) about the receipt.
|
||
- **The minors' profile.** A room in which a grant that may publish and carries `minor` = `true`
|
||
takes effect must meet the profile, named from bitmaps it already has: recording `mode` 0, no
|
||
role with the record or the seek power, no role that publishes nothing permitting `chat` or
|
||
`reaction`, and no grant that publishes nothing permitting them either; otherwise
|
||
`minor_profile_unmet`. WAI defines no gift; a clip needs a recording or a DVR window, and the
|
||
room has neither.
|
||
- **live-recording** §4.4 step 5 refuses a package of a room whose recording policy has `mode` 0
|
||
(`recording_not_permitted`, capture-binding's code), takes or no takes; its corpus gains
|
||
`room_never_recorded`, and `live_record_verify.py` the check.
|
||
- **The relay** (`joule-moq-relay`) keeps no DVR window of a room once a session proves a minor's
|
||
publishing grant of it: what it held is deleted, nothing more is written, and the room is
|
||
remembered across a restart, as a room whose objects are sealed is; `joule-moq`'s grant head
|
||
reads the `minor` attribute.
|
||
- **Conformance.** `wai-rs` gains the `age_assurance` module (feature `age_assurance`), reusing the
|
||
ad-presentation module's RSASSA-PSS and token reader; `live-conformance/age/` is written by
|
||
`wai_age_assurance_vectors` from fixed seeds, its issuer keys the published test keys of RFC
|
||
9578 Appendix A.2, RFC 9500 §2.1 and RFC 7517 Appendix A.2, with RFC 9578's first token vector;
|
||
and `live-conformance/age_assurance_verify.py`, written from the text, is its second
|
||
implementation.
|
||
|
||
### `wai.live.ad_presentation`: a player's signed claim that an ad was presented, and a spend that counts only presented impressions
|
||
|
||
- **The extension** ([ad-presentation](extensions/ad-presentation.md)) registers the record
|
||
`wai.live.ad_presentation`. A presentation claim, `WPC1`, is a player's signed statement
|
||
about one impression of a live-ads break: the break, the session and the slot of its
|
||
form-2 `wai.video.ssai` receipt; the content hashes of the creative's media objects it
|
||
presented; spans of the creative's media time, each with the least fraction of the pixels it
|
||
observed in view, in basis points, and whether the ad was audible, measured over the ad's
|
||
pixels, the player's area (the disclosure the video viewability guidelines ask for) or a
|
||
television's full screen; a count of rendered frames where the platform exposes one; and
|
||
optionally a device attestation.
|
||
- **The session key.** A player makes an Ed25519 key for each playback session of a room and
|
||
signs its claims with it; the session its receipts name is derived from the key and the
|
||
room, so the decider never chooses it and it links nothing across rooms.
|
||
- **The creative digest.** A creative is its media objects in order; its hash, in the pod,
|
||
the receipt and the asset list's URIs, digests their content hashes and durations, so a
|
||
claim's presented objects are checked against the bytes the creative's URI names.
|
||
- **Viewability rules, as their texts state them.** The video rule: at least 50% of the ad's
|
||
pixels for 2 continuous seconds of played, unduplicated content (*Viewable Ad Impression
|
||
Measurement Guidelines (Desktop)*, Version 2.0). The cross-media rule: 100% of the pixels for
|
||
2 continuous seconds, which on a television output may be assumed (*Cross-Media Audience
|
||
Measurement Standards (Phase I Video)* §2.2.1). A pause, stall or skip ends a run. The
|
||
quartile a claim reaches is its contiguous coverage at VAST 4.3 §3.14.1's marks, and must be
|
||
the receipt's `quartiles_reached`.
|
||
- **Device attestation.** A Privacy Pass token of RFC 9577 §2.2.1 whose TokenChallenge has as
|
||
its redemption context a digest of the impression and the session key, so one token answers
|
||
one impression; type 0x0002 is verified from the issuer's key as RFC 9578 §6.4 does
|
||
(RSASSA-PSS with SHA-384), type 0x0001 by its issuer. A token attests the device an attester
|
||
vouched for, not the rendering.
|
||
- **The billing rule.** An ad policy of version 2 (`WAP1`) names a viewability rule and can
|
||
require a claim for each impression, an audible run and an attestation. Each claim then gets
|
||
a verdict (reads, signed by the key its session is derived from, the impression's first,
|
||
the creative's own objects, the receipt's quartile, viewable, attested when required), and
|
||
the room verifier's spend-equivalence (live-ads §9.3 step 12) holds the **presented spend**,
|
||
the prices of the impressions whose claim verifies, to the decider grant's ceiling. A
|
||
stripped, redirected or forged beacon counts nothing. The text states plainly what a claim
|
||
cannot prove without attestation (a modified player, or a party making session keys of its
|
||
own, can state anything) and what attestation bounds and does not (a vouched device running
|
||
modified software still signs).
|
||
- **live-ads** takes the version-2 policy, the derived session and the spend that counts; a
|
||
policy of version 1 and every existing receipt verify as before, and `live-conformance/ads/`
|
||
is unchanged.
|
||
- **Reference and corpus.** `wai-rs` feature `live_ads` (`src/live_ads/presentation.rs` and
|
||
`presentation_corpus.rs`), which now takes `num-bigint` (already `oer`'s) for the RSA
|
||
arithmetic. `live-conformance/presentation/`, written from fixed seeds by
|
||
`wai_ad_presentation_vectors`, its issuer key RFC 9578 Appendix A.2's test key and its token
|
||
checked first: good impressions under each rule, attested ones on television outputs, and a
|
||
claim for each reason (too few pixels, a broken run, muted, a duplicate, a key that does not
|
||
name the session, an object not the creative's, a misstated quartile, no token, another
|
||
impression's token, an altered authenticator, a bad signature, a malformed claim, a session
|
||
with no decision, and an impression with no claim), with a room whose receipts pass the
|
||
ceiling only if every billed impression counted. Checked by `tests/ad_presentation.rs` and by
|
||
`live-conformance/ad_presentation_verify.py`, a second implementation from the text with its
|
||
own RSASSA-PSS, which requires every mutated copy to fail and catches each of its own checks
|
||
dropped.
|
||
- **A reference producer** in the browser client, `joule-moq-web/web/ads/presentation.js`: a
|
||
span recorder over a video element (an `IntersectionObserver`, the page's visibility and
|
||
focus, volume, playback position and frame counts) and the claim's writer and signer on
|
||
WebCrypto's Ed25519. Its tests turn observation sequences into the corpus's claims, byte for
|
||
byte. No page of the client plays ads yet.
|
||
|
||
### `wai.access.services`: caption quality anyone can recompute, audio description and sign language
|
||
|
||
- **The extension** ([access-services](extensions/access-services.md)) registers the record
|
||
`wai.access.services`. Captions are timed-text's, unchanged; its records are JSON texts in
|
||
the JCS form, each signed with Ed25519 under `wai:access` and named by its SHA-256.
|
||
- **Caption quality from the signed revisions.** A caption track's **latency** is each cue's
|
||
first display on the room clock less the capture of its first word: a sources record places
|
||
the words on a take, mapped to the room clock by the take's opening commitment (the take is
|
||
checked to derive from a `record_start` to the commitment's author), or on the room clock as
|
||
its signer states, and the class (`capture`, `stated`, `none`) says which. Its **stability**
|
||
is the revisions after first display and the words they changed; its **accuracy**, the word
|
||
errors of the first revision shown against a reference transcript or the reviewed final
|
||
revision, by a prefix alignment that does not count the words a partial has not reached.
|
||
Words fold ASCII case and edge punctuation, and the alignment walks back by one fixed rule,
|
||
so substitutions, deletions and insertions are the same everywhere. A quality record states
|
||
the figure and names its inputs by digest; a verifier recomputes it (`figure_mismatch`,
|
||
`figure_inputs_mismatch`). §9 maps each figure onto 47 CFR 79.1(j) and (k)(2), the two UK
|
||
regulator statements on live subtitling (2013, 2024) and EBU Tech 3370's authoring delay,
|
||
and says what is not computed (severity grading, speed, placement).
|
||
- **Audio description.** A description record holds the script (each event's interval, what
|
||
of the image it describes, its text), the ducking of the programme audio under each event,
|
||
and the voicing, on a separate track or by the sink with a pinned model, the script and the
|
||
voice each labelled human, machine or machine-reviewed. A voice must start within 35 ms
|
||
early and 45 ms late of the end of the duck and end no later than 45 ms after the duck
|
||
starts to rise, DAPT §5.5's bounds (`description_out_of_sync`). The record maps onto a DAPT
|
||
document (W3C Candidate Recommendation Draft of 2026-06-26), written by hand, with the
|
||
ducking as Mixing Instructions on each Script Event and the voice as an Audio Recording or
|
||
Synthesized Audio. A claim reports how much of each description is spoken over captioned
|
||
dialogue, a figure, not a refusal.
|
||
- **Sign language.** A signing record binds each object of an avatar pose stream, or of the
|
||
video of a signer, to the final caption revisions it translates, by digest
|
||
(`sign_revision_unknown`, `sign_revision_not_final`); a review signs off the translation.
|
||
Its figures are each segment's delay behind the speech and, for video, whether it meets the
|
||
resolution and frame rate EN 301 549 V4.1.1 clauses 6.5.2 and 6.5.3 state.
|
||
- **Completeness.** A claim declares a session's access tracks; a verifier checks each is
|
||
present, finished (every cue resolved, every description voiced, every cue signed), in its
|
||
language and within the tolerances the claim states, its reviews held and not rejected.
|
||
- **Reference and corpus.** The `access_services` module of `wai-rs` (feature
|
||
`access_services`) and `live-conformance/access/`, written from fixed seeds by
|
||
`wai_access_services_vectors`: a lesson with human and machine captions, a take, a
|
||
reference, quality records, a voiced description, a motion-captured signer and a claim; a
|
||
reviewed session with a sink-voiced description and a signer's video; a session with no
|
||
sources; and a negative control for every reason of §10, among them a misstated latency, a
|
||
broken revision chain, a signing bound to a superseded revision, descriptions spoken early
|
||
and late, and a claim over a track the verifier does not hold. Its second implementation,
|
||
`live-conformance/access_services_verify.py`, gives every case the same outcome and figures
|
||
and every DAPT document the same bytes, and every mutated copy of the corpus fails.
|
||
- **timed-text** §1 now points audio description, which it leaves out of scope, to this
|
||
extension.
|
||
|
||
### `wai.bearer.receipts`: one receipt on every bearer
|
||
|
||
- **The defect.** unidirectional-delivery §4 required a receipt for objects sent over a bearer
|
||
with no return path to "declare the bearer as unidirectional", and said a sink's later
|
||
reconciliation is a `claim` "under a class registered for it in jwp-receipts §2.4.4, which none
|
||
yet is". Neither had a mechanism: no field, class or vector. Both are now built, and §4 is
|
||
rewritten so its MUST is met and checked.
|
||
- **The extension** ([bearer-receipts](extensions/bearer-receipts.md)) registers the record
|
||
`wai.bearer.receipts`. The group and object receipts stay byte for byte what they were, on every
|
||
bearer; the corpus carries one group on all three bearer classes and checks that its bytes and
|
||
its object verdicts are the same. The bearer is declared beside the receipt, so one group
|
||
carried on several bearers has one receipt and a declaration for each, and no receipt already
|
||
issued changes.
|
||
- **Two claim classes in jwp-receipts.** `transmission` (§2.4.14): the publisher's declaration,
|
||
signed by the group's key and naming the group by its link hash, of the bearer's class
|
||
(`bidirectional`, `multicast`, `unidirectional`), the instance, and the transmission's energy
|
||
with its acquisition class or `null`; `attests` is `transmitted`, and anything else is refused
|
||
(`transmission_not_transmitted`). `reconciliation` (§2.4.15): a sink's later statement of the
|
||
objects it received, a bitmap and a root over their leaves under the group's own tree, checked
|
||
against the publisher's root without the objects (`reconciliation_root_mismatch`), refused from
|
||
the publisher's key (`reconciliation_by_publisher`) and over a leaf count or a group not the
|
||
group's; `attests` is `received`. Both are checked against the group at §3.1 step 6, and over
|
||
HTTP against the group of an index item (http-delivery §8.2). Their authorities `transmission`
|
||
and `reconciliation` are registered in §7.5; a transmission's energy figure is an item of its
|
||
own, as a session's is.
|
||
- **The verdict over a group** (bearer-receipts §6): `published`, `transmitted` or `reconciled`,
|
||
never delivered on the publisher's word. Declarations of one instance that differ are set aside
|
||
(`bearer_contradicted`); a reconciliation over an undeclared or contradicted instance is counted
|
||
nowhere (`bearer_not_declared`, `bearer_contradicted`); a key is counted once per instance, its
|
||
later reconciliations joined (`reconciliation_duplicate_sink`); a relying party may count only
|
||
keys it confirmed (`sink_not_confirmed`). A statement that a group was delivered to *k* sinks
|
||
holds only when *k* keys reconciled every object (`delivery_not_reconciled`,
|
||
`delivery_exceeds_reconciled`). A sink reports whether the instance it received over was
|
||
declared.
|
||
- **Energy per receiver** (§7): an upper bound, ⌈joules / max(1, distinct keys)⌉ at the
|
||
transmission figure's class, tightening as reconciliations arrive; never a measurement, never
|
||
0, none for an unmetered transmission, and stated as a count of keys.
|
||
- **No return path** (§8): a sink confirms under anchors provisioned in advance; a key log that
|
||
reaches it over the bearer is held, so a revocation sent over the bearer takes effect, and
|
||
anchors nothing (`anchor_on_bearer`), so a log forged on the bearer confirms nothing.
|
||
- **Bearer mappings** (§9), normative for the bytes WAI controls (envelopes whole, receipts and
|
||
claims as http-delivery's receipt files, key logs at their path) and informative for FLUTE
|
||
(RFC 6726), ETSI TS 103 769 V1.2.1, ETSI TS 103 770 V1.2.1, ATSC A/331:2026-04 with
|
||
A/333:2026-04 and A/360:2026-08, 3GPP TS 26.346 V19.3.0 and TS 26.517 V19.3.0,
|
||
`draft-ramadan-moq-multicast-00`, and ETSI TS 102 796 V1.8.1, each at the edition read.
|
||
- **Reference implementation and corpus.** `wai-rs` feature `bearer_receipts` (module
|
||
`bearer_receipts`; the bodies in `claim_receipt::bearer`); corpus
|
||
[`live-conformance/bearers/`](live-conformance/bearers/), regenerated by
|
||
`wai_bearer_vectors gen`; second implementation `live-conformance/bearer_verify.py`, written
|
||
from the text, which also mutates the corpus and drops each of its own checks in turn and
|
||
requires each to be caught; `claim-conformance/claim_verify.py bearer` reads the corpus's
|
||
claims through jwp-receipts §3.1 steps 1–4.
|
||
|
||
### `wai.media.direction`: a director's storyboard, keyframes and sequence, bound to the video and checked
|
||
|
||
- **The extension** ([direction](extensions/direction.md)) registers the record
|
||
`wai.media.direction`. A director, a person by an Ed25519 key, signs records at four levels of
|
||
control, and each record states the levels it sets: a **storyboard** (`wai-storyboard`): the
|
||
shots, each with its duration, its subjects by reference picture (one view per angle) and the
|
||
framing it opens on, and optionally their order; **keyframes** (`wai-keyframes`): at stated
|
||
frames of a shot, each subject's box on screen, actor relative, its size class, its screen
|
||
position and its angle, and a camera window over the shot's plate, with `hold`, `linear` or
|
||
`ease` interpolation between keys pinned in integers, and regions of frames held to a reference
|
||
picture; and a **sequence** (`wai-sequence`): which shots, which frames of each, in which order,
|
||
joined by cuts or dissolves.
|
||
- **The directed output** (`wai-directed`), signed by the tool that made the video, names the
|
||
records and its directors, the layout of every frame of every shot as made (plate, camera
|
||
window, each subject's view and box, the regions held), the timeline as made and the output's
|
||
frames by digest.
|
||
- **Exact where the render is.** Under `replicate` the frames are the pinned integer render of
|
||
the extension over layers that `wai.generate.spectral` regenerates or the verifier holds: the plate through
|
||
the camera window by the nearest sample, each subject's view scaled to its box, the held regions,
|
||
and an integer blend in a dissolve. Every frame recomputes, so the checks are exact: each cut on
|
||
its directed frame (`cut_mismatch`), each duration (`duration_mismatch`) and the order
|
||
(`order_mismatch`), each transition (`transition_mismatch`), each subject where its keyframes
|
||
and their interpolation put it and its size, position and angle classes under the pinned
|
||
framing measure (`framing_mismatch`), each held region (`region_mismatch`). Under `attest`, a
|
||
float generator's frames, the same checks run on the tool's stated layout and the outcome is
|
||
attested with the class of what stated it; a float generator's picture claimed exact is
|
||
refused (`check_mismatch`).
|
||
- **Governance and authorship evidence.** Every element governs the output frames that show what
|
||
it directed, and the directed output states them; a claim of frames an element does not govern
|
||
is refused (`governance_mismatch`). An accepted case gives a per-frame map of who directed each
|
||
frame at each level, beside what made its samples. Each element maps onto a `direct` step of the
|
||
contribution record, with its frames, and onto a C2PA 2.4 `c2pa.edited` action whose changes are
|
||
frame ranges (and the rects of a held region), one actions assertion per director; and, in an
|
||
informative section, onto OpenTimelineIO's serialized schemas. WAI decides no legal question.
|
||
- **The corpus** ([`live-conformance/direction/`](live-conformance/direction/)), written by
|
||
`wai_direction_vectors gen` from fixed seeds, holds a storyboard of three shots and two subjects,
|
||
a cinematographer's keyframes (a camera easing across a plate, a push in from medium to close)
|
||
with a held region of a drawn reference, and an editor's sequence that reorders, trims and
|
||
dissolves, at 64 by 36 pixels so every frame recomputes; the storyboard fixing its own order; an
|
||
order left to the tool; the same direction attested; and a control of one defect for each reason,
|
||
among them a cut a frame early, a box a pixel off its track, a size class the box does not
|
||
measure, a record signed by a non-party, a swapped order, a float plate claimed exact and a
|
||
governance entry that claims every frame. Its second implementation,
|
||
`live-conformance/direction_verify.py`, written from the text and sharing no code with `wai-rs`,
|
||
runs the generator in its own arithmetic after checking it against its conformance vector,
|
||
renders every frame and writes every statement itself, requires every mutated copy of the corpus
|
||
to fail, and drops each of its own checks in turn and requires the corpus to catch the weakened
|
||
verifier.
|
||
|
||
### `wai.media.influence`: who conditioned an output, and their share of what it earns
|
||
|
||
- **The extension** ([influence](extensions/influence.md)) registers the record
|
||
`wai.media.influence`. An owner, any key (a person, a collective, a label), publishes a work with
|
||
signed **terms** (`wai-terms`): its `wai.ai.usage` statement, the acts of generation it allows, a
|
||
per-use price or a revenue share, the tier a use of it must reach, and when the terms are in
|
||
force; a **catalogue** (`wai-catalogue`) is a signed set of works one set of terms governs. Terms
|
||
and catalogues are their owner's only by its signature (`not_signed_by_owner`). A work is a style
|
||
or reference image, a voice model, an adapter or another prior, a taste prior or a catalogue, named
|
||
by digest; an owner's prior is published unchanged, pinned as SPEC §3.1 pins any parameter set.
|
||
- **Conditioning.** A generation, candidates or an inpainting of a contribution session names every
|
||
work that conditioned it in a **conditioning** record (`wai-conditioning`) beside the step, bound
|
||
to it by its link hash: contribution reads every record with exactly its members, so a member
|
||
added to the step would make every contribution verifier refuse the session. Each reference
|
||
names its work, the owner's terms in force, its weight and its use, checked against the terms
|
||
(`terms_mismatch`, `not_in_catalogue`, `terms_not_in_force`, and ai-usage's `usage_unanswered`
|
||
and `usage_disallowed`). The pinned conditioned generator, `wai-influence:spectral-blend`,
|
||
blends `wai.generate.spectral`'s picture with every reference image (tiled) and adapter (a
|
||
second model file) by weight in integers, so a verifier regenerates the step from the works it
|
||
names: a work left out, added or reweighted fails (`conditioning_mismatch`), and a reference
|
||
named for a step of the exact generator, which reads none, is refused too.
|
||
- **Attribution.** Every sample of the output has the steps that made it (contribution §7.2's rules
|
||
with the step in place of its performer), and the declared rule `wai-influence:share/1` divides
|
||
the output, 10^6 units a sample, among the people who made samples, the owners of the works that
|
||
conditioned the model by their weights (equal shares, the model counted as one, where none is
|
||
declared), the model's owner, works no one owns, and `unknown` and `undisclosed`, which are
|
||
never credited (`unknown_attributed`). The tool's **attribution** (`wai-attribution`) states the
|
||
shares, which sum to the whole (`shares_not_whole`) and which a verifier recomputes
|
||
(`shares_mismatch`) whenever every step behind the output is disclosed; a training-time estimate
|
||
is stated beside them with its estimator's class and never added (`estimate_mixed`). Each use has
|
||
a tier, `recomputed` where the generation was regenerated with its works, and an owner's terms set
|
||
the lowest it accepts (`attribution_not_met`).
|
||
- **Payment.** A settlement statement's line kind 2, reserved until now, is the **attribution
|
||
line** ([settlement](extensions/settlement.md) §7.8): per owner, object class and basis, the
|
||
output's acknowledged deliveries and bytes and `per_use` × deliveries + ⌊revenue × share ×
|
||
units / (10^6 × whole)⌋, rounded down once per bucket, the remainder staying with the party; the
|
||
statement's `amount` and `classes` remain the delivery lines'. A verifier recomputes every line
|
||
from the bases it verified: a disallowed use paid, a tariff in another currency
|
||
(`currency_mismatch`), an owner left out, and an output's deliveries settled with no line for its
|
||
owners (`attribution_unsettled`) are refused. A statement verified by settlement alone refuses an
|
||
attribution line whose basis it is not given (`record_missing`), and the settlement corpus's
|
||
reserved-line vector now says so.
|
||
- **Transparency report** (`wai-influence-report`): per owner and statement, the outputs its works
|
||
had units in, its buckets, the deliveries, the revenue and the amounts, signed by the statement's
|
||
party. The owner recomputes every amount from its own terms; an auditor holding the statement
|
||
checks that the report lists exactly the owner's lines (`report_incomplete`, `report_mismatch`).
|
||
It names no other owner.
|
||
- **Reference and corpus.** `wai-rs` module `influence` (feature `influence`), the corpus
|
||
`live-conformance/influence/` written by `wai_influence_vectors gen`, and its second
|
||
implementation `live-conformance/influence_verify.py`, which verifies each session with
|
||
`contribution_verify.py`, regenerates every conditioned step itself and reads statements with
|
||
`settlement_verify.py`, now amended to read attribution lines.
|
||
|
||
### `wai.live.coperformance` in the live room: the clock signs, the relay refuses, the page performs
|
||
|
||
- **The room clock signs what only it knows** (`joule-moq-room`, `[co]`, co-performance §8). It
|
||
records each audience member joining when its events track appears, follows every member's
|
||
events and each publisher's media, and holds the room as a verifier does. It logs each object
|
||
it takes with its arrival and its due instant (the object's timestamp, which a page states on
|
||
the room clock) and serves the log (`GET /co/objects`), signs a mark for each object out of
|
||
turn under a schedule that marks or later than its publisher's budget, commits to each window
|
||
once it has closed and the inputs in flight have arrived and signs its selection under its
|
||
rule with its credits (a draw from a seed it drew when the window opened), and states each
|
||
participant's fidelity, every `fidelity_every_s` and when a lead asks. A page asks it for a
|
||
turn schedule, a handover or a window over HTTP, its grant presented with a proof of
|
||
possession over a challenge the service issued (live-rooms §10.5.3), and its powers decide:
|
||
the room's first publisher holds the direct power (`lead`), the turn's holder may hand its
|
||
turn over. A publisher may ask for a grant that marks it an AI participant.
|
||
- **What the clock signs is what the verifier recomputes.** `wai-rs`'s `co_performance::replay`
|
||
replays a room through §9.2 step 4, and a clock reads from it the commitment, the selection,
|
||
the fidelity statement and the marks the verifier will recompute (`Replay::commitment`,
|
||
`selection`, `fidelity`, `marks_for`) and the floor (`FloorBook`). `tests/co_performance.rs`
|
||
recomputes every commitment, selection and fidelity statement of the corpus's accepted rooms
|
||
from the room as it stood just before each, byte for byte, and every object's marks.
|
||
- **The relay refuses invalid audience inputs** (§7.3): without the audience-input power of the
|
||
grant its author's session proved, to a window it does not hold or one already committed, of
|
||
a type the window does not take, past its author's power or its policy's rate, that does not
|
||
read, or whose signature fails. Each is withheld from every object that carries it and counted
|
||
by reason in the session's summary (`co_refused`), as is each object held back out of turn
|
||
(`refused_out_of_turn`). A grant head (`joule-moq`) now reads a grant's attributes and its
|
||
policy's rate window.
|
||
- **The room page performs** (`joule-moq-web/web/room/co.js`). It shows the turn schedule and
|
||
who holds the floor, each participant's intents as upcoming lines and what became of them, the
|
||
window open now with an input box for a viewer whose grant allows it, each selection with its
|
||
credits, and the clock's fidelity statement; a lead's controls ask for turns, a handover, a
|
||
vote, prompts and a fidelity statement. A performer's page sends media only while it holds the
|
||
floor, a guard inside each turn, and states its media's timestamps on the room clock. With
|
||
`ai=scripted` the page is a demo AI participant: a drawn picture and a tone, and a scripted
|
||
line announced ahead of each it says. `wai-live-web` reads every statement, signs a page's
|
||
intents and inputs, and verifies the room as a page holds it (`Room.coVerify`;
|
||
`wai_live_web co-verify` on the host).
|
||
- **The extension's text** now says what a relay does with an input naming a committed window,
|
||
what it counts, how a room clock that is a session of its own on the relay takes arrivals and
|
||
signs marks, when it commits and states fidelity, how a room key states a statement a
|
||
participant asks for, and that a performer states an object's due instant as its timestamp
|
||
on the room clock.
|
||
|
||
### `wai.live.coperformance`: intents, turns and audience co-creation in rooms
|
||
|
||
- **The extension** ([co-performance](extensions/co-performance.md)) registers the record
|
||
`wai.live.coperformance`. The room authority signs a co-performance policy, `WPP1`, chained
|
||
by the instant it takes effect; every other statement is a `meta` payload of
|
||
[live-events](extensions/live-events.md), signed by its author in its chain, and the
|
||
descriptor's and the grant's layouts are unchanged.
|
||
- **Intents.** A participant announces, ahead of the instant it plays and for a horizon the
|
||
policy bounds, the material it means to play: an object by the digest of its payload, notes,
|
||
a text line, or a camera move (`WPI1`). A verifier finds what played (the object nearest the
|
||
planned instant on the announced track, or the author's chat or caption) and compares it
|
||
under a pinned rule: digests equal, notes with their pitches equal and their onsets and
|
||
durations within the policy's tolerance, text equal byte for byte, a camera's pan and tilt
|
||
within the angle tolerance and its zoom equal. Each intent is kept, diverged or missed, and
|
||
a room key's fidelity statement (`WPF1`) states per participant how many of each, which the
|
||
verifier recomputes as of the statement's instant (`fidelity_mismatch`).
|
||
- **Turns.** A turn schedule (`WPT1`) gives the floor in turns of microseconds or of bars at a
|
||
stated tempo, its holders taking turns in order, solo or as call-and-response pairs, and a
|
||
handover (`WPH1`) passes a turn from an instant. The schedule states its mode: under refuse
|
||
the relay does not forward an object published out of turn (`turn_violated`); under mark the
|
||
relay forwards it and the room clock signs a mark (`WPM1`, `turn_unmarked`). A grant's
|
||
`latency_budget_ms` bounds how late a participant's object may arrive, and a late object is
|
||
marked before any mix (`late_unflagged`); a mark the room's records do not support is
|
||
`mark_unfounded`.
|
||
- **Audience co-creation.** A grant's `audience_input` attribute is the audience-input power
|
||
and its limit per window; a member's input (`WPA1`) is a prompt, a sketch or a movement sample
|
||
by digest, or a vote, sent to a window (`WPW1`) that states its rule and its close. Once the
|
||
window closes, the clock commits to the eligible inputs (`WPK1`): the inputs sent before the
|
||
close and not restricted by a moderator's decision. A selection (`WPS1`) recomputes from the
|
||
commitment under a plurality vote, the top inputs by reactions, or a draw from a seed the
|
||
window committed to before any input; a pick sent after the close is `input_late`, and one
|
||
the rule does not give `selection_mismatch`. Every pick carries a credit: the member who sent
|
||
it, as an author of material, or the voters of an option together, as directors; each maps
|
||
onto a party and a step of a contribution record, a slot this revision names and writes
|
||
nothing into.
|
||
- **Reference and corpus.** `wai-rs` feature `co_performance` (`src/co_performance.rs`).
|
||
`live-conformance/coperformance/vectors.json`, written from fixed seeds by
|
||
`wai_co_performance_vectors`: a jam room in which a person and an AI participant trade
|
||
four-bar phrases, an audience room with a window of each rule, the audience room with a gap,
|
||
and a refused room for each reason of §10. Checked by `tests/co_performance.rs` and by
|
||
`live-conformance/co_performance_verify.py`, a second implementation that shares no code with
|
||
`wai-rs` and that drops each of its own checks in turn to show the corpus catches it.
|
||
|
||
### `wai.media.takedown`: takedown across every rendition and cache, against the regime's clock
|
||
|
||
- **The extension** ([takedown](extensions/takedown.md)) registers the record
|
||
`wai.media.takedown`. A service signs a **takedown** (`wai-takedown`) of a source (an object
|
||
by its content hash, a group, a track or a take) in answer to a signed **notice** or order
|
||
(`wai-takedown-notice`), or of its own initiative, under a **policy**
|
||
(`wai-takedown-policy`) that names its stores and the clocks it runs under.
|
||
- **Regime clocks are data.** A registry (`wai-takedown-clocks`) holds each clock by
|
||
identifier, type (`deadline`, `reply_window`, `unfixed`), duration and start, citing its
|
||
instrument and provision; a policy pins it by link hash. WAI publishes one at
|
||
[`registry/takedown-clocks.json`](registry/takedown-clocks.json): Public Law 119-12, section
|
||
3(a)(3); the Information Technology (Intermediary Guidelines and Digital Media Ethics Code)
|
||
Rules, 2021, rules 3(1)(d) and 3(2)(b), as amended by G.S.R. 120(E); Regulation (EU)
|
||
2024/1083, Article 18(4); and Regulation (EU) 2022/2065, Article 16(6).
|
||
- **The derivation closure**, computed exactly from deriving-edge records and synthesis origin
|
||
claims, transitively, with each group it reaches expanded from its delivery receipts
|
||
(`group_unexpanded` otherwise). A takedown's stated set that leaves a derivative out is
|
||
`closure_incomplete`; one that claims what no record derives is `closure_unsupported`; a
|
||
perceptual match (`wai-takedown-match`) listed as exact is `match_as_exact`. Copies made
|
||
outside every WAI derivation are out of reach, and the text says so.
|
||
- **Purges and the clock.** Each holder (a store of the policy, an edge that derived from a
|
||
covered object, a party that claimed to, a publisher of a covered group, and a party that a
|
||
`wai.delivery.settlement` acknowledgement, signed by its receiver, shows served it or, as a
|
||
hop, received it) signs a purge (`wai-takedown-purge`). The verifier computes the time to
|
||
removal from the clock's start to the last purge required: `purge_missing` past the deadline,
|
||
`deadline_missed`, and `window_not_observed` for a removal inside a reply window. A purge by a
|
||
party that never held what it names counts for nothing.
|
||
- **Counts** (`wai-takedown-counts`), per clock, ground, category and source, with the lower
|
||
median time to removal, recompute from the cases (`counts_mismatch`); a service gives each
|
||
takedown's statement-of-reasons record its link hash as `puid`.
|
||
- **Live moderation** is unchanged: its records are versioned, and a takedown beyond a room is a
|
||
sibling record.
|
||
- **The reference relay's DVR store** (`joule-moq-relay`) honours a signed takedown on the track
|
||
`[key, takedown]`: it forgets every covered object and track in every room, never stores an
|
||
identical copy again, withholds them from every FETCH, keeps its takedowns across a restart,
|
||
and signs a purge with the relay's key. It does not check the live fan-out.
|
||
- `wai-rs` module `takedown` (feature `takedown`); corpus
|
||
[`live-conformance/takedown/`](live-conformance/takedown/) (`wai_takedown_vectors`), and its
|
||
second implementation, `live-conformance/takedown_verify.py`.
|
||
|
||
### The browser sink: routes before decode, and the frame on screen bound to its receipt
|
||
|
||
- **The extension** ([browser-sink](extensions/browser-sink.md)) says how a sink in a web page
|
||
answers SPEC §4's "the sink advertises `cap`". For each object it builds a capability report
|
||
and routes the object to the user agent's decoder, a native host's or WAI's own WebAssembly
|
||
decoder. The user agent is asked only about codec strings the WebCodecs Codec Registry
|
||
(Registry Draft, 24 September 2026) lists: a capability with no such string (`wai.video.av2`,
|
||
`wai.video.vvc`, `wai.audio.iamf`) is never put to `isConfigSupported`, and an answer about
|
||
one is never read. The user agent's decoder is taken only where it is exact for the tier the
|
||
object needs: a need of `decode-equivalence` takes it only after a probe matches published
|
||
frame digests, and otherwise falls back to wasm. Each step's refusal has a reason code.
|
||
- **Constraint sets in the page.** [capability-constraints](extensions/capability-constraints.md)
|
||
gains its register (§3.1, the `wai:` parameters) and its evaluation, in browser-sink §5:
|
||
enumerations, minima and maxima ORed over sets, in integers.
|
||
- **Energy beside `powerEfficient`.** A measured figure per decode is reported beside Media
|
||
Capabilities' boolean, with its acquisition class ([energy-measurement](extensions/energy-measurement.md)
|
||
§2) and coverage; the boolean stays the user agent's word, and a decode with no measurement is
|
||
unmetered, never 0 J.
|
||
- **`contentBinding`.** Every frame a page decodes carries the content hash of the receipted
|
||
object it was decoded from ([jwp-receipts](extensions/jwp-receipts.md) §2.1), the link hash of
|
||
the group receipt that object verified under, and the tier its samples hold. The binding
|
||
survives `clone()`, serialization and transfer, gives nothing to the bytes `copyTo()` writes,
|
||
and does not survive re-encode. Until the WebCodecs VideoFrame Metadata Registry lists the
|
||
member, a page keeps it in a side table keyed by decoder and timestamp, and refuses a
|
||
timestamp given twice. The state of the frame on screen joins the binding to the receipts and
|
||
to [live-provenance](extensions/live-provenance.md)'s verdict. A draft registry entry and
|
||
issue text are in [`proposals/webcodecs-content-binding.md`](proposals/webcodecs-content-binding.md),
|
||
not filed. `contentBinding` is a wire identifier of the new kind `frame-metadata`, status
|
||
`proposed`.
|
||
- **Reference and conformance.** `wai-rs`'s `browser_sink` module (feature `browser_sink`) and
|
||
the corpus [`live-conformance/browser/`](live-conformance/browser/) with its second
|
||
implementation, `browser_sink_verify.py`. `wai-live-web` builds the same functions for the
|
||
page, and with its feature `int_video` a wasm decoder that verifies a `wai.video.int_motion`
|
||
object against its receipts and binds every frame. The room client routes each track before it
|
||
decodes it, binds every frame its decoders output, and shows each tile's state with
|
||
`?debug=binding`; its end-to-end scenario `binding` runs all of it in a browser.
|
||
|
||
### `moq-streaming-format`: the MOQT drafts the reference speaks
|
||
|
||
- **§6** says that nothing in the extension depends on the MOQT draft carrying the objects,
|
||
and that the reference implementation speaks `draft-ietf-moq-transport-22`, `-18` and
|
||
`-16`, chosen per session, and carries the extension's envelopes, `WAI_RECEIPT` properties
|
||
and receipts tracks unchanged on each. `joule-moq`'s README records the relays and drafts
|
||
they have been carried through, and the date.
|
||
|
||
### `wai.delivery.settlement`: delivery paid on what the party that received it signed
|
||
|
||
- **The extension** ([settlement](extensions/settlement.md)) registers the record
|
||
`wai.delivery.settlement`. The downstream party of a delivery, a next hop or a sink, signs an
|
||
**acknowledgement**, `WDA1`, of what it received in a window: per object class (`live`, `fetch`,
|
||
`fill`, `ancillary`), the objects by content hash under jwp-receipts §2.2's tree, the byte range
|
||
of each and their byte count, the item lists travelling beside the record under a digest it
|
||
signs. A party's acknowledgements to one upstream form a chain of windows that do not overlap.
|
||
A relay acknowledges upstream and is acknowledged downstream over the same content hashes, and
|
||
a hop's billable bytes are exactly those its downstream parties signed for. An acknowledgement
|
||
whose downstream is its upstream is refused (`self_acknowledged`).
|
||
- **Unacknowledged delivery.** A hop whose downstream will not or cannot sign states the delivery
|
||
in a **declaration**, the same record signed by itself, counted in a class of its own: never as
|
||
zero, never billed, and never for a byte the same party acknowledged
|
||
(`declared_and_acknowledged`). A unidirectional bearer settles under unidirectional-delivery §4.
|
||
- **Byte-range proofs**, `WBR1`: the covered chunks of a range and the chaining value of every
|
||
subtree of BLAKE3's chunk tree outside them, verified to the object's content hash; the head
|
||
proof of http-delivery §6 is the case of a range that starts at 0. A proof establishes the
|
||
object's length only when it reaches the last chunk, and its verifier says whether it did. An
|
||
auditor asks a downstream party for one over an item it acknowledged.
|
||
- **Paid retrieval.** A fetch below the live edge, a recording fetch, or a fill past a cache is
|
||
billed only to a room grant whose `WRP1` proof of possession the request carried, checked by
|
||
live-rooms §10.5.4 steps 2 to 5 and the seek power; one proof pays for one record
|
||
(`pop_replayed`); an ungranted retrieval is free (`paid_without_grant`).
|
||
- **The settlement statement**, `WSS1`, per party and period: lines by object class and payer,
|
||
the acknowledged bytes by class, the declared bytes, the party's joules by acquisition class
|
||
from its per-hop records (`WMH1`) and never one total across classes (`classes_summed`), and the
|
||
amount under a declared tariff, in millionths of a minor unit per 10^9 bytes, rounded down once
|
||
per line. A verifier recomputes every figure from the acknowledgements: a line billing bytes no
|
||
acknowledgement covers is `bytes_not_acknowledged`, any other difference `statement_mismatch`.
|
||
A line kind is reserved for a payee that is not a hop.
|
||
- **Logs.** Each acknowledged item of an HTTP delivery maps onto a `cdni_http_request_v1` record
|
||
(RFC 7937 §3.4.1), the acknowledgement's digest in a `remark` directive; a log carries no
|
||
counter-signature, so a converter maps a log onto declarations, never onto acknowledgements.
|
||
- **Viewers as relays over ciphertext.** An acknowledgement binds a sealed object's content hash,
|
||
over its sealed bytes, so a forwarder that cannot open what it forwards earns acknowledged bytes,
|
||
and proves ranges of them, on the terms of any relay.
|
||
- **Reference and corpus.** `wai-rs` module `settlement` (feature `settlement`), its generator
|
||
`wai_settlement_vectors` and `tests/settlement.rs`; the corpus
|
||
[`live-conformance/settlement/`](live-conformance/settlement/), 48 statements and 24 range
|
||
proofs; and its second implementation, `live-conformance/settlement_verify.py`, written from the
|
||
text, which requires every mutated copy of the corpus to fail and every one of its own checks
|
||
to be load-bearing.
|
||
|
||
### `wai.media.contribution`: who contributed what, computed from the process
|
||
|
||
- **The extension** ([contribution](extensions/contribution.md)) registers the record
|
||
`wai.media.contribution`. A tool signs a **session** naming its parties: people by Ed25519 key
|
||
(`human`, `audience`), each countersigning its own membership (`wai-membership`), and models
|
||
by digest. A person cannot be enrolled as a maker or a director of any sample without that
|
||
signature (`party_not_countersigned`).
|
||
- **Steps.** Every act is a signed step (`wai-step`) in a hash chain, signed by the person who
|
||
performed it or by the tool for a model: `author`, `generate`, `candidates`, `select`, `edit`,
|
||
`inpaint`, `transform`, `compose` and `reject`; `direct` is reserved for direction tracks.
|
||
Transforms and compositions evaluate pinned operations by name: deriving-edge's crop,
|
||
rotation, mirror, overlay and grid composition, synthesis's upscale and denoise, and one new
|
||
per-sample colour table, `wai-contribution:lut`. The exact generator is `wai.generate.spectral`.
|
||
- **The authorship map**, computed by re-executing each act's dependency rule: for every pixel
|
||
of the output, `made_by` (whose act produced its value) and `directed_by` (whose prompt, mask,
|
||
selection, parameters or arrangement conditioned it). A mixing filter takes the union of what
|
||
it mixes; a selection adds the selector everywhere; a composition takes each layer where it is
|
||
visible. Encoded with a palette and run-length rows, in one encoding, and bound to the output's
|
||
samples by its digest. Shares are exact sample counts per party for each of the two sets,
|
||
never one blended figure; an estimator's share rides beside them with its class.
|
||
- **Unknown is never zero.** An attested step's unpinned sample mapping leaves its samples
|
||
`unknown`, and a verifier marks samples from steps it does not hold `undisclosed`; neither is
|
||
ever credited to a party, and a stated map that credits an unknown sample is refused
|
||
(`unknown_attributed`).
|
||
- **What a verifier checks**: the chain and every step's inclusion in the record's Merkle root;
|
||
that an edit or an inpainting changed nothing outside its region, byte for byte
|
||
(`outside_region_changed`); that a selection is a member of its committed set
|
||
(`selection_not_in_set`) and that every candidate regenerates from its seed
|
||
(`regeneration_mismatch`); that each transform and composition recomputes
|
||
(`recompute_mismatch`); that the map recomputes (`map_mismatch`); and that the output's origin
|
||
claim is synthesized wherever a model made a sample (`origin_understated`).
|
||
- **Selective disclosure.** Steps are disclosed with inclusion proofs and prompts are salted
|
||
commitments; the outcome's tier says what was reached: `recomputed`, `checked` or `stated`.
|
||
- **Mappings.** One C2PA 2.4 actions statement per session, computed where every step is
|
||
disclosed (`c2pa.created` from an empty canvas, then one action per step behind the output,
|
||
with its software agent, its IPTC digital source type and its changed region); identity
|
||
assertions and live tracks, informative.
|
||
- **WAI decides no legal question.** The record states facts a reader applies its own rule to.
|
||
- **Conformance.** `live-conformance/contribution/`, written from fixed seeds by
|
||
`wai_contribution_vectors gen`, every reason of §11 refused at least once; `tests/contribution.rs`; and the second
|
||
implementation, `live-conformance/contribution_verify.py`, which runs the generator in its own
|
||
arithmetic after checking it against the generator's conformance vector, catches every
|
||
mutated copy of the corpus, and is caught by the corpus with each of its checks dropped in
|
||
turn. Audio maps by sample interval and video maps by frame are not part of this revision.
|
||
|
||
### `wai.ai.usage`: AI usage preferences bound to the bytes, and the agent's consumption claim
|
||
|
||
- **The extension** ([ai-usage](extensions/ai-usage.md)) registers the record `wai.ai.usage`.
|
||
A **usage statement** carries three published vocabularies verbatim, each in its own member:
|
||
`aipref`, a statement of preference of `draft-ietf-aipref-vocab-08` in its Structured Field
|
||
form (`train-ai`, `ai-use`, `search`); `tdmrep`, the TDM Reservation Protocol's
|
||
`tdm-reservation` and `tdm-policy` (Final Community Group Report of 10 May 2024); and `cawg`,
|
||
the entries of a `cawg.training-mining` assertion (Training and Data Mining Assertion 1.1).
|
||
WAI adds no category and maps no term of one onto another. Each member names the revision it
|
||
is read at, and a revision this one does not read is refused.
|
||
- **RFC 9651's Dictionary**, parsed in full: every bare item type, parameters and inner lists,
|
||
with a repeated key keeping its last value. A value that does not parse, which the vocabulary
|
||
would read as unknown throughout, is refused rather than carried.
|
||
- **Bound to the bytes.** The manifest member `ai_usage` puts the statement inside the envelope,
|
||
so the object's receipt signs it with the content: an object whose statement is removed or
|
||
altered no longer hashes to its receipt (`usage_unbound`). A room states its preference in a
|
||
room usage record, `WAU1`, signed by the room authority and chained by the instant it takes
|
||
effect, so a change never reaches what was published before it; the descriptor's layout is
|
||
unchanged. A grant's attribute `ai_usage` carries the terms one principal is granted.
|
||
- **HTTP and MoQ.** An origin writes `draft-ietf-aipref-attach-05`'s `Content-Usage` field and
|
||
the TDMRep fields from the signed statement (http-delivery §5.6); a client compares them by
|
||
meaning, so a respelling is not a conflict and a contradiction is (`header_conflicts`). A MoQ
|
||
catalog states a track's statement as `wai_ai_usage` (moq-streaming-format §4), checked the
|
||
same way (`catalog_conflicts`).
|
||
- **One precedence rule.** A grant's terms decide the uses they name; otherwise every signed
|
||
statement that governs the object, the most restrictive governing, as the vocabulary combines
|
||
statements; unsigned statements count only where nothing is signed. CAWG's `constrained`
|
||
reads as not allowed unless a grant says otherwise.
|
||
- **The consumption claim**: the jwp-receipts claim class `consumption` (§2.4.13 there), signed
|
||
by the agent under its `did:key`. It names the objects by content hash, the term it acted
|
||
under in every vocabulary their statements carry (or `null`, a use outside it), the statements
|
||
it saw and the grant it held. A verifier checks it against the objects, their statements and
|
||
the room, and refuses a use they disallow (`usage_disallowed`); the refusal is of the use, and
|
||
the claim stays its signer's statement.
|
||
- **The gate.** A relay or gateway MAY require claims before it serves an agent: claimed before
|
||
the fetch over HTTP, where an object's content hash is known first (the proposed request field
|
||
`WAI-Usage-Claim`), or within a window of groups over MOQT; and MAY require every use to be
|
||
allowed.
|
||
- **Reference and corpus.** `wai-rs` feature `ai_usage` (`src/ai_usage.rs`, with RFC 9651's
|
||
parser in `src/ai_usage/sf.rs`), and the class's body and rules in
|
||
`src/claim_receipt/consumption.rs`. `live-conformance/usage/`, written from fixed seeds by
|
||
`wai_ai_usage_vectors`: statements, receipted envelopes (a `WAI2` among them) with a stripped
|
||
and an altered statement, a room with chained records and agent grants, responses, catalogs,
|
||
claims accepted and refused at each rule, and gates. Checked by `tests/ai_usage.rs` and by
|
||
`live-conformance/ai_usage_verify.py`, a second implementation that shares no code with
|
||
`wai-rs`, parses Structured Fields, canonical forms, receipts and claims itself, requires every
|
||
mutated copy of the corpus to fail and every weakened copy of itself to be caught;
|
||
`claim-conformance/claim_verify.py` reads the class's steps 1 to 4. The registry lists the
|
||
record, the magic `WAU1`, the catalog field `wai_ai_usage` and the proposed field
|
||
`WAI-Usage-Claim`.
|
||
|
||
### `wai.media.synthesis`: what any piece of media is, in two marking layers, with reviews and labels
|
||
|
||
- **The extension** ([synthesis](extensions/synthesis.md)) registers the record
|
||
`wai.media.synthesis`. An **origin claim** (`wai-origin`) says of one object, group or track
|
||
that it is captured, reconstructed (a codec, neural or not, rebuilding what was captured),
|
||
assistive (an edit that does not substantially alter its input) or synthesized (generated).
|
||
It names its step (`capture`, `convert`, `assist`, `generate`), the models the step ran, the
|
||
oversight of a generation, and each input by its content hash and the link hash of its own
|
||
claim. Its class is the greatest of its step's and its inputs', so it rises along a chain of
|
||
derivations and never falls; a transcode of a generated video is synthesized. A codec is a
|
||
conversion: it names no model and keeps its input's class and source, as energy-binding §6.1
|
||
has a decode.
|
||
- **IPTC and C2PA as functions of the claim.** The digital source type (`digitalCapture`,
|
||
`algorithmicallyEnhanced`, `trainedAlgorithmicMedia`, `compositeWithTrainedAlgorithmicMedia`,
|
||
`compositeSynthetic` and the rest) follows from the step, the inputs, the models and the
|
||
oversight; the C2PA statement is the actions (`c2pa.created` with the source, or
|
||
`c2pa.opened` then `c2pa.converted` or `c2pa.edited`) and one `c2pa.ai-disclosure` per trained
|
||
model, written in JCS. The mapping keeps timed-text's for captions.
|
||
- **Assistive claims checked, not trusted.** Two integer derivations are pinned: a 2× upscale
|
||
with `wai.video.int_motion`'s half-sample rounding, and a 3 × 3 binomial denoise. Evidence
|
||
names the input's and the output's sample digests; a verifier holding the input's decoded
|
||
samples recomputes the output and refuses a mismatch. A float or unpinned step states
|
||
`attest` and is reported as asserted; a policy can require proof.
|
||
- **Two marking layers.** The signed claim is the first; the second is a watermark whose 8-byte
|
||
payload is the claim's class and the head of its link hash. A marking record names the scheme
|
||
and the payload embedded, a detection record what a detector read, and a verifier refuses a
|
||
detection that disagrees with the claim or finds no mark in a marked object, and, under a
|
||
policy that asks for both layers, a synthesized object with no marking. No watermark scheme is
|
||
registered: the scheme is a slot a later revision fills.
|
||
- **AI participants in rooms.** live-rooms §4.4's grant attribute `ai` names the model that acts
|
||
for a participant; its chat, reactions and captions state origin 2 or 3 with that model, and
|
||
the room verifier refuses one that does not (`ai_unmarked`, live-rooms §9.2 step 6). Chat and
|
||
reaction events of live-events gain an optional `origin` and `model` after their last field.
|
||
Every track an AI participant publishes needs a synthesized track claim naming its model.
|
||
- **Certified synthesis.** A review (`wai-review`) is a signed verdict over one exact
|
||
reconstruction at `decode-equivalence` or `generation-equivalence`. A player presents it only
|
||
where its latest presentation of the object holds that tier on its decode path and output the
|
||
reconstruction reviewed, so one review holds for every viewer of exact output.
|
||
- **Labels.** A label policy (`wai-label-policy`) says which classes are labelled, with which
|
||
visual and audible assets, at what interval and grace; a player's signed log (`wai-player`) of
|
||
presentations, labels shown, resumptions after a break and reviews presented is held to it:
|
||
a label at first exposure, after each interruption and within the interval. The EU icons the
|
||
Code of Practice's Annex 1 publishes are pinned by SHA-256; no audible EU label is published.
|
||
- **Reference and corpus.** `wai-rs` feature `synthesis` (`src/synthesis.rs`).
|
||
`live-conformance/synthesis/`, written from fixed seeds by `wai_synthesis_vectors`: a
|
||
capture's chain through a neural codec and both pinned derivations, a float super-resolution
|
||
asserted, generated media in both layers, a room with an AI host, a certified and labelled
|
||
session, and one refused case per reason. Checked by `tests/synthesis.rs` and by
|
||
`live-conformance/synthesis_verify.py`, a second implementation that shares no code with
|
||
`wai-rs`, verifies each room with `live_rooms_verify.py`, runs the derivations and writes each
|
||
statement itself, and requires every mutated copy of the corpus to fail. The registry lists
|
||
the record and its document kinds.
|
||
|
||
### `wai.edge.derivation`: an edge that derives, under its own receipts, chained to its source
|
||
|
||
- **The extension** ([deriving-edge](extensions/deriving-edge.md)) registers the record
|
||
`wai.edge.derivation`. An edge evaluates an operation of derived-tracks (`wai.derive.*`) over
|
||
8-bit RGB samples, rebuilds an object at a lower rung for a sink that lacks its capability (a
|
||
frame of a `wai.video.int_motion` clip as a `wai.image.png` still or a `wai.image.jpeg`
|
||
picture), or explodes a `WAI2` component into its `WAI1` envelope. It alters nothing it
|
||
received: each output is a new object, published in a group of the edge's own track under its
|
||
key, and its manifest is pinned, with `derived_from` naming a single input.
|
||
- **The record** names each input by content hash, the group receipt that carries it, the frame
|
||
it was read at, the digest of its samples and the record that derived it where another edge
|
||
did; the derivation, its parameters and the encoder where one is not pinned; the output by
|
||
content hash, receipt, size and samples digest; and the energy with its acquisition class,
|
||
which must agree with the output's object receipt. It is JSON under jwp-receipts §2.4.3's
|
||
encodings, signed over its canonical form and identified by a BLAKE3 hash.
|
||
- **The check.** `replicate` when every step is pinned: the inputs' reading (packaging §5's
|
||
still reader, which here refuses a chunk that could state a colour representation; the
|
||
`WIV1` keyframe-0 decode), an operation exact under derived-tracks §2 (every registered one
|
||
but scaling, by the registry's own composition rule), and the still writer of packaging §5
|
||
or SPEC §7.1's explode. A verifier holding the inputs re-executes the hop and compares the
|
||
output byte for byte. `attest` otherwise: the inputs and every pinned step before the
|
||
attested one are still checked.
|
||
- **Verification** follows a derived object hop by hop to the publishers a sink subscribed to:
|
||
each record read and its signature checked, its output bound to the edge's receipts, its
|
||
inputs to theirs, each derived input to the record that derived it, each replicate hop
|
||
re-executed where its inputs are held, and each edge's authority, `edge/derive`, from the
|
||
origins of the sources its chain reaches (registered in jwp-receipts §7.5). A sink's policy
|
||
may refuse attested hops, require re-execution, or require authority. The facts state each
|
||
hop's check, how it was checked, its energy and authority, the sources reached, and the
|
||
energy per acquisition class, never summed across classes.
|
||
- **Provenance carried forward.** A record lists, by digest, every provenance item of every
|
||
input: the claims about it, the C2PA Manifest Stores bound to it or embedded in it (a PNG's
|
||
`caBX` chunk, C2PA 2.4 §A.3.2) and every item the records below it carry, each in a system or
|
||
a personal compartment, read in the split California Business and Professions Code
|
||
§22757.1 makes (AB 853, Statutes of 2025, Chapter 674, whose §22757.3.1 forbids large online
|
||
platforms to knowingly strip system provenance from 1 January 2027). A hop that leaves one out
|
||
is refused (`provenance_stripped`), and the sink holds every item's digest from the subject's
|
||
record alone. This revision carries personal items as it carries system ones; redaction under
|
||
a stated rule is to come.
|
||
- **An AI-origin label** (`assist`, `generate`) on every input and output, which
|
||
may rise along a chain and never fall (`synthesis_dropped`); a step that synthesizes content
|
||
labels its output.
|
||
- **Refusals** from `record_malformed` to `attested_refused`, among them a derivation not
|
||
registered, inputs that do not verify, a hop passing an edge's output off as a source object
|
||
(`chain_skips_hop`), an output that does not match its recomputation (`replicate_mismatch`),
|
||
and an edge the source's origin does not list (`edge_not_authorised`).
|
||
- **Reference and corpus.** `wai-rs` feature `derive_edge` (`src/derive_edge.rs`), and the
|
||
reference edge `wai_edge`, which derives from a bundle of objects and receipts after verifying
|
||
each input, and verifies a derived object back to its source. The still writer and reader of
|
||
packaging §5 move from `wai-pack` into `wai-rs` (`png_still`), which `wai-pack` re-exports.
|
||
`edge-conformance/`, written from fixed seeds by `wai_edge_vectors`: replicate hops of every
|
||
operation, rebuilt stills, an explode and chains of several hops, attested hops, policies, and
|
||
controls for every reason. Checked by `tests/derive_edge.rs`, which also runs the reference
|
||
edge twice and verifies the chain it makes, and by `edge-conformance/edge_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`, re-executes every replicate hop, requires
|
||
every mutated copy of the corpus to fail and the corpus to catch it with each of its own
|
||
checks dropped. The registry lists the record and the document kind `wai-derivation`.
|
||
|
||
### `wai.live.provenance`: C2PA 2.4 live video provenance for room tracks and takes, tied to the JWP receipts
|
||
|
||
- **The extension** ([live-provenance](extensions/live-provenance.md)) registers the record
|
||
`wai.live.provenance`: C2PA 2.4 §19's live video provenance, written exactly as §19 defines
|
||
it, for a room's CMAF tracks and a device's CMAF take. A room track packaged as CMAF travels
|
||
under `draft-ietf-moq-cmsf-01`, each MoQ group one segment and each object one CMAF chunk.
|
||
A segment is its C2PA boxes followed by its group's objects and nothing else, so the
|
||
segment's `c2pa.hash.bmff.v3` and the group's JWP receipt commit to the same chunks; the
|
||
segment's sequence number is its first `mfhd`'s, following from the receipts' leaf counts.
|
||
- **Both methods.** Per-segment C2PA Manifest Boxes: each manifest holds the hard binding, the
|
||
actions, `c2pa.livevideo.segment` (the stream identifier and the manifest before it) and a
|
||
group assertion naming its receipt by link hash and root. Verifiable Segment Info: an `emsg`
|
||
of version 0 per segment carrying a COSE_Sign1 by a session key over the segment-info map;
|
||
the session keys, OKP Ed25519 keys bound to the claim signer's certificate, are delivered in
|
||
the initialization manifest and rotated by a manifest in a segment, and the session key also
|
||
signs the group's receipts. A take uses both at once, its device's key the session key, and
|
||
its stream identifier its take id's, as capture-binding §11 had them.
|
||
- **The profile.** One manifest per store; EdDSA throughout, deterministic as C2PA §13.2.1
|
||
recommends for streaming; an X.509 chain of version 3 Ed25519 certificates with
|
||
`c2pa-kp-claimSigning`; four whole-box exclusions in every hash; every manifest identifier
|
||
derived from the stream, the manifest's role and the segment's sequence number. Two readings
|
||
of C2PA 2.4 are stated: the VSI box's exclusion offset is 12, where `scheme_id_uri` begins,
|
||
since the 0 §19.4.1 prints compares a box's size; and a `signerBinding` is detached with a
|
||
`nil` payload, as §13.2.3 requires, where §18.25's example prints `h''`.
|
||
- **AI disclosure.** Every manifest carries the IPTC digital source type and one
|
||
`c2pa.ai-disclosure` per model that the stream's label gives, as timed-text §10.3 gives them
|
||
for captions; a compositor's output is `compositeCapture`, `composite` or
|
||
`compositeSynthetic` from its inputs' labels and carries their models' disclosures. A stream
|
||
labelled machine-made without the marking is refused. This is the machine-readable marking
|
||
Regulation (EU) 2024/1689 Article 50(2) describes, stated as an engineering requirement.
|
||
- **Dynamic and redundant packaging.** C2PA 2.4 treats content packaged on request as live
|
||
(§19.2.1) and recommends deterministic signatures for it, naming ISO/IEC 23009-9 (§13.2.1).
|
||
With derived identifiers, deterministic CBOR and Ed25519, every byte of a segment is a
|
||
function of its group's objects, the stream and the claim signer's key: packagers that share
|
||
a key write the same segment, and packagers that do not chain across a failover, their
|
||
segments' objects under the publisher's one receipt. Checking two encoders' outputs by
|
||
re-encoding under a `replicate` capability is stated as not in this revision.
|
||
- **Repository receipts** (C2PA 2.4 §18.27). A registered initialization segment keeps its
|
||
manifest box as `original` and ends with an `update` box: an update manifest whose one
|
||
ingredient is the parent, with the parent's validation results, and whose
|
||
`c2pa.repository-receipt` carries, as the repository's proof, an RFC 9942 receipt of
|
||
inclusion of the parent's manifest hash in the repository's RFC 9162 log, verified against
|
||
the repository's key.
|
||
- **Carriage.** Over HTTP, the segment files. Over MoQ, each segment's C2PA boxes ride a
|
||
provenance track, `<layer>_c2pa` (`packaging` `wai-c2pa`, named in `wai_c2pa_track`), whose
|
||
group *g* is segment *g*'s C2PA boxes, so the media objects stay the chunks a CMSF player plays
|
||
and the receipts commit to; the initialization segment rides the catalog's inline data.
|
||
- **Thirty-three reasons**, from `segment_malformed` to `segment_missing`, each with the C2PA
|
||
2.4 §15 and §19.7 codes a C2PA validator reports for it, where C2PA checks it.
|
||
- **Reference and corpus.** `wai-rs` feature `live_provenance` (`src/live_provenance.rs` and
|
||
its `bmff`, `jumbf`, `cose`, `x509` and `corpus` modules), with no C2PA library and no new
|
||
dependency. `live-conformance/c2pa-live/`, written from fixed seeds by
|
||
`wai_live_provenance_vectors` over the packaging corpus's AV1 floor: a camera track under each
|
||
method (the session key rotated at its third segment), a track labelled machine-made, a
|
||
compositor's output, a take, a failover between two packagers and a registered stream; and
|
||
one negative per reason, among them a segment whose bytes
|
||
changed, a missing segment and a reordered one, a manifest bound to the wrong receipt, a
|
||
stream identifier that differs, an expired and a time-stamping end entity, and a machine-made
|
||
track without its disclosure. Checked by `tests/live_provenance.rs` and by
|
||
`live-conformance/c2pa_live_verify.py`, a second implementation that shares no code with
|
||
`wai-rs` and requires every mutated copy of a good vector to be refused. The corpus's
|
||
initialization segments, the registered one with its update manifest included, also validate
|
||
in the C2PA command-line tool; the tool opens no media segment (`c2pa-live/README.md`). The
|
||
registry lists the record and the catalog values `wai-c2pa` and `wai_c2pa_track`.
|
||
- **capture-binding §11** now points here for the C2PA side of a take, and is implemented.
|
||
|
||
### `wai-ops/1-i32` on GPUs, and the tier where a run showed it
|
||
|
||
- **`wai/wai-ops-gpu`**, an operator implementation of `wai-ops/1-i32` (integer-operators §9.1) in WGSL compute kernels through wgpu: Metal, Vulkan and DX12 natively, and a browser's WebGPU. WGSL has no 64-bit integer, so the kernels use 32-bit words only:
|
||
- a graph whose certificate meets `acc32` runs with one word per value, its two `product` intermediates formed from 16-bit halves (§7.3);
|
||
- every other graph runs with each `acc` value in two words and each `wide` and `product` value in four, exact 64-bit and 128-bit arithmetic built from 32-bit words, an `acc64` implementation (§7.3);
|
||
- a graph with `layernorm_c`, `l2norm`, `softmax`, `sigmoid` or `elu` runs on the CPU, and `support` names the operator that sent it there.
|
||
|
||
The host makes the interpreter's run-time checks, in its order, before the device runs anything; a `check` tensor that leaves its range sets its operator's flag, and the first flagged operator refuses the decode with `field_range`. Each device passes an arithmetic probe against 128-bit host arithmetic before it runs a graph.
|
||
- **wai-rs `ops_gpu`** (off by default): `int_ops::run_graph` is now where every integer decoder runs a graph, and inside `int_ops::gpu::with_gpu` it runs each graph on the device, counting what ran where and why.
|
||
- **Tests that do not pass on a skip.** `wai-ops-gpu/tests/`: the probe on a million cases; every graph vector of `ops-conformance/` that the subset covers, at both widths, against the committed results and the interpreter; eighteen kernel edits, each of which the corpus must catch; and the 32-bit kernels shown to differ on graphs their certificate keeps from them. `wai-rs/tests/ops_gpu.rs`: every committed vector and refusal fixture of every capability that runs a graph, with its graphs on the device, against the CPU decode, graph by graph. `wai-ops-gpu/browser/` runs the corpus in a page's WebGPU. With no adapter each prints SKIPPED, and fails under `WAI_OPS_GPU_REQUIRE`.
|
||
- **determinism-tiers §5** now meets `decode-equivalence` for a decoder's `wai-ops/1-i32` graphs on the GPUs and backends a run showed equal, and lists them; §6 records the runs. Nothing is claimed for another adapter, and nothing for an NPU.
|
||
- **`tools/ops-gpu-bench`** times each capability's decode with its graphs on the CPU and on each adapter, and reads the energy counters the host has.
|
||
- **CI:** `wai-ops-gpu.yml` runs the Metal and browser legs on the `macos` runner, and requires on a runner with no GPU that the device tests skip without `WAI_OPS_GPU_REQUIRE` and fail with it.
|
||
|
||
### wai-quantum 0.6.14 and wai-quantum-cli 0.1.2: contraction trees that sweep along the line, and the design checks on the job service
|
||
|
||
0.6.13 was not published on its own: this release carries it to the package registry, and `wai-quantum-cli` 0.1.2 requires it.
|
||
|
||
- **Sweep seeds for the contraction search** (`quantum_tn`):
|
||
- `Network::amplitude_split_placed` builds the same network as `amplitude_split`, index for index, and gives each index its place in the circuit: a qubit and a time.
|
||
- `tensor_places` gives each tensor its place.
|
||
- `sweep_orders` orders the tensors qubit by qubit along time, or time by time across the qubits.
|
||
- `sweep_path` absorbs them in that order.
|
||
- `find_path_seeded` and `find_path_restarts_seeded` score such trees beside the trials, and polish the best of them all alike. A seed that is not a tree of the network is ignored.
|
||
|
||
A deep circuit on a line, swept qubit by qubit, keeps only a time slice of the bonds between two neighbouring qubits as its boundary: about half its depth for a controlled-Z brickwork. The bipartition and greedy families rarely find that tree on their own. The engine's `tensor_network` method and the thorough classical-shortcut lens now take both sweeps as seeds.
|
||
- **On the 70-qubit random graph-state circuit** (CZ-depth 70), for one amplitude:
|
||
|
||
| | largest intermediate | multiply-adds |
|
||
|---|---|---|
|
||
| the search alone, 16 trials | 2^59 | 10^23.5 |
|
||
| the search with the sweeps as seeds, 16 trials | 2^36 | 10^14.6 |
|
||
| the thorough lens, before | 2^48 | 2^56.1 |
|
||
| the thorough lens, with the sweeps | 2^36 | 2^48.7 |
|
||
|
||
The thorough check took 182 s, down from 238 s. The plans pinned in `tests/bit_identity.rs` call `find_path` itself and do not change.
|
||
- **The design checks on the job service:**
|
||
- `POST /v1/critique`;
|
||
- `POST /v1/equivalent`;
|
||
- `POST /v1/improve`;
|
||
- `POST /v1/shortcuts`;
|
||
- the matching MCP tools: `critique_qasm`, `check_equivalent`, `improve_qasm`, `classical_shortcuts`.
|
||
|
||
They answer at once, in the service's own process, within bounds:
|
||
- each counts against the client's submissions, as a job does;
|
||
- at most two run at a time, and one that waits ten seconds for a turn is refused (503);
|
||
- some checks are refused (422) with the command that runs them locally: an equivalence check past 4·10⁹ gate amplitudes, an improvement whose checks would pass that, and a thorough search for shortcuts.
|
||
|
||
The service's smoke checks cover each one.
|
||
|
||
### wai-quantum 0.6.13 and wai-quantum-cli 0.1.1: the engine critiques a design, checks two circuits are the same, offers alternatives, and prices its classical shortcuts
|
||
|
||
The engine is the microscope and the judge, a local model is a muse that waits to be asked, and the reader is the author. Every figure below is computed by the engine. Nothing changes the reader's program until they apply a change.
|
||
|
||
- **`engine::critique`** gives a program's measures and its smells before it runs.
|
||
- The measures: gates by kind; one-, two- and three-qubit gates; T gates; gates off the Clifford angles; depth and two-qubit depth; and the measured qubits.
|
||
- It says whether the program is a Clifford circuit.
|
||
- It gives a fidelity at reference error rates (`REFERENCE_RATES`), named as a model of a device and not a run.
|
||
- The smells, each with its lines and its operations: gates that cancel; rotations that merge; rotations that do nothing; qubits nothing uses; and qubits acted on but never measured.
|
||
- In a program that measures in the middle, a measurement, a reset or a condition is a barrier, and no smell spans one.
|
||
- **`critique::variants`** rewrites the program in its own text with its smells fixed.
|
||
- It offers every fix, to a fixed point, since one fix can bring two gates together. It also offers each first-round fix alone, and each distinct program once.
|
||
- A fix touches only a statement alone on its line. A merge keeps the program's own expressions, as `rz(pi/4 + pi/8)`, and its comment.
|
||
- Each candidate carries its measures and the equivalence check's verdict against the program. A candidate the check finds different is never offered.
|
||
- **`engine::equiv`** says whether two programs are the same circuit up to a global phase, and how it knows:
|
||
- Clifford circuits by their tableaus, at any number of qubits;
|
||
- otherwise on every basis state, up to 8 qubits and within a work budget;
|
||
- otherwise on three random product states, up to 22 qubits, which is evidence and not proof;
|
||
- otherwise unchecked, with the reason.
|
||
|
||
A difference names the input it was found on. Programs that measure in the middle are not compared. Programs of different widths, or that write different qubits to the bits, differ by shape.
|
||
- **`qasm::parse_lines`** gives each operation's source line: a broadcast's gates and a definition's expansion share the call's line. **`draw::layout`** gives each gate the index of its operation (`op`), so a diagram can mark the operations a smell names.
|
||
- **In the browser:** `wai-web` exports `engine_critique`, `engine_equivalent` and `engine_variants`, tested native and under wasm.
|
||
- **Quantum Studio, in the browser and on the desktop:**
|
||
- **The critique, always on.** Under the diagram it shows the measures, each with its change since the last run, a Clifford badge and the modelled fidelity. The smells are listed with their lines. Their gates are flagged on the diagram, and each gate's tooltip names its smell. Show selects the lines and marks the gates. Fix applies the engine's rewrite, and the note says how the result was checked the same circuit; Undo brings the lines back.
|
||
- **Alternatives.** Improve offers the same circuit, cheaper: the engine's rewrites, and the model's when one is connected, each checked against the draft. A rewrite the check finds different is listed as not offered, with the reason. Explore asks the model for different circuits toward an aim the reader names. The engine reads, measures and compares each one, and gives how many lines it changes. An atlas plots the draft and each alternative by two-qubit gates and depth. Apply puts one in the editor.
|
||
- **Predict first** (off by default). Before a run, the reader names the outcome they expect most often and its share of the shots. The run's counts are set against it, and a tally of how close their predictions come is kept on this device.
|
||
- **The assistant asks questions first, and speaks only when asked.** The reader chooses how it helps: it asks questions first (the default), suggests changes, or writes programs. They also choose when it speaks: only when asked (the default), or with a hint offered, never sent, after a problem the engine has reported for a while or two refused runs in a row. New quick asks are "Ask me a question" and "Hint". New tools are `critique_qasm` and `check_equivalent`. For Alternatives it is called once per request, without tools and outside the conversation, and only proposes.
|
||
- The desktop app answers `critique`, `equivalent` and `variants` natively.
|
||
- **`wai-quantum-cli` 0.1.1:**
|
||
- `wai-quantum critique` gives the measures and smells.
|
||
- `wai-quantum equivalent a.qasm b.qasm` exits 0 for the same circuit, 1 for different and 2 for unchecked.
|
||
- `wai-quantum improve` prints the improved program, and what changed and how it was checked on standard error.
|
||
- `wai-quantum shortcuts prog.qasm [--thorough] [--json]` prices each exact classical route.
|
||
- The MCP server adds `critique_qasm`, `check_equivalent`, `improve_qasm` and `classical_shortcuts`.
|
||
- **`engine::shortcuts`** lists every exact classical route the engine knows for simulating a program, with its cost on that program:
|
||
- **the stabilizer tableau**, for Clifford circuits, at any width;
|
||
- **the cut-rank sum**, for a Clifford circuit followed by one layer of one-qubit gates. Its cost is a table of 2^cut entries along the best order found, and includes running the bulk's tableau and reducing it to graph form;
|
||
- **an exact matrix product state** along the qubits' own order. Its bond is bounded across each cut by the product of the operator Schmidt ranks of the gates that cross it, each computed from the gate's matrix, and never by more than the smaller side's dimension. The bound is rigorous: a test holds it against the true Schmidt rank at every cut of random circuits of every two- and three-qubit gate;
|
||
- **the light cone** of a one-qubit expectation value;
|
||
- **a tensor-network contraction** of one amplitude of a seeded random output, by the engine's tree search (when asked to be thorough);
|
||
- **the state vector**.
|
||
|
||
Each route gives its memory, the log2 of its operations to a first answer, and a time at a reference rate of 10¹⁰ operations a second, which is a reference and not a measurement. The cheapest route is named for counts, amplitudes, expectation values and one-qubit expectation values. A summary leads with "classically easy at any width" for a Clifford circuit.
|
||
|
||
A cheap route proves a design classically easy. None being cheap proves nothing about hardness: other methods, approximate ones above all, may cost far less, and the result says so.
|
||
- **On the 70-qubit random graph-state circuit** (CZ-depth 70, a closing layer of face-basis rotations), the quick check finds the cut-rank sum in under 2 s on one workstation. Its widest cut is 30 bits along a quick order search, at about 2^35 operations a sample. The matrix product state's bound is 2^35 at the middle cut, and the light cone of every qubit is all 70.
|
||
- **The lens in the browser and on the desktop:** `wai-web` exports `engine_shortcuts`, tested native and in wasm. The desktop app answers `shortcuts` off the window's thread.
|
||
- **The lens in Quantum Studio:**
|
||
- Under the critique, "Classical cost" prices each route and names the cheapest. "Thorough" adds the contraction search.
|
||
- A check is marked stale once the program changes.
|
||
- The assistant gains the tool `classical_shortcuts`.
|
||
|
||
### wai-quantum 0.6.12: cheaper contraction trees, and the cut-rank table on the GPU
|
||
|
||
0.6.11 was not published on its own: this release carries it to the package registry, and `wai-quantum-cli` 0.1.0 requires it, since the command line's `metal` feature turns on `quantum_cutrank_metal`.
|
||
|
||
One pinned digest changes, by design: the tensor-network plan in `tests/bit_identity.rs`, because the search now finds a cheaper plan. The native and WASI builds compute the same new digest. Every other pin is unchanged.
|
||
|
||
- **Each bisection runs two V-cycles.** After the split is projected onto the full graph:
|
||
- the graph is coarsened again, matching nodes only within a side, so the split carries to the coarse graph with the same cut;
|
||
- the split is refined there and on the way back down, which never raises the cut.
|
||
- **The best few trees are annealed after reconfiguration** (`quantum_tn::anneal`):
|
||
- random rotations at each node are accepted on the change in log total cost, at a falling temperature;
|
||
- the cheapest tree seen is kept and reconfigured again;
|
||
- a rotation changes the cost of two contractions only, so each move costs two set sizes.
|
||
- **On the 61-qubit Nighthawk logical circuits** (split and reduced, 1024 trials, 8 restarts, the median of 4 seeds). The figures are log10 complex multiply-adds for one amplitude:
|
||
|
||
| depth | 0.6.11 | 0.6.12 |
|
||
|---|---|---|
|
||
| 8 | 5.30 | 5.29 |
|
||
| 12 | 8.10 | 8.09 |
|
||
| 16 | 11.12 | 10.97 |
|
||
| 20 | 14.02 | 13.68 |
|
||
|
||
The search takes about 1.7 times as long at depth 20.
|
||
- **`quantum_cutrank_metal`** (feature `quantum_cutrank_metal`, macOS) runs the cut-rank table on the platform's GPU:
|
||
- one shared buffer of `2^cut` single-precision entries, at most `2³¹`;
|
||
- one dispatch per pass of the walk, in its order and with its arithmetic, done in `f32`, so an amplitude is one command buffer;
|
||
- a sample's sums reduced per threadgroup on the device and added on the host in `f64`, in order, with the outcome drawn as the host's walk draws it;
|
||
- a batch of shots sampled side by side, each shot's table at its own offset and its weights in a small per-shot array, so a qubit is one command buffer for the whole batch;
|
||
- each shot drawing from a stream of its own, so a seed's samples are the same at any batch size.
|
||
|
||
It is reproducible on one device, and is checked against the double-precision walk on states of 10 to 34 qubits: amplitudes within `10⁻⁵` of each amplitude's size, and samples whose probabilities are their own and whose counts follow them.
|
||
- **The engine's `cutrank_single` method** runs a `cutrank` job on that table, where the build has the feature and the host has a GPU. `auto` never picks it.
|
||
- Its amplitudes carry the circuit's global phase, which is found on the same table.
|
||
- The job service, the command line's `metal` feature and the desktop app's `metal` feature build it in.
|
||
- The service marks a `cutrank_single` job's energy figure partial, as it does `statevector_single`'s, because the counter does not read the GPU.
|
||
- `examples/cutrank_gpu` times it against the host's tables.
|
||
- **Speed.** One amplitude of a random graph state on a shared desktop machine, each device timed at its quietest. Either side ran up to five times slower while other work held it.
|
||
|
||
| widest cut | host, double | host, single | GPU |
|
||
|---|---|---|---|
|
||
| 23 bits | 0.027 s | 0.024 s | 0.005 s |
|
||
| 25 bits | 0.057 s | 0.045 s | 0.019 s |
|
||
| 27 bits | 0.18 s | 0.17 s | 0.046 s |
|
||
| 29 bits | 1.03 s | 0.54 s | 0.34 s |
|
||
|
||
A pass of the table runs at about 550 GB/s on an idle device.
|
||
|
||
Sampling 64 shots of the same states, 16 a batch, with the host at about half its quietest speed:
|
||
|
||
| widest cut | host, single | GPU, one at a time | GPU, 16 a batch |
|
||
|---|---|---|---|
|
||
| 16 bits | 0.52 s | 0.62 s | 0.036 s |
|
||
| 20 bits | 1.97 s | 0.73 s | 0.067 s |
|
||
| 23 bits | 3.87 s | 0.91 s | 0.34 s |
|
||
| 25 bits | 7.48 s | 1.82 s | 1.20 s |
|
||
|
||
The engine samples up to 32 shots a batch, as many as 2 GiB beside the job's first table holds.
|
||
|
||
### wai-quantum 0.6.11 and wai-quantum-cli 0.1.0: one job engine for the service, the command line and a browser tab
|
||
|
||
Additive, apart from two refusals and an admission noted below. Every pinned digest is unchanged.
|
||
|
||
- **`wai_quantum::engine`** (feature `quantum_engine`) is the job service's engine, moved into the library.
|
||
- `catalog` plans a job onto the strongest method it admits, and says why.
|
||
- `run` carries the plan out.
|
||
- `dynamic` runs programs that measure in the middle.
|
||
- `receipt` is the service's receipt form: a result sealed over the request and the result in their RFC 8785 canonical text.
|
||
- `job` takes a job end to end and checks it.
|
||
- `draw` writes a program's circuit as text.
|
||
|
||
The service imports all of these. A job now gives the same result and the same receipt form whether it runs on the service, on the command line or in a browser tab.
|
||
- **The engine builds for `wasm32-unknown-unknown`.** There a result carries no `elapsed_ms`, since a tab has no clock std can read. The multi-seed cut-rank order search runs its seeds one after another and finds the same orders.
|
||
- **A noise channel the model does not know is refused** (`catalog::NOISE_CHANNELS`). It was read as no noise, so a misspelt channel ran the circuit noiseless.
|
||
- **The job's memory margin is counted once for a program that measures in the middle on the state vector,** as it is for trajectories. It was counted once per state, and the service refused a qubit it could hold.
|
||
- **`quantum_receipt::did_key`** gives an Ed25519 key's `did:key`, held to the W3C method's example.
|
||
- **`wai-quantum-cli` 0.1.0** is a new crate whose binary is `wai-quantum`. Its commands:
|
||
- `run`: planned and run as the service runs it, signed with the user's key;
|
||
- `plan`, `lint` and `draw`;
|
||
- `submit` to the service;
|
||
- `verify`, for any receipt this tool, the service or `lab` signs;
|
||
- `key`, `methods` and `completions`;
|
||
- `mcp`, which serves these tools to an agent over stdio;
|
||
- `lab`, which holds the earlier command's research tools.
|
||
|
||
It depends on the library alone. The `wai-quantum` bin and the `cli_keys` feature leave wai-rs.
|
||
- **Quantum Studio** (`wai/quantum-studio`) is one interface for the browser and the desktop app (`wai/wai-quantum-desktop`). Its editor:
|
||
- highlights and completes OpenQASM;
|
||
- marks the engine's diagnostics on their lines;
|
||
- draws the circuit and shows the plan as the program is typed.
|
||
|
||
It runs a job in the browser, on the desktop's native engine, or on the service, and checks each receipt where it runs. The desktop app runs each job in a process of its own and seals it with the user's key.
|
||
|
||
Its assistant uses a local model with the engine as its tools. It checks and runs every program the model proposes before offering it, and marks any figure the engine did not compute.
|
||
- **`engine::job`** also splits into `plan_and_run` and `seal`, for hosts that run a job away from their key.
|
||
- **`engine::draw::layout`** gives a circuit's layout as data: its columns, and each gate's cells. The text drawing and Quantum Studio's diagram are both drawn from it. wai-web exports it as `engine_layout`, and the desktop app as `layout`.
|
||
- **Path search from independent restarts** (`quantum_tn::find_path_restarts`):
|
||
- independent searches run side by side, and the cheapest plan is kept;
|
||
- the plan is never costlier than one search, and is the same whatever the thread count;
|
||
- the engine's `tensor_network` method runs 8 restarts natively and 1 in a tab, and the job option `path_restarts` (1–64) sets the count.
|
||
- **Release builds of `wai-quantum`.** A tag `wai-quantum-cli-v<version>` builds the binary on the project's runners and attaches each archive, with its SHA-256, to the forge release:
|
||
- macOS, for Apple silicon and for Intel;
|
||
- Linux, linked statically;
|
||
- Windows.
|
||
|
||
The same workflow writes a Homebrew formula and a Scoop manifest for the archives.
|
||
- **wai-web, feature `quantum_engine`:** `QuantumEngine` (run, plan, methods and signer), `engine_lint`, `engine_draw` and `engine_verify`, for a page. Tested in wasm32 under Node on every method a tab holds.
|
||
|
||
### wai-quantum 0.6.10: one OpenQASM reader for the service, the command line and the browser IDE
|
||
|
||
Additive, apart from the old line reader's refusals below. Every pinned digest is unchanged.
|
||
|
||
- **`wai_quantum::qasm`** (feature `quantum_qasm`) is the job service's OpenQASM reader,
|
||
moved into the library unchanged. The service, `wai-quantum run --qasm` and the browser
|
||
IDE's import now read a program the same way. Before, the IDE read two registers as
|
||
one, skipped a gate on a whole register, and read past what it did not know.
|
||
- **`qasm::dyadic_circuit`** gives the exact state vector's circuit for a program whose
|
||
measurements all come at the end, and refuses one that measures and then acts, resets
|
||
after a gate or uses `if`, with the reason.
|
||
- **`quantum_toolchain::from_qasm`**, the older line reader, refuses what it cannot take
|
||
instead of dropping the line: more than one register, `reset`, gate definitions,
|
||
`if`/`while`/`for`, a gate after a measurement, two statements on one line. Each
|
||
refusal names `wai_quantum::qasm`.
|
||
- **The IDE binding** (`wai-web`) builds its JSON with `serde_json`, so a quote or a
|
||
control character in a program or an error no longer breaks a reply.
|
||
`set_signing_key`, `public_key_hex`, `signer_did` and `key_is_demo` let a page sign
|
||
receipts with a key of its own; without one it signs with the published demo key and
|
||
says so.
|
||
- **The command line** signs with `--key FILE`, `$WAI_QUANTUM_KEY`, or this user's key at
|
||
`$XDG_CONFIG_HOME/wai/quantum-key` (or `~/.config/wai/quantum-key`). That key is made
|
||
from the operating system's random source on first use, with mode 0600.
|
||
`--demo-key` keeps the published key, with a note that its receipts show a result is
|
||
unaltered, not who signed it.
|
||
- **A receipt from the command line or the IDE names its signer by its key's `did:key`**
|
||
(`wai::signer_id::did_key`), where it used to carry a label naming no key.
|
||
|
||
### Live L10b: end-to-end encrypted rooms in the browser
|
||
|
||
Additive. No record, code point, reason or corpus vector changes.
|
||
|
||
- **confidentiality §15, a member in a browser.** A page is a member as any other: the
|
||
reference's member, sealed objects and codec built for WebAssembly as one module a page loads
|
||
for such a room alone, every key in the page's memory and nowhere else. It seals each encoded
|
||
frame before its receipt is signed over the sealed bytes, and opens each object through §6
|
||
before a decoder sees it; an object of an epoch not yet reached waits (§5.8).
|
||
- **§15.2, a room service as the carrier** of a room's MLS messages (§5.1): one log, in the
|
||
order the clock took them, of the clock's lifecycle records, its proposals, and every
|
||
GroupInfo, commit and Welcome a member posts; the clock checks each post as a member does and
|
||
does not carry what it refuses. A member reached through such a log **MUST** apply a
|
||
GroupInfo or commit it made only when the log returns it, and drop it when another's commit
|
||
for the epoch comes first: §5.5 names the committer from the commit's own proposals, so two
|
||
members with different views of the pending proposals can each commit. Before its Welcome a
|
||
member follows the group from its handshake messages, as the clock does.
|
||
- **§15.3** states what a browser member does not do in this revision: watch without being a
|
||
member, ingest by WHIP or WebRTC into such a room, seek back. §1's scope says so.
|
||
- **wai-live-web**: `roomDescriptor` gives the descriptor's `recording_mode`, which a page
|
||
checks a room's confidentiality record against (§3.3 step 5).
|
||
|
||
### `wai.live.ads`: a room's cues as server-guided ad breaks, and the break form of `wai.video.ssai`
|
||
|
||
- **The extension** ([live-ads](extensions/live-ads.md)) registers the record `wai.live.ads`.
|
||
The room authority signs an **ad policy**, `WAP1`: the grant the ad decider runs under, whose
|
||
funds ceiling is the room's ad budget; the slots of a break; the avail of a break opened with
|
||
no planned duration; a preload window; skip and jump restrictions, a skip control and
|
||
snapping; and the bases of the asset-list and creative URIs.
|
||
- **Breaks from cues.** A room's breaks are its signed `cue` events of `wai.live.events`,
|
||
unchanged: an out and its in, or a break of known duration; a chapter opens nothing. A
|
||
post-roll is a break its author annotates with a `WAB1` `meta` event right after it, and ends
|
||
the program. A verifier refuses an out or a break inside another break, an in with no open
|
||
out of its `cue_id` and author, a break of no duration, an out with no planned duration when
|
||
the policy has no open avail, an in whose stated duration is not the one that passed, an
|
||
annotation that does not read or that follows anything but a break, and any cue after the
|
||
post-roll; the room refuses a cue by a principal whose grant does not permit `cue`.
|
||
- **Shared manifests.** Each break is one Interstitial `EXT-X-DATERANGE`
|
||
(`draft-pantos-hls-rfc8216bis-22` Appendix D) with `CUE`, its duration or planned duration,
|
||
`X-ASSET-LIST`, `X-RESUME-OFFSET`, `X-PLAYOUT-LIMIT`, `X-SNAP`, `X-RESTRICT` and
|
||
`X-SKIP-CONTROL-OFFSET`, a closing tag at its in, and a Preload date range (Appendix F); and
|
||
one DASH alternative-presentation event of ISO/IEC 23009-1:2026, a replacement for a mid-roll
|
||
and an insertion for a post-roll, read from the sixth edition's schema and its examples, with
|
||
the attributes whose meaning that revision could not state left unwritten. Every value is a
|
||
function of the signed cue, its annotation, the policy and `t0`, and the `ID` is derived from
|
||
the cue's digest, so every edge writes the same bytes.
|
||
- **Asset lists.** The one URI a break names is where each viewer is decided: an HLS asset list
|
||
or a DASH list MPD, written exactly, each creative's URI naming it by its hash, filled by
|
||
`wai.video.ssai`'s decision over the viewer's pod; a viewer who joins a break running is
|
||
filled from what remains of it. An empty fill is `{"ASSETS":[]}` in HLS and no list MPD in
|
||
DASH.
|
||
- **`wai.video.ssai` gains form 2, the break form.** A receipt names the room, the break by its
|
||
cue's digest, the session, the viewer's offset, the asset list's SHA-256 and format, the
|
||
avail, the slots and the funds the decision ran with, beside the stitched form's pod hash,
|
||
impressions, Merkle root, billable spend and energy figure. It is carried as JSON, signs
|
||
under its own domain (`wai:video-ssai-break-receipt`) and shares the identity domain, so the
|
||
figure profile `wai:video-ssai-receipt-id` names either form. Every stitched receipt keeps
|
||
its bytes, and its legacy pin is unchanged.
|
||
- **Verification end to end**: the room, the policy, the schedule, the renderings, then each
|
||
receipt: its own rules, the decider's key and grant reference, the break, the decider grant's
|
||
window and revocation, the pod hash, the decision recomputed, the asset list's digest, one
|
||
receipt per session and break, and the spend of every receipt together within the decider
|
||
grant's ceiling (prices in millionths of a minor unit of its currency).
|
||
- **Twenty refusal reasons**, from `ad_policy_malformed` to `funds_exceeded`.
|
||
- **Reference and corpus.** `wai-rs` feature `live_ads` (`src/live_ads.rs`), and
|
||
`SsaiBreakReceipt` in `src/wai_video_ssai.rs`. `live-conformance/ads/`, written from fixed
|
||
seeds by `wai_live_ads_vectors`: a live break with a viewer at its start and one who joins it
|
||
running, a mid-roll under restrictions with an empty fill, a program closed by a post-roll,
|
||
chapters with a planned out, a gap, and one refused room per reason; each accepted room's
|
||
HLS tags a file of its own. Checked by `tests/live_ads.rs` and by
|
||
`live-conformance/live_ads_verify.py`, a second implementation that shares no code with
|
||
`wai-rs`, verifies each room with `live_rooms_verify.py`, writes every tag, event and asset
|
||
list itself, and requires every mutated copy of the corpus to fail. The registry lists the
|
||
record and the magics `WAP1` and `WAB1`.
|
||
|
||
### `wai.live.watermark`: forensic watermark sessions, matched and disclosed one at a time
|
||
|
||
- **The extension** ([forensic-watermark](extensions/forensic-watermark.md)) registers the
|
||
record `wai.live.watermark`, for a room's Lane A delivery (SPEC §6.5). The edge that chooses
|
||
each segment's A or B variant for a viewer, as ETSI TS 104 002 has an edge choose it, signs a
|
||
**session record**, `WWS1`, when the session ends: the room and the content (a slot or a
|
||
publisher, and its media kind), the span, the KIDs of the content keys the variants were
|
||
encrypted under, the TS's watermark token by its SHA-256, the token's vendor and pattern
|
||
length, how the edge had the pattern (in the clear, decrypted, or derived by the vendor's
|
||
core), a bitmap of the positions it sequenced and the variant at each, its counts of
|
||
sequenced and unmarked segments, a salted commitment to the session's grant and principal,
|
||
where it keeps the record, and when it is deleted.
|
||
- **The token** is the TS's CWT, read as the TS and RFC 8392, RFC 8949 §4.2, RFC 9052 and RFC
|
||
9053 have it: deterministically encoded CBOR, a COSE_Mac0 under HMAC 256/256 or a COSE_Sign1
|
||
under ES256, optionally in the CWT tag, and the claims `wmver`, `wmvnd`, `wmpatlen`,
|
||
`wmsegduration`, `wmpattern` (in the clear, or a COSE_Encrypt0 or COSE_Encrypt) or `wmid`,
|
||
`wmopid` and `wmkeyver`, with `exp` and `iat`. A form the RFCs forbid is `token_malformed`,
|
||
and one they allow that this revision does not read (a floating-point date, an untagged or
|
||
nested message, another algorithm) `token_unsupported`. The record names the token by digest
|
||
and does not carry it: a token can carry `sub` or a vendor's `wmid`, and can be presented
|
||
again until it expires. The token's MAC or signature is the edge's to check.
|
||
- **No viewer in the record.** It names no grant: every participant holds a room's grants, and
|
||
a grant names its principal. Only the room authority, to which the edge gives each record's
|
||
opening, can link a session to a principal. Records, tokens and openings are kept within the
|
||
room's region pin and its retention bound.
|
||
- **Matching.** A claimant signs the variant sequence it recovered from a copy, `WWQ1`, with
|
||
its erasures. A session agrees when it sequenced every stated position with the stated
|
||
variant; a match is the one agreeing session among the records given (`no_match`,
|
||
`ambiguous_match` otherwise). The TS leaves detection's statistics to its vendors, so the
|
||
rule has no tolerance and no threshold, and states how many positions were stated.
|
||
- **Disclosure.** For one matched session the room authority signs a `WWD1` that opens its
|
||
commitment; a third party checks it against that record, the sequence and the grant, and
|
||
learns nothing of other sessions.
|
||
- **Lane A only.** A record names at least one content key (`not_protected`), and a room with
|
||
a confidentiality record has no edge that can mark it (`e2ee_room`): its objects' AEAD binds
|
||
each to its track, group and object.
|
||
- **Refusal reasons**, from `record_invalid` to `disclosure_expired`. jwp-receipts §7.5
|
||
registers the key-log authority `live/watermark` for an edge.
|
||
- **Reference and corpus.** `wai-rs` feature `live_watermark` (`src/live_watermark.rs`), with a
|
||
deterministic-CBOR reader of its own and no new dependency. `live-conformance/watermark/`,
|
||
written from fixed seeds by `wai_live_watermark_vectors`: tokens read and refused, an
|
||
edge's records of four sessions with a token in each mode, sequences matched, unmatched and
|
||
refused, disclosures accepted and refused, one control for each reason. Checked by
|
||
`tests/live_watermark.rs` and by `live-conformance/live_watermark_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`, requires every mutated copy of the corpus
|
||
to fail, and requires the corpus to catch a verifier that leaves out any one of its checks.
|
||
The registry lists the record and the magics `WWS1`, `WWQ1` and `WWD1`.
|
||
|
||
### Live L7b: the DVR window, the seek power, and WMH1 version 2
|
||
|
||
Additive: no existing record changes its bytes, and every existing corpus vector keeps its
|
||
records.
|
||
|
||
- **live-recording §8** gains what a relay that keeps a **DVR window** of a room's tracks
|
||
does with it (§8.2 to §8.5): a window by age (at most 24 hours) and by bytes, evicted
|
||
oldest first by whole group, each media group with its receipts group; a FETCH answered from
|
||
the cache and the window together, groups in the requested order; what was evicted marked
|
||
with an End of Unknown Range (MOQT §11.4.1.2, `0x10C`), never with an unmarked gap or an End
|
||
of Non-Existent Range, since an evicted object existed; an object that cannot be served
|
||
inside a group marked from the group's start, since -22's End of Range moves to a later
|
||
group; a harvest window whose end lies ahead as a SUBSCRIBE with that range and a fill;
|
||
decisions read from the room's events tracks withholding what they deny or redact, never
|
||
replacing it; every object checked again against its receipt after a restart. §8.6 maps the
|
||
window onto DASH's `timeShiftBufferDepth` and an HLS live playlist. §8.1 is the take
|
||
addressing as before. §8.2 also says what a relay never keeps: a room with a confidentiality
|
||
record, known by its first sealed object. The relay keeps nothing of the room from then on,
|
||
deletes what it kept of it before, and remembers the room across a restart. confidentiality
|
||
§9 points to it.
|
||
- **live-rooms**: the **seek** power, bit 4 of `powers` (§3.3), and **§10.6 Seeking back**:
|
||
a FETCH, or a fill, that starts below a track's live edge is served only to a grant with the
|
||
power, and refused `UNAUTHORIZED`, `seek_not_granted`, otherwise. Roles and grants may now
|
||
set bits 0 to 4. `wai-rs` `live_room::seek_check`; the corpus gains a `dvr` room, three
|
||
refusals (a grant's seek beyond its role, power bit 5 in a grant and in a role) and a
|
||
`seek` section of requests served and refused, which `live_rooms_verify.py` checks too.
|
||
- **live-metering**: `WMH1` **version 2** (§3.9) adds a hop's DVR figures before `sig`: the
|
||
objects and bytes served from its window, those it wrote to it, the bytes it held in
|
||
byte-seconds, and the objects it dropped because they did not verify; with their rules
|
||
(§3.6) and bound `B₂(M) = 600 + 180 × (M + 2)` (§3.7). A hop with no window signs version 1.
|
||
The receipt stays version 1 and does not sum the figures. The corpus gains a version 2 relay
|
||
record and four refusals; `live_metering_verify.py` reads both versions.
|
||
|
||
### wai-quantum 0.6.9: cut-rank amplitudes with the circuit's own global phase
|
||
|
||
Additive. Every pinned digest is unchanged.
|
||
|
||
- **`PhasedTableau`** (`quantum_tableau`) is a stabilizer tableau with its global phase.
|
||
- It tracks one basis state `r` of the support and its exact amplitude
|
||
`⟨r|ψ⟩ = 2^{-k/2} e^{iπm/4}` through every gate.
|
||
- Paulis, phase gates, CX, CY, CZ and SWAP move `r` and the phase directly.
|
||
- For a Hadamard on qubit `a`, the amplitude of `r ⊕ e_a` comes from the stabilizer
|
||
whose X part is `e_a`, found by reducing the stabilizers (about `n³/64` word
|
||
operations). `√X` is `H S H`, exactly.
|
||
- Tested against the dense state vector after every gate of 40 random circuits
|
||
(2 to 8 qubits, over 2,000 gates). A mutant of the Hadamard update is caught.
|
||
- **`ProductMeasured::with_last`** keeps the graph form, order and steps with other closing
|
||
gates. **`ProductMeasured::scale`** multiplies every amplitude by a factor, folded into
|
||
one qubit's weights. Tested against fresh builds.
|
||
- **The service's `cutrank` method** returns amplitudes with the circuit's own global
|
||
phase.
|
||
- The factor is `e^{iΘ} a/b`. Here `a` is the tracked amplitude of the Clifford part,
|
||
`b` is the graph form's amplitude at the same basis state (closing gates set to the
|
||
identity), and `e^{iΘ}` is the phase the lowering to Clifford gates drops. That
|
||
phase is read gate by gate from each gate's matrix against its lowering's.
|
||
- Past about a minute of tracking (counted from the Hadamards), the result says
|
||
`global_phase` is not fixed, as before.
|
||
- `auto` now tries `cutrank` for amplitudes too, before `tensor_network`.
|
||
- Tests compare amplitudes with the state vector's directly (no phase aligned). The
|
||
circuits include phase-carrying lowerings: `rz`, `rx`, `ry`, `u`, `p`, `cp`, `rzz`,
|
||
`rxx`, `iswap`, `sx`, `sxdg`, `cy`, `swap`, `crz` and `cu`. Two mutants are caught:
|
||
the lowering phase dropped, and the tracked amplitude ignored.
|
||
|
||
### wai-quantum 0.6.8: threads only where they pay, and a cut-rank search that recounts only what moved
|
||
|
||
Additive. No result changes a bit: every kernel path gives the same bits at any thread count,
|
||
and every pinned digest is unchanged.
|
||
|
||
- **State-vector passes** (`quantum_sv`): a gate, a slice pass or a fixed-chunk sum now spawns
|
||
at most one thread per `2¹⁸` amplitudes. Each pass spawns its threads afresh, and below
|
||
that size a thread costs more than its share. On a shared 32-core host, a gate from 14 to
|
||
18 qubits ran 5 to 14 times slower with a thread per core than on one thread. A count that a
|
||
test forces is still taken as it is, so the split paths stay tested at small sizes. The
|
||
ignored `gate_timings` test prints the times.
|
||
- **The cut-rank search** (`ProductMeasured::anneal_order`) moves one qubit at a time. Seven
|
||
moves in eight go at most four places, and the rest anywhere. Only the cuts between the
|
||
two places are recounted, so a move costs about a ninth of a full recount. On the 70-qubit
|
||
graph-state circuit, 300 000 moves reach a 29-bit widest cut in about six seconds. In
|
||
tests, every move's recounted cuts are checked against a full recount.
|
||
- **The service's `cutrank` method:**
|
||
- The planner makes one short search, enough to bound the job's memory and work.
|
||
- The run searches up to 300 000 moves from each of four seeds, on threads of their own,
|
||
with the planner's order among the candidates.
|
||
- It keeps the cheapest order whose widest cut is no wider than the planner's, so a job
|
||
stays inside the memory it was admitted with.
|
||
|
||
### wai-quantum 0.6.7: tensor networks that split controlled gates and fix projections
|
||
|
||
Additive. `Network::amplitude` and `Network::simplify` are unchanged, so every pinned digest is too.
|
||
|
||
- **`Network::amplitude_split`** (`quantum_tn`) splits each gate on two or more qubits at
|
||
every qubit it is block-diagonal on: a control, or a qubit it is diagonal on. There
|
||
`G = Σ_a |a⟩⟨a| ⊗ G_a`, so that qubit gets the delta `δ(out, in, k)` on its wire, and the
|
||
blocks `G_k` keep the rest, joined by a bond of dimension 2. Only zeros of the matrix are
|
||
read. A cut through a controlled-Z or controlled-X then costs 2, not 4.
|
||
- **`Network::reduce`** fixes every index that some tensor allows only one value of: each
|
||
tensor holding it keeps its entries at that value and drops the index, until nothing
|
||
changes; then it simplifies. Only zeros are read and entries selected. Input and output
|
||
projections run through diagonal gates and split deltas, so a controlled-Z beside a
|
||
projected output becomes scalars. Open indices are never fixed.
|
||
- On 61-qubit controlled-Z circuits of depth 8, 12, 16 and 20, the search (64 trials) finds
|
||
single-amplitude trees of log₁₀ 5.35, 8.25, 11.22 and 14.26 multiply-adds on split, reduced
|
||
networks, against 8.03, 12.46, 16.09 and 19.64 on whole-gate, simplified ones.
|
||
- `examples/tn_cost`, the CLI's `wai-quantum tn` (`--whole-gates` for the old networks) and the
|
||
service's `tensor_network` method build split, reduced networks.
|
||
- Tests: split networks, reduced or not, and reduced whole-gate networks give the state
|
||
vector's amplitudes on circuits of controlled-Z, controlled-X both ways, a controlled
|
||
random unitary, random diagonal gates, Toffoli, CCZ and random two-qubit unitaries, with
|
||
outputs projected and open. CZ and CX split and a random unitary does not; CX splits at its
|
||
control. On a 4 × 5 grid of controlled-Z, the split tree costs under an eighth of the
|
||
whole-gate one. Three mutants are caught: the delta's entry, the block-diagonal test, and
|
||
the value kept by `reduce`.
|
||
|
||
### wai-quantum 0.6.6: Clifford circuits measured in a product basis, exactly, by cut-rank
|
||
|
||
Additive. Every pinned digest is unchanged.
|
||
|
||
- **`Tableau::graph_form`** (`quantum_tableau`) writes a stabilizer state as local Cliffords
|
||
(`H^h S^s Z^z` per qubit) on a graph state. Tested by rebuilding the state from the form
|
||
over 40 random circuits.
|
||
- **`quantum_cutrank`** (a new feature, in `full`) sums a Clifford circuit's state measured
|
||
after one closing layer of single-qubit gates at any angles:
|
||
`⟨x| ⊗ V_q |ψ⟩ = 2^{-n/2} Σ_z ∏_q W_q[x_q][z_q] (−1)^{Σ_{a~b} z_a z_b}`.
|
||
- The sum goes one qubit at a time along an order, in a table of `2^{cut-rank}` entries.
|
||
Each qubit is an in-place pass: a merge where the boundary space loses a dimension, the
|
||
qubit's signed terms, and a doubling or an XOR-shifted sum for its edges ahead.
|
||
- The passes depend on the graph and the order alone, so they are planned once per state.
|
||
- By Parseval, the table's norm is a prefix's probability, so the same passes sample
|
||
exactly: `amplitude`, `probability`, `sample`.
|
||
- `Precision::Single` stores the table in `f32` with `f64` arithmetic: half the memory and
|
||
traffic. `anneal_order` searches orders for less work (`work` = Σ 2^cut) and never
|
||
returns a worse one.
|
||
- Every reduction is summed in fixed chunks, so a result is the same bits at any thread
|
||
count.
|
||
- Tests:
|
||
- amplitudes equal the state vector's in probability and relative phase (30 circuits up
|
||
to 12 qubits);
|
||
- forward, reverse and shuffled orders agree (24 to 54 qubits);
|
||
- single-precision tables agree with double;
|
||
- samples follow the exact law and are seeded;
|
||
- annealing lowers the work and keeps the amplitudes;
|
||
- cut-ranks are the entanglement of GHZ and product states.
|
||
- Five mutants are caught: the merge's signs, the sampling choice, the doubling's sign,
|
||
the fold's coefficient, and the graph form without its S.
|
||
- **Examples:**
|
||
- `cutrank_qasm` reads an OpenQASM 2 Clifford bulk with a closing single-qubit layer, and
|
||
prints cut-ranks, annealed orders, amplitudes and exact samples. On a 70-qubit
|
||
graph-state sampling circuit, an annealed order keeps every cut at 29 bits or fewer: one
|
||
amplitude in about half a second, one exact sample in 1.1 to 1.5 s, with a largest
|
||
table of 4 GiB.
|
||
- `patch_xeb` scores measured shots of a patched circuit by patched cross-entropy, each
|
||
patch simulated exactly on the dense state vector.
|
||
- `tn_cost` prints the contraction cost of one amplitude as `find_path` finds it.
|
||
|
||
### wai-quantum 0.6.5 and the service: programs that measure in the middle
|
||
|
||
Additive. Every pinned digest is unchanged, and a program that measures only at the end
|
||
runs exactly as before.
|
||
|
||
- **`Tableau::measure`** (`quantum_tableau`) measures a qubit of the wide tableau and leaves
|
||
the state the outcome selects. A random outcome is the caller's to choose, so a branch
|
||
can be followed on purpose. The anticommuting stabilizer is multiplied into every other
|
||
row at once, each row's phase summed mod 4 in two bit planes. `Tableau::is_random` says
|
||
in one column scan whether an outcome is random.
|
||
- Tested against the state vector's projection, Pauli string by Pauli string, over 60
|
||
random circuits of six measurements each.
|
||
- On wide tableaux, measuring every qubit lands inside the support held before, with one
|
||
random outcome per dimension.
|
||
- Three mutants are caught: signs kept, the Y phase swapped, the destabilizer not moved.
|
||
- **`StateVector::probability_one` and `StateVector::collapse`** (`quantum_sv`): a qubit's
|
||
probability of reading 1, summed in fixed chunks, and the state a reading leaves.
|
||
- **The service runs dynamic programs.** It reads measurement and `reset` anywhere,
|
||
`if (c[0]) …`, `if (!c[0]) …`, `if (c == 3) { … } else { … }`, OpenQASM 2's
|
||
`if(c==n) gate;`, and `bit b = measure q;`.
|
||
- Such a program runs on `statevector` or `stabilizer`. Its shots split at each
|
||
measurement and reset as independent shots would, and each outcome runs on in its own
|
||
copy of the state.
|
||
- Expectation values are the exact mixture over the branches, of the state before the
|
||
final measurements.
|
||
- Tests: teleportation gives ⟨Z⟩ = cos θ and ⟨X⟩ = sin θ exactly. It agrees between the
|
||
tableau and the state vector, reset and re-measurement behave, and a bit-flip syndrome
|
||
corrects the flip. Four mutants are caught.
|
||
- Loops, noise in such a program, and amplitudes of one are refused with their reasons.
|
||
|
||
### `wai.live.moderation`: review policies, signed decisions, and statements of reasons
|
||
|
||
- **The extension** ([live-moderation](extensions/live-moderation.md)) registers the record
|
||
`wai.live.moderation`. The room authority signs a **review policy**, `WMP1`, chained to the
|
||
one it replaces and in effect from a room-clock instant: which event kinds and media are held
|
||
for review before delivery (pre) or reviewed after it (post), the reviewers, each one's
|
||
deadline and fallback (allow, deny or hold), the trusted flaggers it recognises, the reason
|
||
codes with their ground, provision or clause and database category, the territory, and the
|
||
redress text. Every policy keeps the room descriptor's review field (live-rooms §3.6).
|
||
- **Decisions.** A moderator's verdict is a `decision` event of `wai.live.events`, unchanged,
|
||
followed in its chain by a `meta` event carrying the **statement**, `WMS1`: its target (an
|
||
event, or a media object such as a recorded chunk named by its leaf), the target's `prev`,
|
||
the decision it supersedes, its basis (own initiative, a notice, a complaint, the policy's
|
||
fallback), its use of automated means in detection and in the decision, and its ground,
|
||
explanation and facts. Decisions on one target form a line by digest; an appeal's outcome
|
||
supersedes the decision complained of. The room's clock signs each fallback the policy
|
||
applies, at or after the deadline. A decision by a principal without the moderate power is
|
||
refused by the room verifier (`kind_not_permitted`).
|
||
- **Notices and complaints**, `WMN1`, are signed by whoever submits them: a notice names an
|
||
item, its ground and the submitter's statement and contact (Article 16(2)); a complaint
|
||
names a decision (Article 20). The contact is never exported.
|
||
- **Review and redaction.** Each item's state over time (held, pending, allowed, denied,
|
||
redacted, or the fallback at its deadline), what a relay holds and delivers, and what a sink
|
||
shows. A restricted item is withheld, never replaced: a gap it leaves in its author's chain
|
||
is explained by the moderators' signed statements, walking the chain back through each
|
||
withheld event's `prev`; a clip over a redacted chunk keeps its range proof, withholds the
|
||
chunk's bytes (`redaction_not_applied` when they are given), and presents the chunk again
|
||
once a later decision restores it. A recording package under moderation is specified only.
|
||
- **The statement-of-reasons export.** Each restricting decision maps onto one record of the
|
||
DSA Transparency Database's statement-of-reasons format (the schema in force from
|
||
1 July 2025, which its documentation calls API v2), written byte for byte: every member,
|
||
its source in the WAI records, closed lists and length limits, the sixteen categories, the
|
||
content types, and every Article 17 element with WAI's field for it. It carries no
|
||
principal key, notice contact or content; `source_identity` is never written. A record that
|
||
cannot be written is refused `sor_incomplete`, naming the member.
|
||
- **Twenty refusal reasons**, from `policy_malformed` to `redaction_not_applied`.
|
||
- **Reference and corpus.** `wai-rs` feature `live_moderation` (`src/live_moderation.rs`);
|
||
live-recording's clip verifier gains `verify_clip_exempting`, which `verify_clip` calls
|
||
exempting nothing, so its corpus is unchanged. `live-conformance/moderation/`, written from
|
||
fixed seeds by `wai_live_moderation_vectors`: a pre-moderated chat room whole and as a
|
||
subscriber receives it, a notice and a trusted flagger's notice with their decisions, an
|
||
appeal that reverses a decision, live-recording's lesson session with a chunk redacted and
|
||
then restored, one refused room per reason, an export refused, and clips of the redacted
|
||
take; each export a file of its own. Checked by `tests/live_moderation.rs` and by
|
||
`live-conformance/live_moderation_verify.py`, a second implementation that shares no code
|
||
with `wai-rs`, verifies each room and clip with its corpora's second implementations, writes
|
||
every export itself, and requires every mutated copy of the corpus to fail. The registry
|
||
lists the record and the magics `WMP1`, `WMS1` and `WMN1`.
|
||
|
||
### wai-quantum-service: every job's energy, signed with its result
|
||
|
||
- **What a result carries.** `energy` holds the CPU energy of the job's worker process, from
|
||
its start to its exit, inside the body the receipt seals. It is given as `joules_micro`,
|
||
with the acquisition class `OnChipCounter`, a declared uncertainty
|
||
(`relative_ppm`, `window_us`) and the counter's filtering state, in
|
||
[energy-measurement](extensions/energy-measurement.md) §2's names. It also states its
|
||
coverage and what it excludes.
|
||
- **How it is read.** On macOS, the service reads the worker's kernel count
|
||
(`proc_pid_rusage`, `ri_energy_nj`, plus energy system services billed to it) after the
|
||
worker has exited and before it is reaped, waiting with `waitid(WNOWAIT)`. An exited
|
||
process has left every core, so the count is complete. The declaration covers the
|
||
rounding to whole microjoules, and meets the §2.3 floor at every size.
|
||
- **What it does not claim.** A `statevector_single` job runs on the GPU, which the counter
|
||
does not read, so its figure is `partial`. A job the service cannot read is
|
||
`{"unmetered": why}`, never zero joules.
|
||
- **Agents.** The MCP summary states the figure and its coverage.
|
||
|
||
### `wai.live.steering`: each client's pathway, chosen by the joules its hops signed
|
||
|
||
- **The extension** ([energy-steering](extensions/energy-steering.md)) registers the record
|
||
`wai.live.steering`. The operator signs a **steering policy**, `WSP1`: the pathways (each a
|
||
pathway ID of the content description or a clone of one, through another host), each
|
||
pathway's hops (edge or interior, a hop in one pathway only), the quality floor, the latency
|
||
ceiling, each pathway's capacity and minimum share, the hysteresis margin, the TTLs, and the
|
||
period and window over which the hops' per-hop records (`WMH1`) are read. The new key-log
|
||
authority `live/steer` lets an operator key sign policies and decisions.
|
||
- **The evidence.** A pathway's figure is its hops' joules over the window, per media byte its
|
||
edge hops delivered or per microsecond of delivery at the floor tier or above, with the
|
||
records' common acquisition class. The window ends on the last period past a lag, and every
|
||
hop's records must tile it exactly, so every pathway is read over the same span and a
|
||
record left out makes the pathway **stale**, never cheaper. A pathway with an unmetered hop,
|
||
hops of two classes, stale records or no delivery has **no figure**: a decision that gives
|
||
one a figure is refused `unmetered_as_zero` or `classes_summed`.
|
||
- **The rule.** Pathways are ordered by class (calibrated instrument, counter, model,
|
||
estimate), within a class by figure, compared by exact products, and then by ID; a pathway
|
||
with no figure comes after every pathway with one, so not measuring is never rewarded. A
|
||
client with a short buffer or at its quality floor is held; a session's draw, fixed for the
|
||
session, places it on a pathway by that pathway's minimum share whatever its evidence; a
|
||
client moves off its pathway only for a figure lower by more than the margin. The output is
|
||
the priority, the TTL and the reason the first pathway is first.
|
||
- **The steering decision**, `WSD1`, names the policy, the records by digest, the client's
|
||
report (read from `_HLS_pathway` and `_HLS_throughput`, `_DASH_pathway` and
|
||
`_DASH_throughput`, and CMCD's `bl` and `mtp` in the query or the `CMCD-Request` header), the
|
||
server's session and loads, each pathway's evidence and limits, and the output; a verifier
|
||
recomputes all of it; ten refusal reasons.
|
||
- **The mappings.** The steering manifest of `draft-pantos-content-steering-04`, which HLS
|
||
(`draft-pantos-hls-rfc8216bis-22` §7) and DASH (ETSI TS 103 998 V1.1.1) read, written byte
|
||
for byte with `PATHWAY-CLONES`, a `RELOAD-URI` carrying the session, and one member,
|
||
`science.transaction.wai-decision`, naming the decision's URI, which players ignore; the
|
||
`EXT-X-CONTENT-STEERING` tag and the DASH `ContentSteering` element.
|
||
- **Reference and corpus.** `wai-rs` feature `energy_steering` (`src/energy_steering.rs`, with
|
||
an RFC 9651 parser of its own); `live-conformance/steering/`, written from fixed seeds by
|
||
`wai_energy_steering_vectors`, its figures synthetic, and checked by
|
||
`tests/energy_steering.rs` and by `live-conformance/energy_steering_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`, reads the room and the per-hop records
|
||
with their corpora's second implementations, recomputes every report, figure, decision and
|
||
manifest, and requires every mutated copy of the corpus to fail. The registry lists the
|
||
record and the magics `WSP1` and `WSD1`; jwp-receipts §7.5 lists `live/steer`.
|
||
|
||
### Live ingest and egress: WHIP and WHEP onto a room's tracks, E-RTMP v2 and SRT mapped
|
||
|
||
- **The extension** ([live-ingest](extensions/live-ingest.md)) binds the contribution and
|
||
playback protocols of standard encoders and players to the tracks of a room. No record
|
||
and no capability of its own; no new transport and no new token format.
|
||
- **Admission (§3).** A WHIP or WHEP bearer token is a room grant, `WRG1` in base64url. A
|
||
gateway checks it against the room's descriptor as live-rooms §4.3 does, then its window,
|
||
the room's revocations, its origins and its single-use nonce, and answers `401` with
|
||
RFC 6750's challenge; a good grant without the power a request needs (publish for each
|
||
media kind of a WHIP offer, subscribe up to the layer for WHEP) is `403`
|
||
`insufficient_scope`. Every request with a grant carries a proof of possession of its key
|
||
(live-rooms §10.5.3): `WAI-Room-Proof` over a single-use `WAI-Room-Challenge`, refused with
|
||
live-rooms' `pop_*` reasons.
|
||
- **Tracks and objects (§4, §5).** A WHIP session publishes a layer-0 media track per kind,
|
||
each with its receipts track, and the owner's catalog with a receipts track of its own.
|
||
Each frame is one object, one `WAI1` envelope in its capability's framing: an H.264 access
|
||
unit in Annex B, a VP8 or VP9 frame in one-frame IVF, an AV1 temporal unit in SPEC §5's
|
||
framing, one Opus packet. The objects of a live track, from a group's first, are one
|
||
stream of the codec. Video groups begin at keyframes; audio groups are fixed windows on
|
||
the room clock. A track's first group ID is its first frame's room-clock time, so a
|
||
publisher that reconnects continues above the IDs it used.
|
||
- **Time (§6, §7).** The manifest's new `live` member carries the frame's room-clock time,
|
||
its RTP time and, on a group's first object, the grant reference of live-rooms §4.5. RTP
|
||
time maps to the room clock from the track's first frame, re-anchored past `max_skew_ms`,
|
||
so every frame's time lies within that bound of its arrival at the gateway.
|
||
- **Receipts (§8).** The gateway signs each group's receipts as the first hop, after the
|
||
group's last object, chained per track on an alias derived per session. They state what
|
||
the gateway received, not what the publisher signed.
|
||
- **WHEP (§9).** An egress session reads the owner's catalog once its receipt verifies,
|
||
joins each track at its latest group, and verifies every object (its receipt's group and
|
||
Object ID, the signature under a trusted first hop, the chain, the inclusion proof, the
|
||
envelope) before the frame reaches the player; what fails is dropped and counted. A WHEP
|
||
player sees no receipt; a WAI-aware player subscribes over MoQ and verifies itself.
|
||
- **Mapped, not yet implemented (§12, §13).** E-RTMP v2 multitrack: `capsEx`, the stream
|
||
name as the token, tracks by the grant's profile layers, FourCCs to capabilities, the
|
||
reconnect request. SRT 1.5.7: a one-time session ID from an HTTP exchange in the stream
|
||
ID's `s` key, rejection codes from the HTTP statuses, MPEG-TS stream types to
|
||
capabilities, latency.
|
||
- **How an ingest relates to a take (§15)** and **to metering (§16)**: an ingest is the live
|
||
tier, never a `single-take`; a device that speaks capture binding still commits its take
|
||
beside its WHIP session. Each gateway session ends with a signed record in the shape of
|
||
live metering's per-hop record.
|
||
- **Reference.** `joule-moq-ingest` in `joule-code/joule-code-rs/crates/`, on `str0m`
|
||
(sans-IO WebRTC, its pure-Rust crypto backend). Its reading of rooms and grants is held to
|
||
`live-conformance/rooms/`.
|
||
|
||
### `wai.video.vp8` and `wai.audio.opus.packet`: the media of a WebRTC ingest
|
||
|
||
- **Why.** A live ingest of WebRTC media hands WAI the codecs WebRTC endpoints send.
|
||
VP8 is mandatory to implement in every WebRTC browser, and in every WebRTC
|
||
non-browser that sends or receives video (RFC 7742 §5); a live track carries
|
||
Opus one packet per object, not as an Ogg stream. SPEC §5 *Broadcast and
|
||
streaming codecs* registers both, 2026-10-18, with no edition named.
|
||
- **`wai.video.vp8`** (RFC 6386): VP8 frames in the IVF frame container with
|
||
FourCC `VP80`, a 32-byte file header and a 12-byte header before each frame, the
|
||
framing `wai.video.vp9` uses. Royalty-free, as VP9 (the reference source under a
|
||
BSD licence with a patent grant). `StandardUnclassified`: RFC 6386's
|
||
decoder-conformance text was not read, so the registry promises no byte identity;
|
||
VP8 shares VP9's framing, not its class.
|
||
- **`wai.audio.opus.packet`** (RFC 6716): one Opus packet (§3), its TOC byte first,
|
||
with no container, decoded with an Opus decoder (§4) of one or two channels at any
|
||
rate it supports. It carries no pre-skip and no output gain, which are Ogg Opus's
|
||
(RFC 7845 §5.1); `wai.audio.opus` remains the capability for a stored stream. A
|
||
different framing takes a string of its own (§8), suffixed as
|
||
`wai.video.av1.lossless` is. Royalty-free; `StandardTolerance`, as `wai.audio.opus`
|
||
(RFC 6716 §6). How consecutive objects of one live track decode is the live
|
||
binding's rule ([live-ingest](extensions/live-ingest.md)).
|
||
- **Neither is a default.** Both are standalone, `Lossy`, sink-supplied, and outside
|
||
the floor and the recommended set; the native sink advertises neither. Neither is a
|
||
base of `wai.video.lcevc`. The codec glossary links its VP8 and Opus entries to
|
||
them, and the research registry records the licence and replicate basis of each
|
||
(`research/media-codec-registry.md`, ¹⁵). The registry export, the vocabulary and
|
||
the table in `REGISTRY.md` are regenerated.
|
||
|
||
### Live delivery hardening: receipt chains, the receipts' binary form, room track names, proof of possession
|
||
|
||
- **A join's fill from the chain's first group.** [jwp-receipts](extensions/jwp-receipts.md)
|
||
§3 step 3: below a join's anchor a group with a `null` parent is the chain's true start,
|
||
`parent_not_held` and held as history until the group above it links back to it, not a
|
||
second chain head (`chain_break`). A `null` parent outside such a gap, and a fork, are
|
||
still `chain_break`. `wai-rs`, `jwp_verify.py` and two corpus vectors
|
||
(`chain_join_fetch_from_group_0`, `chain_join_second_start_after_the_fill`, §5.9).
|
||
- **The receipts' binary form.** jwp-receipts §2.7 (new): group and object receipts, and a
|
||
group's receipts together (`WJR1`), as bytes. A group is its canonical bytes (§3 step
|
||
3), its link hash computed over them as received; an object its members in fixed widths.
|
||
Reading yields the JSON form's members, and §3 applies to them unchanged; §2.7.4 says
|
||
what does not read. [moq-streaming-format](extensions/moq-streaming-format.md) §4.1: a
|
||
receipts-track object carries this form, not JSON. 20 vectors (procedure `binary`,
|
||
§5.12), read and written byte for byte by `wai-rs` (`jwp_delivery::binary`),
|
||
`jwp_verify.py` and `joule-moq`.
|
||
- **Room track names.** [live-rooms](extensions/live-rooms.md) §6.1 names a media track's
|
||
receipts track (its name followed by `_receipts`) and a publisher's catalog track (the
|
||
room and owner fields, Track Name `catalog`); §6.2 ties the catalog's
|
||
`wai_receipts_track` to it; §10.2's `moqt` scopes admit both. The rooms corpus's scopes
|
||
change accordingly.
|
||
- **Proof of possession.** live-rooms §10.5 (new): a grant is public, so every binding
|
||
honours it only beside a `WRP1` proof by its principal over a fresh challenge the
|
||
verifier chose and a context naming the endpoint (a verb and `//authority/path`). Over
|
||
MOQT the relay's SETUP carries the challenge (`GRANT_CHALLENGE`) and the client's the
|
||
grant and the proof (`GRANT_PROOF`); a SETUP whose grant has no proof that passes is
|
||
closed `UNAUTHORIZED`. Over HTTP (WHIP and WHEP's Bearer token) the proof rides
|
||
`WAI-Room-Proof`, and single-use challenges `WAI-Room-Challenge`. §10.5.4's checks
|
||
add eight reasons to §11 (`pop_missing` to `pop_wrong_context`); the CAT mapping and
|
||
§13 follow. The rooms corpus gains 17 proofs, MOQT and HTTP; `live_rooms_verify.py`
|
||
checks them from the text.
|
||
- **Registry.** Wire identifiers `WJR1`, `WRP1`, the Setup Options `GRANT_CHALLENGE` and
|
||
`GRANT_PROOF`, and the HTTP fields `WAI-Room-Proof` and `WAI-Room-Challenge` (two new
|
||
kinds, `setup-option` and `http-header`), the last four proposed and not registered.
|
||
|
||
### `wai.live.metering`: a room session's units and joules per hop
|
||
|
||
- **The extension** ([live-metering](extensions/live-metering.md)) registers the record
|
||
`wai.live.metering`. Every hop that carried a room session, a relay, a compositor or an
|
||
ingest gateway, signs one **per-hop session record**, `WMH1`: per track and direction the
|
||
objects and bytes it carried and the objects it dropped by reason (delivery timeout,
|
||
refused at the door, duplicate, queued at the end) with those whose receipts it
|
||
forwarded; its subscriber sessions' time by layer class; the events it delivered; its own
|
||
energy with the acquisition class of energy-measurement, or the unmetered marker; and the
|
||
root of a **ledger**, one entry for each grant its sessions ran under, with that grant's
|
||
joule and funds ceilings. Media tracks are counted one by one, events tracks and every
|
||
other track of the room together, subscribers by layer class, so a record is at most
|
||
552 + 180 × (*M* + 2) bytes, *M* the media tracks the room published, whatever the
|
||
audience; a record past that is refused before it is read.
|
||
- **The room-session receipt**, `WMR1`, signed by the room authority or by a key the
|
||
origin's key log lists with the new authority `live/meter` (and `energy/<class>` for each
|
||
class it totals): a key-log authority rather than a grant power, since a receipt states
|
||
every principal's consumption and a grant's powers are live-rooms' closed set. It names
|
||
the per-hop records, the takes and the composition receipts it aggregates and states
|
||
publisher-seconds, subscriber-seconds by layer class, composition-seconds, events sent
|
||
and delivered and recorded-seconds (the capture each device committed while capturing),
|
||
every hop's joules with its class, one total per class and never one across classes
|
||
(refused `classes_summed`), and each grant's consumption: its units, a share of each
|
||
metered hop's figure by the bytes it moved, labelled with that figure's class, its funds
|
||
under a stated tariff, and its verdicts against its ceilings, committed by a consumption
|
||
root and every grant past a ceiling named. A verifier recomputes every number from the
|
||
records it names; eighteen refusal reasons.
|
||
- **QoE.** Clients' delivery-telemetry observations, as CMCD version 2 Event-mode reports,
|
||
ride the CMCD event timeline of `draft-wilaw-moq-cmcd-event-timeline-00` (`T` the
|
||
report's `ts`, values as strings, `sf` and the custom keys not carried), or CTA-5004-B's
|
||
`application/cmcd` batch, whose line syntax the reports already have. Only a cell of at
|
||
least fifty sessions enters the receipt, suppressed below that; the observations never
|
||
leave the session and the room's service.
|
||
- **C2PA.** `c2pa_emit` (with `live_metering`) writes a receipt into a real signed manifest:
|
||
`c2pa.environmental-sustainability` with `energy_kwh` only where one class covers the
|
||
session whole, GHG and water only when the caller supplies them with their method, and
|
||
always the companion `science.transaction.wai.live.metering` with the totals by class.
|
||
- **The reference relay** signs the per-hop record of a room from its session summaries
|
||
(joule-code's CHANGELOG), and the corpus holds a record it signed in its test.
|
||
- **Reference and corpus.** `wai-rs` feature `live_metering` (`src/live_metering.rs`;
|
||
`capture_take::find_take` exposes capture-binding §9.2 to it); `live-conformance/metering/`,
|
||
written from fixed seeds and the relay's recorded record by `wai_live_metering_vectors`, and
|
||
checked by `tests/live_metering.rs` and by `live-conformance/live_metering_verify.py`, a
|
||
second implementation that shares no code with `wai-rs`, verifies rooms, takes and
|
||
composition receipts with their corpora's own second implementations, recomputes every
|
||
unit, total and consumption entry, and requires every mutated copy of the corpus to fail.
|
||
The registry lists the record and the magics `WMH1` and `WMR1`.
|
||
|
||
### wai-quantum 0.6.4: noisy Clifford circuits by Pauli frames, the state vector on the GPU, and the density matrix by superoperators
|
||
|
||
Additive. Every pinned digest is unchanged.
|
||
|
||
- **`DensityMatrix::evolve`** (`quantum_noisy`): the exact noisy state on up to 16 qubits
|
||
(`MAX_EVOLVE_QUBITS`), against 12 for `DensityMatrix::run`, which stays as the referee.
|
||
- Each gate goes on `ρ`'s rows, its conjugate on the columns. Each channel becomes one
|
||
superoperator `Σᵢ wᵢ Kᵢ ⊗ Kᵢ*` on its qubits' row and column bits, in `run`'s order.
|
||
- A gate and its noise fuse into one gate on at most four of the vector's qubits, applied
|
||
in the state vector's tiled passes. A gate therefore costs one pass and no copy of `ρ`.
|
||
- **Checked:** against `run`, every entry within 10⁻¹² on circuits with every channel and
|
||
a Toffoli. At 14 qubits, past `run`'s size, against branching trajectories within four
|
||
standard errors.
|
||
- **Measured** on a 16-core desktop processor: 12 qubits in 0.53 s against 158 s for
|
||
`run`, and 10 qubits in 39 ms against 4.1 s. 15 qubits with 132 noisy gates take 49 s.
|
||
- **The service's `density_matrix`** runs on it, up to 16 qubits within a job's memory and
|
||
a work cap. `auto`, with noise, still tries it first.
|
||
- **`quantum_spd::expectation_capped`**: the sparse-Pauli engine held to a number of terms.
|
||
Whenever a rotation leaves more, the threshold doubles and the sum is truncated again
|
||
until it fits, and everything dropped that way joins the bound. A run that never reaches
|
||
the cap is the uncapped run, bit for bit.
|
||
- **Checked:** on the 127-qubit kicked-Ising circuit, a cap above the peak changes no bit.
|
||
A cap a tenth of the peak stays within its bound of the uncapped value. On a small
|
||
register, the bound covers the exact error.
|
||
- **The service's `pauli`** holds as many terms as the job's memory allows (`max_terms`
|
||
can lower it). A sum that would have outgrown the job now finishes at a coarser
|
||
threshold, reported as `threshold_reached`, with its bound.
|
||
- **The tensor-network executor blocked and threaded** (`quantum_tn`). Each pairwise
|
||
contraction is a complex matrix product blocked for the cache, its rows shared between
|
||
threads. Slices running in parallel keep their contractions on one thread each. Every
|
||
output adds its terms in the same order, so the result is the same bits (a test holds it
|
||
to the plain loop's, and the pinned digests hold).
|
||
- **Measured:** large products went from 2.0 to about 25 billion complex multiply-adds a
|
||
second on a 16-core desktop processor (1024 × 1024 × 1024: 0.52 s → 0.043 s).
|
||
- **Faster state-vector kernels** (`quantum_sv`). Gates on two to five qubits run at
|
||
compile-time sizes, several groups side by side, with groups of low strides gathered.
|
||
Every output is the same sum in the same order, so the state is the same bits.
|
||
- Measured in cache on one core of a desktop machine: two-qubit gates 280–345 → 400–460
|
||
million amplitudes a second, and dense three- and four-qubit gates 10–25% faster.
|
||
- On a 16-core desktop processor, dense three- to five-qubit gates are 35–50% faster.
|
||
|
||
- **`quantum_sv_metal`** (new feature, macOS): the dense state vector in single precision
|
||
on the platform's GPU, in one buffer the GPU and the host share. Half the bytes of the
|
||
double-precision state, so one more qubit fits.
|
||
- Gates are fused as `quantum_sv` fuses them, then grouped into tile passes. Each
|
||
threadgroup copies 4096 amplitudes into threadgroup memory, applies every gate of the
|
||
pass there between barriers, and copies them back.
|
||
- A kernel is built per group size, so each group stays in registers. The kernels are
|
||
compiled with the platform's safe math mode. Norms, expectations and samples are summed
|
||
on the host in double precision.
|
||
- **Checked:**
|
||
- Against the double-precision state vector on random circuits of 6 to 21 qubits at
|
||
every fusion width: amplitudes within 2·10⁻⁶, expectations within 10⁻⁵.
|
||
- Tiled and untiled runs are the same bits, and repeated runs are the same bits.
|
||
- **Measured** on a desktop machine's GPU, with the machine heavily loaded by other work:
|
||
a 30-qubit, depth-20 brickwork of 910 gates runs in about 4.2 s at fusion width 3 with
|
||
tiles, against about 8 s gate by gate.
|
||
- **The service** offers it as `statevector_single`, accuracy class `single`, where a GPU
|
||
is present. `auto` tries it after the double-precision `statevector`, so it takes the
|
||
circuits that one cannot hold: up to 33 qubits within a job's memory.
|
||
- **Measured** through the public service, on a host under heavy load:
|
||
- a 33-qubit job (a 64 GiB state, 97 gates, chosen by `auto`) in 70 s;
|
||
- 31 qubits in 23.5 s;
|
||
- 26 qubits in 2.1 s, against 10.1 s for the double-precision method. The expectation
|
||
values agree to 10⁻⁸.
|
||
|
||
- **`quantum_tableau::frame_counts` and `frame_expectations`.** Clifford circuits under Pauli
|
||
noise (`FrameStep`: depolarizing on one or two qubits, dephasing; readout flips), on up
|
||
to 32768 qubits.
|
||
- One noiseless outcome from the tableau is the reference. Each shot carries a Pauli
|
||
frame, the difference its noise makes, 64 shots to a machine word.
|
||
- Frames start with random Z bits. They leave `|0…0⟩` unchanged, and carried through the
|
||
circuit they spread each shot uniformly over the noiseless outcomes.
|
||
- Errors are placed by geometric gaps, with the crate's own logarithm.
|
||
- An expectation value is the noiseless value times the frames' mean sign against the
|
||
string, with its standard error.
|
||
- Both readouts are the same bits at any thread count.
|
||
- **Checked:** on random Clifford circuits of 3 to 5 qubits with every channel, the counts
|
||
(200,000 shots, with readout flips) and the expectation values agree with the exact
|
||
density matrix within five standard errors. Two mutations are caught: dropping one qubit
|
||
of the two-qubit channel, and dropping the random Z bits.
|
||
- **Measured** on a 16-core desktop processor: 100,000 full-width shots take 44 ms at 1,000
|
||
qubits with 10,000 noisy gates, and 1.6 s at 10,000 qubits with 100,000.
|
||
- **The service's `stabilizer` method takes Pauli noise.** Each QASM gate gets its noise on
|
||
the qubits it touched, as the other noisy methods apply it. Expectations are estimated
|
||
from at least 10,000 shots unless `trajectories` says otherwise. Amplitude damping is not
|
||
a Pauli channel and is refused. With noise, `auto` tries `density_matrix` (12 qubits,
|
||
exact), then `stabilizer`, then `trajectories`.
|
||
- **The OpenQASM writer keeps every gate** (`quantum_toolchain::to_qasm`). A gate with
|
||
three or more controls, such as Grover's oracle and diffusion past three qubits, was
|
||
written as a comment, so the program written out was not the circuit. Every controlled
|
||
gate without a standard name is now written in OpenQASM 3's modifier form,
|
||
`ctrl(n) @ g`, and `from_qasm` reads `ctrl`, `ctrl(n)`, `negctrl` and `negctrl(n)` back.
|
||
- **Checked:** each base gate under zero to five controls is read back op for op, and
|
||
Grover's search on five and seven qubits keeps its state.
|
||
- **The service** (`wai-quantum-service`, not published):
|
||
- It reads the control modifiers on `x y z h s sdg t tdg p` and on `cx cy cz ch cp ccx
|
||
ccz`. A gate with a standard name is read as that gate, so a Clifford program keeps
|
||
the stabilizer method. A phase on m qubits is written exactly with 2^m − 1 `cx` and
|
||
as many `p`, its angles dyadic when the phase is. **Checked:** on a 6-qubit state,
|
||
every amplitude against the gate applied directly where the controls hold, within
|
||
10⁻¹², each test failing when the expansion's signs or its releases are broken.
|
||
- Receipts sign the RFC 8785 canonical text of the request and the result, so a result
|
||
verifies after any JSON reader has carried it, a browser's included. Its numbers are
|
||
checked against a JavaScript engine on RFC 8785's appendix B and on 2 million random
|
||
doubles. Receipts made before it still verify.
|
||
- It reads numbers correctly rounded (serde_json's `float_roundtrip`). Before, a result
|
||
could change in its last bit on its way from the job's process to the reply.
|
||
- A job carries its request as submitted, so whoever holds its id can check its receipt.
|
||
|
||
### `wai.image.jpegai`'s hyper decoders, context stages and synthesis through the integer operator set
|
||
|
||
- **§13.6.2 is normative.** [integer-operators](extensions/integer-operators.md) §13.6.2
|
||
writes the topology of the capability's other integer networks as four `WHG1` graphs,
|
||
lowered by `jpegai-int/1` from the bundle's eight weight files in §13.0's canonical
|
||
form: `rec_y` and `rec_uv` (each plane's hyper decoder, and for luma its four context
|
||
stages, from the hyper-latent and the dequantised residual to the reconstructed
|
||
latent) and `syn_y` and `syn_uv` (the synthesis). §1's claim widens to what these
|
||
networks give for their inputs. The independent checker (`tools/wai_ops_check.py`),
|
||
written from the text alone, reproduces every lowered graph and certificate byte for
|
||
byte.
|
||
- **Ranges.** Every activation an operator can grow, and every input but the
|
||
hyper-latent, is checked within i32 (`A32`), so every sum is bounded by the weights.
|
||
On the test codestream inputs stay within 21 bits, activations within 24, and the
|
||
largest sum is 37 bits against certified bounds of 42 to 60: `acc64` holds for every
|
||
sum, and no wider accumulator is needed (§13.6.2, *Accumulators, measured*).
|
||
- **Profiles.** The `ops` member is `{ wai-ops/1, acc64, full, jpegai-int/1,
|
||
role_profiles: { hs_y: acc32, hs_uv: acc32 } }`: the set meets `acc64`, and its scale
|
||
path still `acc32` (§11's new `role_profiles`; both certifiers take
|
||
`--role-profile`). The certificate of `jpegai/v1` (`3e243dc1…`) holds the six graphs.
|
||
- **The switch.** `jpegai_decode::{decode, decode_with_stages}` run the hyper decoders,
|
||
context stages and synthesis through the operator set (`jpegai_ops::Networks`,
|
||
`jpegai_decode::load_networks`); `decode_loaded` takes the scale path and the networks
|
||
loaded once, with the eICCI net. Every committed and pinned output is unchanged: the
|
||
`Synthesis` stage hash and every other stage hash of `tests/jpegai_cross_platform.rs`,
|
||
and the decoded RGB SHA-256 that `wai-jpegai-e2e.yml` pins. The direct networks run
|
||
only in `jpegai_decode::legacy`, test-only (`legacy_int`). The neural sink lowers and
|
||
loads the networks from the members' bytes through the store of loaded sets.
|
||
- **No loader panics.** `Weights::from_json`, `IntNet::from_json` and
|
||
`IcciNet::from_json` return a `Result` (`jpegai_synthesis::BadMember`); a mistyped
|
||
member is refused `model_mismatch` where it panicked, and the sink reports that
|
||
refusal rather than decoding through the float path.
|
||
- **Planned breaks** (integer-operators §15.3 item 4, SPEC §8): a weight file that
|
||
breaks §13.6.2's member rules or shapes, or networks whose certificate does not meet
|
||
`acc64` (`model_mismatch`); a dequantised residual, a latent or an activation of the
|
||
networks past i32 as a Q16 value (`field_range`), where the decoder computed on. No
|
||
committed bundle or codestream reaches either.
|
||
- **CI.** `tests/jpegai_nets_ops.rs` runs in both fast-kernel steps of
|
||
`wai-conformance.yml` on the committed synthetic networks, and in `wai-jpegai-e2e.yml`
|
||
on `jpegai/v1`, each pinned case required by name; the differential gains the
|
||
networks' half, per push and nightly.
|
||
|
||
### wai-quantum 0.6.3: the wide stabilizer tableau, matrix product states at any angle, and fewer passes for the state vector, noisy shots and sparse Pauli sums
|
||
|
||
Every pinned digest is unchanged, and the state vector and the sparse-Pauli engine give the
|
||
same bits as before.
|
||
|
||
- **`quantum_tableau`** (new feature, in `full`): Clifford circuits on up to 32768 qubits.
|
||
The Aaronson–Gottesman tableau is packed qubit by qubit, with 32-bit qubit indices, so a
|
||
gate costs a few word operations per 64 rows.
|
||
- **Outcomes:** `Tableau::support` computes the affine subspace the computational-basis
|
||
outcomes are uniform over, by Gaussian elimination on the stabilizers with their signs
|
||
tracked. A `Sampler` then draws each shot as a random combination of its basis,
|
||
projected onto the measured qubits.
|
||
- **Expectation values:** `Tableau::expectation` gives the exact ±1/0 of a Pauli string,
|
||
from the destabilizers.
|
||
- **`quantum_mpsim`** (new feature, in `full`): matrix-product-state simulation of circuits
|
||
with any angles, on up to 4096 qubits.
|
||
- The chain is kept in mixed-canonical form, the center moved by QR. A gate on distant
|
||
qubits is routed by swaps.
|
||
- A two-qubit gate is a two-site update. It keeps the leading eigenvectors `U` of `M·M†`
|
||
and makes `U†·M` the new center, so no singular value is divided by. A matrix taller
|
||
than wide is first factored `M = Q·R`. The update is truncated to the bond dimension and
|
||
a cutoff.
|
||
- The eigensolver is new in `linalg`: Householder reflections to a real tridiagonal form,
|
||
then QL with implicit shifts, in a fixed order with only `+ − × ÷ √`. Shares below 10⁻¹²
|
||
of an update's weight are its rounding and are always dropped. An update that loses
|
||
under 10⁻¹³ of its weight is not counted as a truncation.
|
||
- Every truncation is reported: the bonds used, the summed discarded weight, and the
|
||
fidelity estimate `Π(1 − εᵢ)`.
|
||
- Readouts: amplitudes, Pauli expectation values by transfer matrices, and shots by
|
||
sequential conditional sampling.
|
||
- **Measured:**
|
||
- With nothing truncated, every amplitude and expectation agrees with the dense state
|
||
vector to 1e-10, and sampled frequencies match the probabilities. Under truncation,
|
||
the estimate tracks the true fidelity.
|
||
- On a 16-core desktop processor, on nearest-neighbour brickworks, against the crate's
|
||
one-sided Jacobi split: 500 qubits at depth 10 take 0.75 s against 3.1 s, and depth
|
||
20 at bond 64 takes 8.1 s against 35.9 s.
|
||
- At depth 20 the estimate reports what truncation cost: 0.0426 at bond 64 and 0.4712
|
||
at bond 128, the same under both splits.
|
||
- **Measured** on a random Clifford circuit of 10n gates at 10,000 qubits: the gates in
|
||
49 ms, the outcome subspace in 353 ms, 1000 full-width shots in 154 ms, one expectation
|
||
value in 27 ms. Over 40 random circuits on up to 10 qubits, the outcome set and its
|
||
uniform probabilities match the dense state vector's, as do 800 Pauli expectation values.
|
||
- **Tiled state-vector passes** (`quantum_sv`). `StateVector::run` now groups consecutive
|
||
gates into passes, as a `Schedule` sets (`run_scheduled`; `Schedule::UNTILED` gives a pass
|
||
per gate). Each tile holds the amplitudes that agree on every bit outside the pass's local
|
||
bits. A tile is copied into a buffer the size of a core's cache, every gate of the pass is
|
||
applied to it there, and it is copied back.
|
||
- A pass's dense gates act on its local bits. Diagonal gates may act on any bit.
|
||
- Gates keep their order, and each amplitude goes through the same expressions as before,
|
||
so the schedule never changes a bit. A test holds tiled and untiled states equal bit for
|
||
bit at several tile and run sizes, at one thread and many.
|
||
- A gate of three or more qubits on the top qubits used to fall back to one thread; it now
|
||
runs in tiles. Gates on two or more qubits whose lowest stride allows it update eight
|
||
groups side by side.
|
||
- **Measured** on a 16-core desktop processor, 26 qubits, double precision:
|
||
- a depth-20 brickwork of random rotations and fSim gates: 5.2 s → 2.3 s at fusion width 2;
|
||
- a QFT: 2.9 s → 1.6 s at width 3;
|
||
- a QAOA circuit: 2.9 s → 0.95 s.
|
||
- **Branching trajectories** (`quantum_noisy::branching_trajectories`). The shots of a noisy
|
||
circuit have the law of independent trajectories, but shots that took the same errors
|
||
share one state.
|
||
- At each noise event a group splits as independent shots would. The number that err is
|
||
binomial (drawn by geometric gaps, so the cost is the number of errors, not of shots),
|
||
and each erring shot chooses its Pauli uniformly. A damping event's jumps are binomial
|
||
in the group's own jump probability. The error-free prefix and the error-free majority
|
||
therefore run once.
|
||
- The largest branch carries on in place. Other branches run first in copies while fewer
|
||
than a stated number of states are alive; past that, a branch waits and is replayed.
|
||
Estimates, counts and histories depend on neither the thread count nor the state limit.
|
||
- **Checked:** expectations land within four standard errors of the exact density matrix
|
||
for three seeds, and counts with readout flips follow the exact distribution.
|
||
- **Measured**, 18 qubits and 300 shots of a brickwork circuit, against independent
|
||
trajectories: 28 times faster at two-qubit error 10⁻³, 5.6 times at 5·10⁻³, and 1.9
|
||
times at 2·10⁻² with damping.
|
||
- **The sparse-Pauli engine on a worker pool** (`quantum_spd`). A large sum is updated by
|
||
workers started once per run, each taking pieces of the table as it frees up.
|
||
- A run of consecutive Clifford gates is applied in one pass, each term through the gates
|
||
in order.
|
||
- A rotation's new terms have distinct strings, so each existing coefficient receives at
|
||
most one addition. The rest are appended in table order, and dropped magnitudes are
|
||
summed in table order.
|
||
- The thread count therefore changes the time and never a bit: a test compares one, two,
|
||
three and eight threads bit for bit. `expectation_threaded` takes an explicit count.
|
||
- **Measured:** the 127-qubit kicked-Ising run (20 steps, threshold 10⁻⁴, 2.2 million terms
|
||
at its peak) takes 9.4 s, against 49 s on one thread, with the same value.
|
||
|
||
- **The tensor-engine member's kernel compiles for a device.**
|
||
- **The kernel compiled for the simulator only.** The helper
|
||
(`quantum_fabric/tensor_engine_helper.py`) defined its kernel inside `main()` and
|
||
imported the kernel toolchain there. The simulator runs the kernel as Python, so it found
|
||
the names. Compiling for a device traces the kernel's source and looks its names up in
|
||
the kernel's module, so it found none and refused the first request (`failed to resolve
|
||
name`). On a device every test of the member failed at that request. The kernel now sits
|
||
at module level. `main()` still imports the toolchain after the target is chosen, but
|
||
binds it as module globals.
|
||
- **The compiler is found beside the interpreter.** Compiling for a device runs the
|
||
toolchain's compiler from `PATH`, and the interpreter the member is given need not be on
|
||
it. The helper now puts that interpreter's directory first on `PATH` for a device target.
|
||
- **Checked** off the device with the toolchain's own compiler: the kernel now compiles
|
||
for the device target. Execution there needs the device's runtime. The simulator path is
|
||
unchanged.
|
||
|
||
### wai-quantum-service 0.1.0: the quantum stack as a public job service
|
||
|
||
- **What it is** (`wai/wai-quantum-service`): an HTTP job service over wai-quantum. A job is
|
||
an OpenQASM 3 or 2 program and the outputs wanted: measurement counts, Pauli expectation
|
||
values, or amplitudes of named bit strings, optionally under a noise model. The service
|
||
answers with the method that ran, its accuracy, and a receipt. No account and no key.
|
||
The public instance is at `https://wai.transaction.science/quantum-jobs/v1`.
|
||
- **The OpenQASM front end** (`src/qasm.rs`) reads:
|
||
- registers, and broadcast over them;
|
||
- user `gate` definitions, inlined with their parameters;
|
||
- angle expressions;
|
||
- the standard gate libraries;
|
||
- every terminal-measurement form.
|
||
|
||
Mid-circuit measurement, classical control and gate modifiers are refused, with their
|
||
line. It lowers one circuit three ways, each exact up to a global phase: to matrix gates
|
||
(the dense state vector, tensor networks, noise), to Pauli rotations (sparse Pauli
|
||
dynamics) and to the dyadic gate set (the bit-exact state vector, the stabilizer tableau).
|
||
The tests hold each lowering to the others on every gate.
|
||
- **Seven methods, and `auto`:**
|
||
- `exact` (bit-exact, dyadic angles, 26 qubits);
|
||
- `statevector` (exact, any angles, up to the memory a job has: 32 qubits);
|
||
- `stabilizer` (Clifford, 32768 qubits, on the wide tableau below: counts and exact
|
||
expectation values);
|
||
- `pauli` (bounded, 1024 qubits, each value with its truncation bound);
|
||
- `mps` (any angles, 4096 qubits, at bond dimension up to 256: exact when nothing is
|
||
truncated, otherwise heuristic with its fidelity estimate and a warning below 0.9);
|
||
- `tensor_network` (exact amplitudes, with the contraction's cost);
|
||
- `density_matrix` (exact noise, 12 qubits);
|
||
- `trajectories` (noise as an estimate with standard errors).
|
||
|
||
`statevector` runs in tiled passes. `trajectories` runs every shot as a branching
|
||
trajectory and reports how many distinct error `histories` ran. `pauli` reports a
|
||
`ladder`: each value and bound at up to three thresholds a decade above the one asked
|
||
for (`"ladder": false` turns it off). `auto` picks the first that takes the job, and the plan says why, and why not the others.
|
||
- **A public service on a shared host:**
|
||
- every job is planned before it is queued, and one whose memory estimate is over the
|
||
per-job cap is refused at once;
|
||
- jobs run inside a total memory budget, and a large one waits until the host's free memory
|
||
covers it and a reserve;
|
||
- each job runs in its own child process, at a lower priority than the host's other work,
|
||
watched for its resident memory and its time, and stopped past either;
|
||
- clients get per-client active-job and submission limits, against a bounded queue.
|
||
|
||
The limits are stated at `GET /v1`.
|
||
- **Receipts:** every finished result is sealed with the service's Ed25519 key over the
|
||
BLAKE3 digest of the request and the result; `POST /v1/verify` checks one.
|
||
- **Measured:** 29 tests on Linux and macOS, and `smoke.sh` against local instances and the
|
||
public one. That covers a 40-qubit GHZ by the stabilizer, receipts that verify while a
|
||
changed result does not, a 100-qubit circuit's expectation values by sparse Pauli dynamics
|
||
with their bounds and ladder, 2000 noisy shots of a 14-qubit circuit by branching
|
||
trajectories, refusals with reasons, the per-client limit, and a job stopped when it
|
||
outgrew its memory. CI: `wai-quantum-service` (Linux and the macOS runner).
|
||
|
||
### `WIV1` and `WIR1` through the integer operator set's stage operators
|
||
|
||
- **`ops` members with no graph.** `wai.video.int_motion` and
|
||
`wai.{image,video,audio,splat}.int_refine` decode through the stage operators of
|
||
[integer-operators](extensions/integer-operators.md) §13.5 alone, and register
|
||
`{ wai-ops/1, stage: rans64 t4097/lenient/canonical }`: with no parameter set, the
|
||
member has no profile, subset or lowering (§11; the registry schema now requires
|
||
`set`, those three together, and a lowering or a stage).
|
||
- **The stage operators the drivers need.** `wai-ops`' E1 decodes a symbol against a
|
||
table in whatever width a payload's parser keeps it (`Rans::symbol_in`, and
|
||
`table_ok` reads its entries likewise), and E10, E11 and E12 gain per-element forms
|
||
(`mc_predict_at`, `up2_at`, `rate_at`) that read a frame, a picture or source samples
|
||
in the driver's layout, so a decode forms no copy.
|
||
- **The switch.** `int_video::{decode_typed, decode_pframes_typed}` decode `WIV1`
|
||
through E1, E10 and E9 (`int_video::ops`), and `staged::wir1::apply_unit` and
|
||
`Wir1::check_streams` decode `WIR1` through E1, E11 or E12, and E9
|
||
(`staged::wir1::ops`). Every committed output is unchanged: the `int_video` clips
|
||
(the half-pel real footage among them) and their SHA-256 sidecars, the payload
|
||
corpus, every stage of the staged corpus and the byte-equality goldens. The direct
|
||
decodes are `int_video::legacy` and `staged::wir1::legacy`, test-only (`legacy_int`).
|
||
No input decodes or refuses differently ([integer-operators](extensions/integer-operators.md)
|
||
§15.3 item 5).
|
||
- **The 32-bit form, behind a feature.** `wai-ops`' E9 32-bit form
|
||
(`stage::residual_step_i32`, `i32_form_fits`, and `residual_step_by_i32` for `i64`
|
||
operands) is behind the new `i32_forms` feature, the arithmetic a 32-bit fabric runs;
|
||
`wai-rs`' `i32_forms` decodes `WIV1` and `WIR1` through it
|
||
(`int_video::ops::decode_typed_i32`, `staged::wir1::ops::apply_unit_i32`), held to the
|
||
exact decode by the parity tests, the differential and the byte-equality goldens.
|
||
- **Text.** [integer-operators](extensions/integer-operators.md) §5E writes E10, E11 and
|
||
E12 out, §13.5 the `WIV1` and `WIR1` drivers, and the status note names the stage-only
|
||
capabilities; integer-payloads §3 and §12 and staged-delivery A.4 cite the stage
|
||
operators; SPEC §8 lists no planned break for them.
|
||
|
||
### `wai.image.jpegai`'s scale path and entropy decodes through the integer operator set
|
||
|
||
- **The residual-variance bands start where the reference decoder's tables change.**
|
||
The exact decoder put a block whose `likely` was exactly 1003, 1352 or 1937 in the
|
||
next band, for its σ addition and for its dequantisation multiplier. The reference
|
||
decoder's own tables (4352 entries per branch, indexed by `likely`) change value at
|
||
1004, 1353 and 1938, and the reference sink's float decode path, which reads those
|
||
tables, already followed them. The edges are now 1004, 1353 and 1938, in the decoder
|
||
and in [integer-operators](extensions/integer-operators.md) §13.6.1 step 3 (§15.3
|
||
item 4). `tests/jpegai_mans_golden.rs` holds both branches' additions and multipliers
|
||
to the reference's tables at every index. No committed codestream has a block on an
|
||
edge, so no output changes.
|
||
- **The lowering, from the bundle the decoder holds.** `wai-ops` gains
|
||
`lower::parse_jpegai`, `lower::jpegai_net` and `lower::jpegai_net_loaded`
|
||
(`jpegai-int/1`, [integer-operators](extensions/integer-operators.md) §13.6.1, of a
|
||
bundle already parsed; the last loads both scale graphs without writing and parsing
|
||
them again), `lower::jpegai::scale` (one plane's graph) and
|
||
`stage::mans::decode_sgm_tables` (E8 with its tables built once). The committed graphs
|
||
and certificates are unchanged.
|
||
- **The switch.** `jpegai_decode::{decode, decode_with_stages}` run the scale path and
|
||
the two entropy decodes through the operator set (`jpegai_ops`): the bundle's scale
|
||
graphs loaded under `acc32` with its tables (`jpegai_decode::load`, a
|
||
`jpegai_ops::ScalePath`), then each plane's hyper-latent, scale, mask, `likely`, RVS
|
||
addition, `σ_idx` and latent. Every committed and pinned output is unchanged: the
|
||
decoded RGB of `jpegai/v1`, every stage hash of `tests/jpegai_cross_platform.rs`, and
|
||
the reference decoder's symbols and scales. Each now returns a `Result`
|
||
(`jpegai_decode::Refusal`); `decode_loaded` decodes with a scale path loaded once. The
|
||
direct decode is `jpegai_decode::legacy` and `jpegai_ops::legacy`, test-only
|
||
(`legacy_int`). `NormTables`' bounds and CDF rows are `i64`, so a load check sees the
|
||
value the file gives. The float decode path decodes its scale path and entropy through
|
||
the same driver, from its own bundle's members.
|
||
- **`ops` member.** `wai.image.jpegai` registers `{ wai-ops/1, acc32, full,
|
||
jpegai-int/1 }` (SPEC §5, §7).
|
||
- **No silent fallback.** The neural sink lowers and loads the scale path from the
|
||
members' bytes through the store of loaded sets (keyed by the listing of `norm.json`
|
||
and `weights/hs_{y,uv}.json`). A refusal of the exact decode is the decode's result
|
||
(`model_mismatch`, `stream_exhausted`, or `set_incomplete` for a member the set
|
||
lacks); only a sink without the bundle, or a thread or target that cannot run the
|
||
float stages, decodes through the float path ([determinism-tiers](extensions/determinism-tiers.md) §5).
|
||
- **The browser crate.** `decodeJpegAiMans` decodes through E8 and throws on a refusal;
|
||
`decodeJpegAiFull` reports the decode's refusal.
|
||
- **One implementation.** The float path's own ports of the mANS decoder and of the
|
||
hyper-scale network (`neural::jpegai_mans`, `neural::jpegai_hyper`) are deleted: that
|
||
path runs the shared driver. The exact decoder's direct pieces (the crate-root mANS
|
||
port, `jpegai_synthesis::decode_hyper_scale`, and the pipeline's `skip_mask`,
|
||
`rvs_additive`, `rvs_likely` and `sigma_index`) are test-only (`legacy_int`), the
|
||
reference the differential (`tests/int_ops_differential.rs`) and the refusal-parity
|
||
tests hold the operator set to.
|
||
- **Planned breaks** (integer-operators §15.3 item 4, SPEC §8): a bundle that fails a
|
||
load check of §13.6.1 (a bound above 255 or a shift outside `0..=127`, which the loader
|
||
wrapped, among them) or whose scale graphs do not meet `acc32` (a sum past `i32`, a
|
||
shift past 63) is refused (`model_mismatch`); a substream that ends before the decode
|
||
it carries is refused (`stream_exhausted`) where the decoder panicked. No committed
|
||
bundle or codestream is affected.
|
||
|
||
### `wai.text.timed`: timed text and live captions
|
||
|
||
- **The extension** ([timed-text](extensions/timed-text.md)) registers the record
|
||
`wai.text.timed`. A cue is a span of media time and its text in a closed set of styling,
|
||
italic runs, line breaks and six anchors, which WebVTT and the IMSC1 Text Profile both
|
||
express; text outside the subset both hold exactly (a tab, two spaces in a row, a space at
|
||
a line's edge) is refused rather than changed.
|
||
- **Live captions.** A caption is a `caption` event of `wai.live.events`, unchanged. A
|
||
machine caption is revised in place: each revision is a new `caption` event, followed by
|
||
a `meta` event carrying the `WTX1` annotation, which supersedes the revision before it by
|
||
digest, says whether the text is final and how much is stable, and carries the anchor and
|
||
italics. A verifier checks that revisions form one line per cue, keep their language,
|
||
anchor and stable prefix, and supersede only a partial of their own principal; it states
|
||
what a sink shows at an instant and the cues a stream resolves to. A publisher repeats
|
||
the open partial and each caption's annotation at a group's start, so a subscriber that
|
||
joins mid-stream shows what one from the start shows. live-events needs no change.
|
||
- **Mappings.** WebVTT documents and segments with `X-TIMESTAMP-MAP`, and a reader of the
|
||
subset; IMSC1 documents, refused when a sound check of the hypothetical render model does
|
||
not pass; the WebVTT and IMSC1 (cue mode) MoQ caption timelines, whose first object of
|
||
each group repeats every cue still showing, and their catalog entries; CTA-608 on field 1,
|
||
CC1, pop-on and roll-up with windows of 2, 3 and 4 rows, odd parity, doubled codes, the
|
||
special and extended characters with their fallbacks, and the slot clock's rate as a
|
||
parameter; CTA-708 is specified only.
|
||
- **The disclosure label.** Origin and model digest travel through every target: the HLS
|
||
`public.machine-generated` characteristic (`draft-pantos-hls-rfc8216bis-22` §4.4.6.1),
|
||
C2PA 2.4 `c2pa.ai-disclosure` and the IPTC digital source type, a WebVTT class span, IMSC1
|
||
attributes, timeline members, a catalog field, and a sidecar published by
|
||
`EXT-X-SESSION-DATA` for targets that cannot carry it, such as 608.
|
||
- **Player obligations** for captions under the viewer's control, persistent and
|
||
discoverable, each citing EN 301 549 V4.1.1 or WCAG 2.2.
|
||
- **Reference and corpus.** `wai-rs` feature `timed_text` (`src/timed_text.rs`, with
|
||
`cc608.rs`); `live-conformance/captions/`, written from fixed seeds by
|
||
`wai_timed_text_vectors` with every expected `.vtt`, `.ttml` and `.608.hex`, and checked
|
||
by `tests/timed_text.rs` and by `live-conformance/timed_text_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`, parses every IMSC1 document with an XML
|
||
parser, and requires every mutated copy of the corpus to fail. The registry lists the
|
||
record, the magic `WTX1`, its catalog fields and its session data id.
|
||
- **Not checked against the standard.** CTA-608-E and CTA-708-E could not be read for this
|
||
revision. The 608 tables are as 47 CFR 79.101 reproduces them; the extended characters are
|
||
not there, and the extension marks its table of them for checking.
|
||
|
||
### `wai.live.recording`: a room session as one package, and clips that prove what they are
|
||
|
||
- **The extension** ([live-recording](extensions/live-recording.md)) registers the record
|
||
`wai.live.recording`. A finished room session is one WAI package: a directory of files and
|
||
its `LISTING` (SPEC §3.1), as a packaging package is, whose components are the room (its
|
||
descriptor, grants and key-log head), its events (each principal's chain whole, the form
|
||
live-events §8.6 gives a recording), one component for each participant's take (its `WTM1`
|
||
manifest, the chunks the store holds, its receipts and consents) and, optionally, the
|
||
composite by its composition receipts. Each component is named as SPEC §7.1 names one, by
|
||
an `id` and a `sha256`, here the SHA-256 of the listing of its files.
|
||
- **The recording manifest** (`WRM1`) is signed by the room authority, or by a principal that
|
||
held the record power at every instant of the session span (its grant in effect from the
|
||
span's start, with no leave, exchange or expiry taking the power away before its end; a
|
||
refusal states the first instant without it), and names every component with the span on
|
||
the room clock and the region the package is kept in, held to the room's region pin. Every
|
||
take the package holds lies within the span; the package holds the room's chains whole. A
|
||
recording verifier replays the room and each take and gives `complete` (every take the
|
||
room's events open is held, each one `complete` with its commitments unbroken from open to
|
||
close, and no event missing) and `publishable` for each purpose.
|
||
- **Clips.** A clip record (`WCL1`) covers a span of one take, by chunks or by a byte range
|
||
of the master (the boundary chunks carried whole, with trims), or one window of the room
|
||
clock across several takes. Each part carries its chunks' descriptors and one range proof
|
||
against a commitment the device made while it captured, the closing one or, while the take
|
||
runs, the one that covers the span. A clip verifier needs the clip's bytes, the record, the
|
||
take's commitments up to that one, the device's key and the room descriptor, never the take,
|
||
and checks `single-take` over the span. An aligned clip states each part's window on its
|
||
device's clock and its bound: the jitter to the room clock, twice it between two takes.
|
||
- **Range proofs.** The output tree gains `range_proof` and `verify_range`: RFC 6962's audit
|
||
path taken over a run of leaves, whose one-leaf case is the audit path node for node and
|
||
whose check agrees with RFC 9162 §2.1.3.2's, tested over every run of trees of up to 33
|
||
leaves. The take tree reuses it; no new tree.
|
||
- **Mappings.** The HLS and LL-CMAF presentation of a CMAF master (one Media Playlist per
|
||
take, its header by `EXT-X-MAP` byte range, parts proved with their chunk) and the MoQ
|
||
addressing of a recorded take's chunks for a FETCH (a group per chunk, a `LOCATION_FILTER`
|
||
per clip part) are specified; the reference states each clip part's FETCH address and
|
||
writes no playlist.
|
||
- **Reference and corpus.** `wai-rs` feature `live_record` (`src/live_record.rs`);
|
||
`live-conformance/recordings/`, written from fixed seeds by `wai_live_record_vectors`, whose
|
||
performer's take is capture binding's `lesson` take byte for byte, checked by
|
||
`tests/live_record.rs` and by `live-conformance/live_record_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`, checks range proofs by the extension's
|
||
recursion and by RFC 9162's own algorithm, and requires every mutated copy of the corpus to
|
||
fail. The registry lists the record and its magics (`WRM1`, `WCL1`). live-events §8.6,
|
||
live-rooms §3.7 and capture-binding §1 point at the extension.
|
||
|
||
### `wai.live.compose`: a room's composition as an integer function
|
||
|
||
- **The extension** ([live-compose](extensions/live-compose.md)) registers the record
|
||
`wai.live.compose`. A `WCD1` composition descriptor, signed by the room authority or by a
|
||
principal whose grant has the compose power, fixes a canvas (each edge even, 16 to 1920
|
||
pixels, at most 1920 × 1080 pixels, 1 to 60 frames a second), an sRGB background, the
|
||
highest layer composed, a layout, the grant attributes it reads, and its limits on tiles
|
||
and voices.
|
||
- **The participant set** at a room-clock instant is the room's lifecycle replay up to it:
|
||
every present publisher but the compositor, its video source (own video, own screen, or
|
||
its live place in a slot) and audio source, each with the dimensions of the composed
|
||
layer, the attributes of its grant in effect, and its join order. A gap in a room key's
|
||
chain is `lifecycle_incomplete`; a gap elsewhere changes nothing. The set has a digest.
|
||
- **The layout** (§5) is integer arithmetic throughout, with every rounding stated: a grid
|
||
chosen by the largest tile of the reference aspect among every column count, its rows
|
||
centred; tile aspects auto, 16:9, 1:1 and 3:4; a featured participant above the others;
|
||
fill, cover and contain, by default cover for grid tiles and contain for the featured
|
||
one; stopped video shown or omitted; order by attribute, then join order, then key; and
|
||
picture in picture, the PiP participant's window over the grid in any corner, of a set or
|
||
derived size, static or dynamic (alone, it is laid out as though it had no PiP
|
||
attribute). The layout is computed again at every lifecycle event and grant exchange.
|
||
- **The mix** (§6): per-voice gain and pan as integers in units of 1/4096, over signed
|
||
16-bit PCM, summed exactly in 64 bits and saturated once, so it is byte-exact over the
|
||
decoded PCM; a decode and a lossy re-encode are not, and the receipt binds objects.
|
||
- **Where it runs** (§7): on each sink by default, with no server encode; a server
|
||
compositor must be the descriptor's compositor, hold the compose power and sign a `WCR1`
|
||
receipt for each object of a composed track, naming the descriptor, the set's digest at
|
||
`t`, the output object and the input objects of exactly the tracks the layout reads.
|
||
- **Live rooms.** §1, §3.3 and §4.4 point at the extension for the compose power and the
|
||
attributes `featured`, `order` and `pip` (and `gain` and `pan`, which it adds).
|
||
- **Reference and corpus.** `wai-rs` feature `live_compose` (`src/live_compose.rs`; the
|
||
layout and the mixer in `src/live_compose/layout.rs` use nothing beyond `core`, `Vec` and
|
||
`String`); `live-conformance/compose/`, written from fixed seeds by
|
||
`wai_live_compose_vectors`, and checked by `tests/live_compose.rs` and by
|
||
`live-conformance/live_compose_verify.py`, a second implementation that shares no code
|
||
with `wai-rs`, steps through the room's lifecycle replay itself and requires every
|
||
mutated copy of the corpus to fail. The registry lists the record and its magics (`WCD1`,
|
||
`WCR1`).
|
||
|
||
### `wai.capture.take`: a device's master recording bound to a live room
|
||
|
||
- **The extension** ([capture-binding](extensions/capture-binding.md)) registers the
|
||
record `wai.capture.take`, for the case of a lesson or an audition where the live call is
|
||
low quality and the master is made on the performer's own device and uploaded after the
|
||
session. A take answers a `record_start` addressed to the device; its id is derived from
|
||
the room, the device and that event. The device's grant binds it to the principal it
|
||
captures for (the grant attribute `subject`), and may mark that principal a minor
|
||
(`minor`).
|
||
- **Commitments.** While it captures, the device emits a `take_commit` event every commit
|
||
interval of the room's recording policy (live-events §4.12's payload, defined here): the
|
||
take id, the chunk range it adds, the root over every chunk so far, its offset to the room
|
||
clock, and an RFC 9162 consistency proof that the root extends the last one. The take
|
||
tree is staged-measurement Appendix C's (RFC 6962's Merkle tree hash, its node) over
|
||
leaves that bind each chunk's index, capture range, length and hash; the output tree
|
||
exposes that hash and gains consistency proofs for it.
|
||
- **Upload and manifest.** The master is uploaded resumably by chunk hash; the store, a key
|
||
the room's key log lists with the new authority `capture/store`, signs `WUR1` receipts of
|
||
the upload's state machine (open, progress, resume, sealed) within the room's region pin.
|
||
The device signs a `WTM1` take manifest listing the chunks, their root, every
|
||
commitment's digest and each subject's `WCN1` consent (purposes live, record, distribute
|
||
and train; a guardian's signature for a minor; revocable through the key log).
|
||
- **Verdicts.** A take verifier replays the room (live-rooms §9) and gives `single-take`,
|
||
`aligned`, `complete` and `publishable` for each purpose a verdict, each failure a named
|
||
reason (§12): a commitment not delivered is `delivery_gap`; a part not committed at its
|
||
index is altered, reordered or extra; a missing part fails `complete`, not `single-take`.
|
||
A room-clock instant maps into the master, so a marker set on the live call lands in it.
|
||
- **Live rooms.** The recording policy gains `commit_interval_ms` and `jitter_ms`
|
||
(live-rooms §3.7; 2 000 and 500 by default), and the rooms corpus is regenerated.
|
||
- **C2PA live video** (C2PA Technical Specification 2.4 §19): the mapping of a take and its
|
||
CMAF output onto per-segment manifests and Verifiable Segment Info, session keys, AI
|
||
disclosure and sustainability is specified; the crate has no CBOR or COSE encoder, so it
|
||
implements only the stream id.
|
||
- **Reference and corpus.** `wai-rs` feature `capture_take` (`src/capture_take.rs`);
|
||
`live-conformance/takes/`, written from fixed seeds by `wai_capture_take_vectors`, and
|
||
checked by `tests/capture_take.rs` and by `live-conformance/capture_take_verify.py`, a
|
||
second implementation that shares no code with `wai-rs` and requires every mutated copy
|
||
of the corpus to fail. The registry lists the record and its magics (`WTM1`, `WUR1`,
|
||
`WCN1`).
|
||
|
||
### `wai.live.room`: room descriptors, room grants and the room verifier
|
||
|
||
- **The extension** ([live-rooms](extensions/live-rooms.md)) registers the record
|
||
`wai.live.room`. A room is fixed by a signed descriptor, `WRD1`, whose digest is the
|
||
room id every `WEV1` event names: its authority, its clock and `t0`, the roles a
|
||
principal may hold (publish per media kind, subscribe up to a layer, the event kinds it
|
||
may send, and the moderate, direct, compose and record powers), its limits and event
|
||
ceiling, the slots several publishers may stand behind, its publish profiles, its
|
||
review policy and its recording policy with a region pin.
|
||
- **Grants.** A principal takes part under a signed room grant, `WRG1`: its role and
|
||
capabilities, a window of at most fourteen days, its delivery origins, a single-use
|
||
nonce, a viewer id, the `WEP1` event policy of live-events §5 it is held to, its joule
|
||
and funds ceilings, the profiles it publishes with, and at most 1 KB of composition
|
||
attributes. A grant is exchanged for one chained to it at a tick of the room clock,
|
||
recorded as the `grant_exchange` lifecycle event, and revoked through the authority's
|
||
key log: the head's `revoked_modules` lists revoked grants and viewers, and the
|
||
authority's key holds the new authority `live/room` (jwp-receipts §7.2, §7.5, §7.6).
|
||
- **Tracks, slots and profiles.** A track is named by the MOQT namespace tuple (room,
|
||
principal or slot, media kind) and its layer. A slot is a virtual track: a publisher of
|
||
higher precedence takes it over with no new session, and a standby takes it on failover
|
||
under the slot's rule. The lifecycle kinds' `detail` is defined. The publish profiles
|
||
`speech`, `music`, `screen`, `camera-720` and `camera-1080` fix codecs, layers and
|
||
limits as data.
|
||
- **Room verification.** Given a descriptor, grants, a key-log head and every principal's
|
||
chain, a verifier merges the chains by room clock, verifies each event with the
|
||
live-events verifier under the policy of the grant in effect at its `t`, and replays the
|
||
lifecycle into room state; each refusal has a named reason (§11).
|
||
- **Bindings**, as tables: the Common Access Token's claims (CTA-5007-B), the `moqt` claim
|
||
of `draft-ietf-moq-c4m-01` (whose scopes the reference derives from each grant), URI
|
||
Signing (RFC 9246) and Privacy Pass (`draft-ietf-moq-privacy-pass-auth-03`). No CAT
|
||
encoding: the crate has no CBOR.
|
||
- **Reference and corpus.** `wai-rs` feature `live_room` (`src/live_room.rs`);
|
||
`live-conformance/rooms/`, written from fixed seeds by `wai_live_room_vectors`, and
|
||
checked by `tests/live_room.rs` and by `live-conformance/live_rooms_verify.py`, a second
|
||
implementation that shares no code with `wai-rs`. The registry lists the record and its
|
||
magics (`WRD1`, `WRG1`).
|
||
|
||
### `wai.live.events`: a signed, timeline-bound event track
|
||
|
||
- **The extension** ([live-events](extensions/live-events.md)) registers the record
|
||
`wai.live.events`. Timed metadata, ad and chapter cues (with an SCTE 35-1
|
||
`splice_info_section()` or an SCTE 35-2 message carried verbatim), chat, reactions,
|
||
polls, markers, remote-control commands, captions with their machine-generation label,
|
||
moderation decisions, take commitments and room lifecycle are each one `WEV1` record:
|
||
signed by its principal, hash-chained per principal (`seq`, `prev`), stamped with the
|
||
room clock and optionally anchored to one MoQ object. Its digest is SHA-256 over the
|
||
record without the signature, and its signature is verified strictly (SPEC §7
|
||
*Signatures*).
|
||
- **Policy and verification.** The event policy `WEP1` states the kinds a principal may
|
||
send and the payload, text and rate bounds, every bound a default a room may change; the
|
||
room grant of a later extension carries the same fields. A verifier refuses a record
|
||
with a named reason (§9), and reports a delivery gap as a gap, never as corruption.
|
||
- **Polls** replay as `wai.feed.state` cells through `wai.world.sim.feed0` (`ADD_INT`):
|
||
a tally is replay-equivalent, and a tally a poll's author states is checked.
|
||
- **Bindings**, field semantics only: an MSF event timeline over MoQ (`eventType`
|
||
`urn:wai:event:1`, one track per principal, one-minute groups, a join set repeated at
|
||
each group start), `EXT-X-DATERANGE` (`CLASS="science.transaction.wai.event"`,
|
||
`X-WAI-EVENT`, with `SCTE35-OUT`/`-IN`/`-CMD` for cues), a version 1 `emsg`
|
||
(`scheme_id_uri` `urn:wai:event:1`), a WebRTC data channel and a recording.
|
||
- **Reference and corpus.** `wai-rs` feature `live_events` (`src/live_events.rs`);
|
||
`live-conformance/events/`, written from fixed seeds by `wai_live_events_vectors`, and
|
||
checked by `tests/live_events.rs` and by `live-conformance/live_events_verify.py`, a
|
||
second implementation that shares no code with `wai-rs`. The registry lists the record
|
||
and its wire identifiers (`WEV1`, `WEP1`, `urn:wai:event:1`,
|
||
`science.transaction.wai.event`, `X-WAI-EVENT`).
|
||
|
||
### wai-quantum 0.6.2: the HIP member's matrix-instruction kernel, and the Metal member's tensor-operation kernel
|
||
|
||
Additive. Every pinned digest is unchanged.
|
||
|
||
- **Every product by the shading language's tensor operation**
|
||
(`quantum_fabric::metal::TENSOR_OPS_SOURCE`, `tensor_ops.metal`; `MetalMember::tensor_ops`):
|
||
a third Metal kernel for the gate, through the language's version 4.0 tensor operation (a
|
||
bf16 matrix multiply accumulated in f32), which the platform maps to the GPU's matrix
|
||
hardware where it has it. The operation reads and writes device memory, so each threadgroup
|
||
owns a slice of scratch (`TENSOR_OPS_SCRATCH_BYTES`): a 64-pair tile is one multiply of
|
||
64 × 32 limbs by 32 × 128 weights, in four 32-column runs. Its operands are offset-binary, as
|
||
for the HIP kernel below, so the result does not depend on how an accumulator treats a
|
||
negative term. A device or operating system without the operation
|
||
(`DeviceIdentity::has_tensor_ops`: GPU family 1007 or later, operating system 26 or later)
|
||
is `MetalError::Unsupported`. Eight mutants (`TENSOR_OPS_MUTANTS`), among them nine-bit
|
||
limbs, every run reading the first columns of the weights, and a wrong offset correction.
|
||
- **Measured** on two GPU generations, silicon: family 1009 (the macOS runner's) and family
|
||
1010, the generation whose GPU cores carry matrix accelerators. On each: 9/9, 66020 probe
|
||
pairs, all four pinned certificates, circuits to 22 qubits, 8 of 8 mutants killed. On family
|
||
1010 the integer and SIMD-group matrix kernels are bit-exact too (9/9 each, 6 of 6 and 7 of 7
|
||
mutants). With signed limbs instead, the tensor operation still agrees on all 66020 pairs on
|
||
both generations: the offset is for the instruction below, not needed here. Where the
|
||
platform runs the operation is its own choice; it is not observed here. CI: a step of
|
||
`wai-quantum-metal` on family 1009; family 1010 was run by hand, the binaries built on the
|
||
build host.
|
||
|
||
- **Every product by the bf16 matrix instruction** (`quantum_fabric::hip::WMMA_SOURCE`,
|
||
`wmma_limbs.hip`; `HipMember::wmma`): a second HIP kernel for the same gate, for the gfx11
|
||
architectures (`DeviceIdentity::has_wmma`), whose wavefronts of 32 multiply bf16 at
|
||
16 × 16 × 16 and accumulate in f32. One wavefront multiplies sixteen pairs' limbs by the
|
||
gate's limb weights in seven 16-column steps, in the instruction's own operand layout. A
|
||
device without it is `HipError::Unsupported` (new). Eight mutants (`WMMA_MUTANTS`): nine-bit
|
||
limbs, the result's rows in another instruction's layout, and a wrong offset correction
|
||
among them.
|
||
- **Offset-binary limbs, because of what the instruction does.** Its f32 accumulation is not
|
||
exact once a negative term enters a sum. Measured on gfx1151, on the instruction alone:
|
||
`1·1 + (−1)·0` returns `1 − 2^-24`, `1 − 1` returns `−2^-24`, `128 − 64` returns
|
||
`64 − 2^-17`; sums of non-negative terms come back exact. The CUDA kernel's signed top limb
|
||
therefore fails the probe on this GPU, off by one in a coefficient. So each value is taken
|
||
as `x + 2^31`, all four limbs unsigned, the instruction forms `(e + 2^31)·(a + 2^31)`
|
||
exactly, and the kernel removes the offset in 64-bit integers:
|
||
`e·a = (e + 2^31)·(a + 2^31) − 2^31·(e + a) − 2^62`, modulo `2^64`. The non-integer
|
||
results the limb program gave through one compiler's HIP build for this GPU (0.6.0) are
|
||
consistent with the same behaviour, though which instruction that build chose was not
|
||
checked.
|
||
- **Measured** on the integrated GPU (gfx1151, silicon): 9/9, 66020 probe pairs, all four
|
||
pinned certificates, circuits to 22 qubits, 8 of 8 mutants killed; the other kernel and the
|
||
state vector unchanged there. CI: a step of `wai-quantum-hip`; its absent leg runs the new
|
||
tests over no device.
|
||
|
||
### wai-quantum 0.6.1: the CUDA member's tensor-core kernel, and the Metal member
|
||
|
||
Additive. Every pinned digest is unchanged.
|
||
|
||
- **Every product on the tensor cores** (`quantum_fabric::cuda::TENSOR_SOURCE`,
|
||
`tensor_limbs.cu`; `CudaMember::tensor`): a second CUDA kernel for the same gate, for
|
||
devices of compute capability 8.0 or later (`DeviceIdentity::has_tensor_cores`). Each
|
||
32-bit value is four 8-bit limbs in bf16, the lower three unsigned and the top one
|
||
signed. One warp multiplies sixteen pairs' limbs by the gate's limb weights (sixteen
|
||
products per pair, seven coefficients each) in fourteen `m16n8k16` steps, accumulated in
|
||
f32. Every limb product is at most `2^16` and every coefficient sums at most four, so f32
|
||
holds every partial sum exactly in any order. The kernel adds the coefficients, shifted
|
||
into place, in 64-bit integers; the rest is the other kernel's arithmetic on exact
|
||
products. The member id is the device's; the implementation id is the kernel's own
|
||
(`cuda-tensor-limbs8/…`), and the member is probed before it is returned. A device below
|
||
8.0 is `CudaError::Unsupported` (new). Seven mutants (`TENSOR_MUTANTS`), one of which is
|
||
nine-bit limbs, which bf16 does not hold.
|
||
- **Measured** on the discrete GPU (compute capability 8.9, silicon): 66020 probe pairs
|
||
agree, all four pinned certificates, circuits to 22 qubits match native, 7 of 7 mutants
|
||
killed. The other kernel and the state vector are unchanged there (all four pins, 6 of 6
|
||
and 4 of 4 mutants). CI: a step of `wai-quantum-cuda`; its absent leg runs the new tests
|
||
over no device.
|
||
- **The Metal member** (`quantum_fabric_metal`, opt-in, macOS): the gate on the platform's
|
||
GPU through its own interface, compiled at run time by the operating system's compiler.
|
||
Two kernels, each with its own implementation id and mutants:
|
||
- `lanes64.metal` (`metal-lanes64/…`): the CUDA kernel's exact 64-bit integer arithmetic
|
||
in the Metal shading language, one serial dispatch per gate in one command buffer, the
|
||
state in memory the GPU and the host share;
|
||
- `matrix_limbs.metal` (`metal-matrix-limbs8/…`, `MetalMember::matrix`): every product
|
||
formed by the GPU's SIMD-group matrix multiply, from 8-bit limbs in bf16 accumulated in
|
||
f32, by the tensor-core kernel's argument. One SIMD group multiplies eight pairs' limbs
|
||
by the gate's limb weights in fourteen 8 × 8 column blocks, and each thread joins its
|
||
four products and forms one row. A device below GPU family 1007 is
|
||
`MetalError::Unsupported`.
|
||
|
||
A device is keyed by its GPU family number (`gpu-msl-<family>`), and its member id binds
|
||
the device, its registry id, family and memory, and the operating system's version. Off
|
||
macOS the member builds, finds nothing and says why.
|
||
- **Measured** on the macOS runner's GPU (family 1009, silicon): both kernels 9/9, each
|
||
with 66020 probe pairs, all four pinned certificates and circuits to 22 qubits; 6 of 6
|
||
and 7 of 7 mutants killed. Nine-bit limbs break 297 of the 484 edge pairs, so the
|
||
multiply takes bf16 inputs. CI: `wai-quantum-metal`, on that runner, and its absent leg
|
||
on Linux.
|
||
- **Measured by hand, outside CI:**
|
||
- the gate as a portable tensor program, through XLA on a cloud tensor processor: the
|
||
gate's module exact and its three mutants failing where they should, and the limb
|
||
program (`stablehlo::limb_module`) exact on the processor's matrix unit, with the
|
||
nine-bit set differing as it must;
|
||
- `wai-quantum-qpu`'s `native_turns` against the service's simulator, no hardware used:
|
||
with no noise, statistical parity at fidelity 1.0 on both gatesets' corpora (16
|
||
circuits of 5 qubits, 1000 shots each), which checks the compilation to the service's
|
||
native gates and the wire contract; with one of the service's device noise models,
|
||
fidelity 0.909 (V1) and 0.927 (V2).
|
||
|
||
### `wai.world.model`'s integer renderer through the integer operator set
|
||
|
||
- **The lowering, from the models the renderer holds.** `wai-ops` gains
|
||
`lower::world_net` and `lower::world_net_loaded`: `world/1`
|
||
([integer-operators](extensions/integer-operators.md) §13.5, informative) of two
|
||
networks already parsed, the second loading `T` before it lowers `g_s`. Both write
|
||
and refuse what `lower::world` does from the bytes; the committed sets' graphs and
|
||
certificates are unchanged.
|
||
- **No `ops` member.** The renderer runs through the operator set, but its lowering is
|
||
informative: the capability's registered payload is its signed action trace, and no
|
||
payload text yet binds a trace to a renderer's set. SPEC §5's row cites §13.5, and the
|
||
registry's test holds that `wai.world.model` has no `ops` member.
|
||
- **The direct guard, test-only.** `int_world::legacy::checked` runs the direct renderer
|
||
with the check every other integer decoder made before each layer
|
||
(`run_net_checked`); the tests hold the `check` ranges of `world/1` to it, layer by
|
||
layer, at the edge of every range and through the whole renderer.
|
||
- **The switch.** `int_world::{decode, decode_deltas, decode_planes}`, and the browser's
|
||
`WorldModel`, now render through `world/1`: `T` and `g_s` lowered and loaded under
|
||
`acc64` once (`int_world::load`, an `int_world::World`), then planned on the latent's
|
||
shape, then run frame by frame. Every committed frame and clip digest is unchanged, on
|
||
every leg and in wasm32. Each now returns a `Result`; `WorldModel`'s methods throw on a
|
||
refusal. The per-row `step`, `step_delta` and `step_planes` of a parsed model are
|
||
replaced by `World::step`, `World::step_delta`, `World::step_action` and
|
||
`World::render`. The direct renderer is `int_world::legacy`, test-only (`legacy_int`).
|
||
- **Planned breaks** (integer-operators §15.3 item 2, SPEC §8). Before the first frame,
|
||
where the direct renderer rendered from the input or panicked: a set whose `afrac` is
|
||
outside `1..=30` or unequal, with no action plane, or that breaks a `WHM1` network's
|
||
rules (`model_mismatch`); a row without one value per action plane, and `T` or `g_s`
|
||
that does not fit the latent (`model_mismatch`); a latent that is not `C·lat·lat`
|
||
values (`geometry_mismatch`), of side 0 or past `2^26` values, and a move whose plane
|
||
value leaves `i64`. A value outside a `check` range is refused (`field_range`). No
|
||
committed set or clip is affected.
|
||
- **Memory.** With its set loaded, a clip through the operator set holds no more than the
|
||
direct renderer held for it, and so do one step and one frame
|
||
(`tests/int_world_memory.rs`).
|
||
|
||
### `wai.neural.int_mlicpp` through the integer operator set
|
||
|
||
- **Its `ops` member in the registry** ([integer-operators](extensions/integer-operators.md)
|
||
§11). `registry/capabilities.json` gives `wai.neural.int_mlicpp` the member `{"set":
|
||
"wai-ops/1", "profile": "acc64", "subset": "full", "lowering": "wmb1/1", "stage":
|
||
{"rans64": {"table": "t2", "escape": "lenient", "end": "final_only"}, "bucket":
|
||
"ceil"}}`. SPEC §5's row cites the payload and bundle
|
||
([integer-payloads](extensions/integer-payloads.md) §14) and the lowering
|
||
(integer-operators §13.4), and SPEC §7 names the capability among those that decode
|
||
through the operator set.
|
||
- **The decode.** `int_mlicpp::decode`, and so `int_payload::decode_int`, the neural sink
|
||
and every sink that decodes through them, now runs the bundle's three networks through
|
||
their lowering `wmb1/1` ([integer-operators](extensions/integer-operators.md) §13.4):
|
||
each lowered by `whm1/1` and loaded under `acc64` with its certificate, then planned on
|
||
the payload's shapes, before either stream. The stage steps are unchanged, with the
|
||
`t2` table rule and the `final_only` end rule kept: a symbol decoded after a stream ran
|
||
out is still decoded and fed to the passes after it, and the refusal comes at the end.
|
||
The committed vector decodes to the same image, with the same bucket and symbol for
|
||
every element; every committed refusal keeps its code. The direct decode is kept,
|
||
test-only, behind the `legacy_int` feature. The store of loaded sets keeps the three
|
||
loaded networks under the SHA-256 of `model.wmb`.
|
||
- **Planned breaks**
|
||
([integer-operators](extensions/integer-operators.md) §15.3 item 3, SPEC §8). A bundle
|
||
is refused before either stream (`model_mismatch`) for the rules of the other `WHM1`
|
||
decoders: a bias of magnitude `2^63 − 1` or more, a `check` range its producer cannot
|
||
meet, a network with no layer of tag 0, 1 or 2, a header `shift` or `gf` of 128 or more
|
||
that no layer uses. Four checks the decoder made only when a network ran now come
|
||
before the streams with the same code: a layer that does not fit the channels before
|
||
it, a network that does not read the payload's channel count, a header `shift` or `gf`
|
||
of 128 or more that a layer uses, and a leaky ReLU's shift of 64 or more. A convolution
|
||
whose padded side exceeds `2^26` is refused.
|
||
- **The direct guard is test-only.** With this decode on its lowering, no production
|
||
decode calls `int_hyper_synth::run_net_checked`, its per-layer checks or its IGDN check:
|
||
the `check` ranges of every lowered network make them. They move into the test-only
|
||
`int_hyper_synth::legacy` (feature `legacy_int`), beside the direct decodes that call
|
||
them for the tests that hold the two to each other. `run_net` and `decode`, the
|
||
unchecked path the integer world renderer uses, stay.
|
||
- **The refusal codes, written down.**
|
||
[integer-payloads](extensions/integer-payloads.md) §14 said that every refusal of this
|
||
capability is `payload_malformed`. The decoder reports `model_mismatch` for a bundle
|
||
that breaks its rules or does not fit the payload, as it did before this revision and
|
||
as its tests pin; §14 and integer-operators §4 now say so.
|
||
|
||
### Load step 8 in closed form
|
||
|
||
- **The bounds pass.** `wai_ops::bounds` now computes each `conv`'s and `conv_transpose`'s term hull and partial-sum interval ([integer-operators](extensions/integer-operators.md) §8.2) from a few sums per output channel, or per residue for `conv_transpose`:
|
||
- the least and greatest weight;
|
||
- the sums of the positive and of the negative weights. These are kept in `i64` where the tap count times the largest magnitude shows that every partial sum fits, and in `i128` otherwise.
|
||
|
||
An `igdn` row now folds in `i128`. These forms give the rule's integers wherever no operation of its fold can saturate. The pass checks that condition: both input ends in `i128` with `l ≤ h`, the result within `±2^255`, and no `i128` overflow. Anywhere else it folds term by term in 512 bits, as before. §8.2 gains an informative note on the closed form. No certificate, verdict, refusal or output changes.
|
||
- **The oracle.** The pass as it stood is kept as `wai_ops::bounds_oracle`, test-only; another crate's tests reach it through the `oracle` feature. The two passes must agree node for node, with the same refusals and the same first failures, on:
|
||
- every committed lowered graph and graph vector;
|
||
- the pretrained conditional-codec set, when `WAI_SOTA_INT_DIR` names it;
|
||
- random operators over input intervals the pass never forms (the ends of `i64` and `i128`, up to `2^255`, and `l > h`);
|
||
- random graphs that reach saturation;
|
||
- every graph the integer-operator differential's generators lower (`tests/int_ops_differential.rs`).
|
||
- **The lowering and the load.**
|
||
- `Builder::finish` now returns the graph it parsed back from its bytes.
|
||
- `whm1_net_loaded` and `wam1_net_loaded` (new) load that graph without writing and parsing its bytes a second time (`graph::load_lowered`, crate-only). They give `load`'s result, refusal for refusal. The `int_hyper`, splat and audio drivers use them.
|
||
- The `WHM1` and `WAM1` lowerings now move each layer's lists into the graph instead of copying them.
|
||
- The `WHG1` writer now adds a list's bytes at once and stores each value as one array.
|
||
- The pass reads a node's weight range once.
|
||
- **SHA-256 on 64-bit Arm.** `wai-rs` now enables sha2's `asm` feature on `aarch64` targets other than Windows. The processor's SHA-2 instructions are used where it has them, detected at run time. The digests are the same, and the wasm32 build is byte-identical. The lockfiles of the workspaces that build `wai-rs` now include `sha2-asm`.
|
||
- **Cost.** For the pretrained conditional-codec set, lowering and loading both networks is now a small part of one decode, natively and in wasm32. A decode that loads its set now costs about what a decode that finds it held costs. The browser demo is one such decode: it decodes once per view.
|
||
|
||
### wai-quantum 0.6.0: the HIP, npu-cores, tensor-engine and avx512 members, the dataflow device's vector engine, and the gate as a portable tensor program
|
||
|
||
Breaking in three places (the last bullet). The default features, `full`, and every pinned
|
||
digest are unchanged.
|
||
|
||
- **The HIP member** (`quantum_fabric_hip`, opt-in, Linux): the CUDA member's kernel text,
|
||
unchanged, compiled at run time by HIP's runtime compiler for each device's own
|
||
architecture, with contraction off. The runtime and its compiler are loaded when first
|
||
used; the device's properties are read in the runtime's frozen layout, or the device is
|
||
refused. Its own implementation id (`hip-lanes64/…`), its own key (`gpu-hip-<arch>`); the
|
||
mutants are the CUDA kernel's. With `quantum_sv`, the dense f64 state vector runs the CUDA
|
||
kernels' text too (`hip-sv64/…`).
|
||
- **The npu-cores member** (`quantum_fabric_npu_cores`, opt-in, Linux): the gate's
|
||
arithmetic on one compute core of a neural processor's tile array, programmed directly
|
||
rather than through a model runtime. The core forms each gathered pair's four rows in
|
||
exact 64-bit integers; the host checks their range and keeps the low 32 bits. The program
|
||
(`npu_cores.cc` in `npu_cores_design.py`) is built at run time with the tile array's open
|
||
toolchain and cached per variant, generation and toolchain; the host side
|
||
(`npu_cores_host.cpp`) is compiled against the device runtime and loaded. One program
|
||
serves every gate and the arithmetic probe; each mutant is its own program. Key
|
||
`npu-cores-<pci vendor>-<device>`.
|
||
- **The tensor-engine member** (`quantum_fabric_tensor_engine`, opt-in): the gate's sixteen
|
||
products per pair as exact limb convolutions in one bf16 matrix multiply per tile of pairs,
|
||
accumulated in fp32, on a tensor processor's matrix engine. Each value is four 8-bit limbs,
|
||
which bf16 holds exactly; every limb product is at most `2^16` and every coefficient a sum of
|
||
at most four, so fp32 holds every partial sum exactly in any order, on any engine that
|
||
multiplies bf16 and accumulates in fp32. The host checks each coefficient is an integer
|
||
below `2^24`, assembles the products and does the rest. The kernel runs as a helper under
|
||
the engine's kernel toolchain; its target here is that toolchain's simulator, and every
|
||
certificate says so. One mutant is nine-bit limbs, which the engine's inputs cannot hold.
|
||
- **The avx512 vector unit** (`quantum_fabric_vector`): eight 64-bit lanes, each `fx` an
|
||
exact 32×32 multiply, an add and the unit's own 64-bit arithmetic shift. Discovery opens
|
||
one member per instruction set the processor reports, so a processor with both units has
|
||
both members.
|
||
- **Measured** on an integrated GPU (gfx1151), a neural processor's tile array and the
|
||
processor beside them, all silicon: the HIP member 9/9 (66020 probe pairs, all four pinned
|
||
certificates, circuits to 22 qubits, 6 of 6 mutants) and its state vector 3/3 (the pin
|
||
`b7d129d0…`, 4 of 4 mutants); the npu-cores member 9/9 (66020 probe pairs, all four
|
||
certificates, circuits to 20 qubits, 3 of 3 mutants); avx2 and avx512 9/9 each; the
|
||
tensor-engine member on its simulator 9/9 (66020 probe pairs, all four certificates, 4 of 4
|
||
mutants); and,
|
||
unchanged, the WebGPU member (`gpu-vulkan-1002`) and the OpenCL member
|
||
(`opencl-gpu-1002`, the state vector's pin included) bit-exact there. CI runs each of them
|
||
on that host.
|
||
|
||
- **The vector engine as a member** (`quantum_fabric_dataflow`, key `dataflow-1e52-b140-vector`):
|
||
on the device generation whose vector engine multiplies 23-bit numbers, the gate's
|
||
arithmetic runs on that engine, 32 lanes at a time, on 32 compute tiles. Each tile's
|
||
first core configures the unpacker and the packer and hands the tile its instructions
|
||
inline; the unpacker copies the pairs' columns into the destination registers as
|
||
32-bit integers, the vector engine forms the rows, the packer copies them back. Each
|
||
32×32 product comes from four 23-bit partial products, corrected for sign; `fx` is
|
||
held as 30 low bits and the rest, and each row's terms are added or subtracted with a
|
||
carry after each. A pass is sized to its share of the pairs, in steps of 64.
|
||
- **`quantum_fabric::tensix`**: the compute tile's instructions the programs use, their
|
||
opcodes and fields as the device simulator's own table gives them; CI checks the
|
||
transcription against that table. **`quantum_fabric::vector_engine`** builds the
|
||
programs, the probe included; the assembler check covers them too.
|
||
- **Measured** on the device simulator, built from source: 66020 probe pairs agree, all
|
||
four pinned certificates, circuits up to 20 qubits match native, 3 of 3 kernel mutants
|
||
(the same three the other members carry) killed. Every certificate is labelled
|
||
simulator.
|
||
- **The gate as a portable tensor program** (`quantum_fabric::stablehlo`): the gate's rows
|
||
and the arithmetic probe as a StableHLO module, the input of compilers for tensor
|
||
processors, GPUs and CPUs. Every value is a 64-bit integer that never overflows
|
||
(`|e·a| < 2^62`; a row of four `fx` stays below `2^35`), so a compiler must give the
|
||
exact rows whatever it does with them. It is not a member: the program leaves the
|
||
crate, a compiler's runtime runs it, and its outputs are compared with the exact ones. The
|
||
`stablehlo_gate` example writes the module, its three mutants (the same three), a
|
||
batch of 4096 pairs whose gate and states reach the 32-bit edges, and the outputs a
|
||
build must give.
|
||
- **Measured** with two independent open-source tensor compilers, IREE 3.12.0 and XLA
|
||
(through JAX 0.11.2, the compiler tensor processors take this program through): exact
|
||
on the x86-64 CPU with each; on every Vulkan device of the hardware host (a discrete
|
||
GPU, an integrated GPU, and a software one); on the discrete GPU through CUDA with
|
||
each; through Metal on two generations of the macOS platform's GPU; with XLA on the
|
||
arm64 CPU. On the CPU each mutant fails where it should under each compiler; on every
|
||
other device, the third mutant's rows do. Builds for three GPU
|
||
generations the fleet lacks compile but are not run. `ci/tensor-program/check.sh`
|
||
holds each run to its outcome and fails a run that ends without stating one, so a
|
||
device that cannot run reads as a failure, never as a mutant caught. CI:
|
||
`wai-quantum-tensor-program`. That host's GPU runs it through HIP, built for the GPU's own
|
||
architecture, and on both of its Vulkan devices.
|
||
- **The products on a matrix unit, as a portable program** (`stablehlo::limb_module`): the
|
||
tensor-engine member's limb layout as one StableHLO `dot_general` of bf16 operands
|
||
accumulated in f32, the program a compiler maps to a matrix unit. With it, the example writes
|
||
a nine-bit-limb data set whose exact coefficients bf16 cannot reproduce, so a run that
|
||
matches them is not comparing. Exact under both compilers on the x86-64 and arm64 CPUs and
|
||
on the discrete GPU through CUDA. Not exact through one compiler's HIP build for the
|
||
integrated GPU, whose bf16 multiply returns values that are not integers; CI shows that
|
||
step and does not require it.
|
||
- **Breaking:**
|
||
- an OpenCL key is `opencl-<kind>-<vendor id>`, the device's `CL_DEVICE_VENDOR_ID` in hex
|
||
(`opencl-cpu-8086`, `opencl-gpu-10de`, `opencl-gpu-1027f00`, `opencl-gpu-1002`), not a word
|
||
of the vendor's name; `opencl::DeviceIdentity` gains `vendor_id`, which the member id
|
||
binds, and `vendor_slug` is gone;
|
||
- `quantum_fabric_vector::Isa` gains `Avx512` (`Isa::ALL` has four), and `native_isas` lists
|
||
every set written for the architecture;
|
||
- `quantum_fabric_dataflow::Chip` gains `vector`.
|
||
|
||
### wai-quantum-qpu 0.2.0
|
||
|
||
Builds against wai-quantum 0.6.0; nothing of its own changes.
|
||
|
||
### Parameter sets kept loaded between decodes
|
||
|
||
- **The store.** A decode through the integer operator set lowers its parameter set and
|
||
loads each graph, the bounds pass included; for the pretrained conditional-codec set
|
||
that costs more than the decode. `int_set_cache::LoadedSets` now keeps a set once it
|
||
has loaded, and the next decode through the same bytes takes it from there
|
||
([integer-operators](extensions/integer-operators.md) §8.6, informative note).
|
||
`int_payload::decode_int` and `int_payload::declared_cost` use it for `WIH1`, a
|
||
`WIV1`'s keyframe, `WIA1`, `WIS1` and `wai.neural.int_synth`, and so the neural sink
|
||
and `wai-web`'s `decodeWiv1`, `decodeWih1`, `decodeWia1`, `decodeWis1` and
|
||
`decodeIntSynth` do too; `decodeIntSynth` now decodes through `decode_int`, and a
|
||
refusal's message starts with its code. `int_synth::decode_from_spec`, which the
|
||
byte-equality crate shares, is now three steps (`check_payload`, `load_spec`,
|
||
`decode_loaded`) and loads its set every time. The entry points that read the tools'
|
||
JSON forms do not use the store.
|
||
- **The key** is the capability, the lowering, the profile the graphs were loaded under,
|
||
and the SHA-256 of the listing of the members the decode reads, each by its own
|
||
SHA-256 (`pin::listing_from`): the digest a pin over exactly those members names.
|
||
Never a name: two sets whose members share their names and differ in one byte have
|
||
two entries. Parameters `int_payload::resolve` built carry the listing digest it
|
||
already formed for check 5, so such a decode does not hash its set a second time;
|
||
the digest is used only for a capability that reads exactly the members it was formed
|
||
over, and any other key hashes the bytes.
|
||
- **What does not change.** An entry is made only from what a load returned, after it
|
||
returned, so a refused set is never kept: it is lowered, loaded and refused again on
|
||
its next decode, with the same refusal. Every check before the load runs as before,
|
||
in integer-payloads §7's order. Every output, refusal and declared cost is unchanged,
|
||
and no golden moved.
|
||
- **Memory.** A kept set is the set's, outside every decode's declared working bytes
|
||
([integer-payloads](extensions/integer-payloads.md) §7, informative note). The store
|
||
keeps its entries within a byte budget, 256 MiB to start with, evicts the least
|
||
recently used first, and keeps no set larger than the whole budget; a budget of 0
|
||
turns it off, and the set is then not hashed. `LoadedSets::set_budget` sets it, and
|
||
`wai-web` exports `setLoadedSetBudget` and `loadedSetStats` for a page. An entry is
|
||
counted as the shared allocation that holds the set, its graphs' heap bytes
|
||
(`wai_ops::Graph::heap_bytes`, new) and a fixed share of the store's indexes, never
|
||
less than it holds (`tests/int_payload_cost.rs`).
|
||
- **Tests.** `tests/int_set_cache.rs`: a second decode through each committed set is a
|
||
hit, by the store's count, with the bytes of a decode with the store off; sets one
|
||
byte apart never share an entry; a set the load refuses (an `int_synth` set the bounds
|
||
pass refuses; a conditional-codec set whose lowering admits no input range) stays
|
||
refused while a set one byte away is held; every refusal fixture keeps its code and
|
||
detail with its set held; the least recently used set goes first; threads decoding
|
||
one set keep one entry. The browser crate's check (`demo/verify_int_payload_wasm.mjs`) decodes `tiny`
|
||
twice and counts the hit.
|
||
|
||
### `wai.neural.int_hyper` and `wai.video.int_hyper` through the integer operator set
|
||
|
||
- **The decode.** `int_hyper_synth::decode_conditional_checked` (so the `WIH1` dispatch,
|
||
the keyframe of a `WIV1` with keyframe 1, the staged sink and the browser crate) plans
|
||
both networks as [integer-payloads](extensions/integer-payloads.md) §7 orders, checks
|
||
the payload's shapes against them, lowers `h_s` by `whm1/1` and `g_s` by `whm1/1` with
|
||
the RGB8 step ([integer-operators](extensions/integer-operators.md) §13.2, §13.0),
|
||
loads each under `acc64` with its bounds certificate, decodes the hyper-latent, runs
|
||
`h_s` through the operator set's interpreter, splits its output into `σ` and `μ`,
|
||
decodes the latent against the tables `σ` selects, and runs `g_s`. Every
|
||
convolution, transposed convolution and inverse GDN runs on the fast kernels behind
|
||
the interpreter. The graphs' `check` ranges are the checks the direct decode made
|
||
before each layer, so the same streams are refused (`field_range`). Every committed
|
||
output is unchanged, byte for byte (the conformance vector, the neural-keyframe clip,
|
||
and the pretrained set's image and clip regenerated in `wai-sota-vectors.yml`), and
|
||
every committed refusal fixture keeps its code. The direct decode is kept, test-only
|
||
(feature `legacy_int`), in the differential tests and their nightly run.
|
||
- **Planned breaks** (SPEC §8, integer-operators §15.3 item 3), as for
|
||
`wai.splat.int_codec`, in either network: new refusals of sets no committed corpus
|
||
contains, each `model_mismatch` after the payload's shapes are checked and before
|
||
either stream is decoded. A scan of every committed set of the two capabilities, and
|
||
of every committed `WIH1` payload wherever it is carried, finds none affected.
|
||
- **The inverse GDN on the fast kernel.** The interpreter now lends an inverse GDN to the
|
||
fast IGDN kernel, as it lends convolutions, wherever the values present keep every
|
||
term and norm within `i128` and the root and output within `i64`, which every
|
||
certified graph does; the splat and audio decodes use it too. Their outputs are
|
||
unchanged.
|
||
- **The unchecked decode is removed.** `int_hyper_synth::decode_conditional`, which ran
|
||
both networks with no exactness check, is gone; its callers (the examples, the
|
||
benchmark, the tests and the byte-exact neural-keyframe golden) decode through
|
||
`decode_conditional_checked`, so through the lowering with its `check` ranges, with
|
||
every output equal. The layer-by-layer guard `run_net_checked` stays while
|
||
`wai.neural.int_mlicpp` still runs its networks through it.
|
||
- **The tools' JSON models.** The browser crate's debug decode of the tools' JSON
|
||
form now refuses (`model_mismatch`) a JSON model holding a weight past `i16` or an
|
||
inverse-GDN `γ` past `i32`, the widths `WHM1` gives them and its lowering writes;
|
||
the committed models hold none.
|
||
- **Their `ops` member in the registry** ([integer-operators](extensions/integer-operators.md)
|
||
§11). `registry/capabilities.json` gives `wai.neural.int_hyper` and
|
||
`wai.video.int_hyper` the member `{"set": "wai-ops/1", "profile": "acc64", "subset":
|
||
"full", "lowering": "whm1/1", "stage": {"rans64": {"table": "t4097", "escape":
|
||
"lenient", "end": "canonical"}, "bucket": "ceil"}}`. SPEC §5's rows cite the payloads
|
||
([integer-payloads](extensions/integer-payloads.md) §4, and §3 for the clip) and the
|
||
lowering (integer-operators §13.2), and SPEC §7 names both capabilities among those
|
||
that decode through the operator set.
|
||
|
||
### wai-quantum 0.5.1: neural-processor, matrix-extension, vector-unit, signal-processor and dataflow members
|
||
|
||
Additive. The default features, `full`, and every pinned digest are unchanged.
|
||
|
||
- **The neural-processor member** (`quantum_fabric_npu`, opt-in, macOS only). A neural
|
||
processor computes in fp16 and does not document how it accumulates, so the member
|
||
never relies on it: each 32-bit operand is split into eight 4-bit limbs, and the
|
||
processor computes the fifteen coefficients `Σ_{i+j=s} a_i·b_j` of each product. Every
|
||
limb product is at most 225 in size and every coefficient at most 1800, so every value
|
||
it forms is an integer fp16 holds exactly, in any order. The host assembles each
|
||
product exactly and applies `lanes::apply_words`'s arithmetic.
|
||
- **Credited by placement.** The member writes its model (one multiply, one sum) as the
|
||
platform runtime's model description, compiles it at run time, and reads the
|
||
runtime's compute plan: every operation must be placed on the neural processor, or
|
||
the member is refused with the placement reported, so no CPU result is credited to the
|
||
processor. It is then probed and certified against the pinned corpus digests, and
|
||
the two model mutants (the multiply made an add, the sum a maximum) are caught.
|
||
- **The same program, portable.** `npu::onnx_limb_model` writes it in the portable model
|
||
format (ONNX, opset 17: one `Mul`, one `ReduceSum`), with fp16 or int32 tensors, and
|
||
`npu::expand_i32` lays out the int32 inputs; `examples/npu_onnx.rs` writes both models,
|
||
a batch of inputs and the exact products they must give, so a neural processor
|
||
reached through a runtime that reads that format can be checked against them.
|
||
- **Fixed** before release: `npu::f16_of(±2048)` subtracted past zero in a debug build.
|
||
- **The matrix-extension member** (`quantum_fabric_sme`, opt-in, aarch64 only): the
|
||
gate's fixed-point arithmetic in the processor's streaming vector mode (SME), 64-bit
|
||
lanes, by inline assembly; the host gathers each gate's pairs and checks each row's
|
||
range. Its instructions exist only in that mode, so a kernel that ran, ran there.
|
||
Detection reads the processor's feature flags (on Linux, `AT_HWCAP2`, which a
|
||
user-mode emulator supplies for its guest); a runner under an emulator declares it,
|
||
and every certificate carries that provenance. Three kernel mutants are compiled in.
|
||
- **The DSP vector-unit member** (`quantum_fabric_dsp`, opt-in, in a build for the signal
|
||
processor with its 128-byte vector extension). The gate's arithmetic in 32-bit lanes,
|
||
each 64-bit value a pair of words: the product is the vector unit's exact 32×32
|
||
multiply (the 32-bit operand times the other's low half, unsigned, plus its high
|
||
half, signed, sixteen bits up); the rounding constant's carry and each row's carries
|
||
and borrows go into the high word; the shift is arithmetic over all 64 bits. The
|
||
fabric gains the class `dsp`. Three kernel mutants are compiled in.
|
||
- **The CPU vector-unit members** (`quantum_fabric_vector`, opt-in): the gate's arithmetic
|
||
in the processor's own vector extension — `avx2` on x86-64 (the 64-bit arithmetic
|
||
shift, which the unit lacks, built from a logical shift and the sign bit), `sve` on
|
||
aarch64 and `rvv` on riscv64, both written once for every vector length (the length
|
||
set per pass and read back from the unit into the member's id). Silicon for `avx2`;
|
||
`sve` at 128, 512 and 2048 bits and `rvv` at 128 and 1024 bits under the user-mode
|
||
emulator. Three kernel mutants per instruction set.
|
||
- **The dataflow member** (`quantum_fabric_dataflow`, opt-in, Linux): the gate's arithmetic on
|
||
the first RISC-V core of each of 32 compute tiles of a dataflow accelerator, each over a
|
||
contiguous slice of a gate's pairs, two device generations (PCI 1e52:b140 and 1e52:401e),
|
||
driven through the device's own library at the level of configuration space, memory
|
||
windows, reset registers and clocks. The program comes from a new RV32IM
|
||
encoder (`quantum_fabric::rv32`), whose listings an independent assembler turns into
|
||
the same words; every 64-bit value is a pair of words. The library is the device's
|
||
open-source simulator, built from source, and every certificate is labelled simulator.
|
||
- **The signal processor as a processor.** The bit-identity digests, the frame fixtures and
|
||
the byte-pinning library tests pass for the processor's Linux target under its
|
||
user-mode emulator. Emulator releases before 11.0 test a whole predicate register
|
||
where the processor tests its low bit, in a branch on a predicate written in the same
|
||
packet; compiled code relies on the low bit, and one library test fails under such an
|
||
emulator. `ci/dsp/predicate_branch.c` shows which emulator does which, and the
|
||
suites run on 11.1.1, built from its release.
|
||
- **CI.** `wai-quantum-npu.yml`: the device tests on the macOS runner's neural processor,
|
||
and the same build on Linux, where the member is absent and says so.
|
||
`wai-quantum-sme.yml`: the matrix-extension member's device tests for aarch64 Linux
|
||
under the host's user-mode emulator, certificates labelled emulator; and the macOS
|
||
runner, whose processor has no SME, finding no member. `wai-quantum-dsp.yml`: the
|
||
signal processor's suites and the vector-unit member's device tests under the pinned
|
||
emulator, after the emulator's own branch probe, and the same build on x86_64, where
|
||
the member is absent and says so. `wai-quantum-vector.yml`: `avx2` on the container
|
||
runner's processor, `sve` and `rvv` under the host's emulator at the lengths above (each
|
||
leg checks the length the member read), and the macOS runner finding no member.
|
||
|
||
### `wai.audio.int_codec` through the integer operator set
|
||
|
||
- **The decode.** `int_audio::decode_typed` (so the `WIA1` dispatch, the staged sink and
|
||
the browser crate) plans the synthesis as
|
||
[integer-payloads](extensions/integer-payloads.md) §7 orders, lowers `model.wam` by
|
||
`wam1/1` ([integer-operators](extensions/integer-operators.md) §13.3, with its PCM
|
||
step), loads the graph under `acc64` with its bounds certificate, decodes the stream,
|
||
and runs the graph through the operator set's interpreter with `t_out` as its scalar
|
||
and the fast transposed-convolution kernel behind it. Its `check` ranges are the
|
||
checks the direct decode made before each layer, and the PCM step's range is the one
|
||
in which a sample's scaling fits `i64`, so the same clips are refused
|
||
(`field_range`). Every committed output is unchanged, byte for byte, and every
|
||
committed refusal fixture keeps its code. The direct decode is kept, test-only
|
||
(feature `legacy_int`), in the differential tests and their nightly run.
|
||
- **Planned breaks** (SPEC §8, integer-operators §15.3 item 3), as for
|
||
`wai.splat.int_codec`: new refusals of sets no committed corpus contains, each
|
||
`model_mismatch` before any stream is decoded, the PCM step's range among the `check`
|
||
ranges whose producer must meet them. A scan of every committed audio set and payload
|
||
finds none affected.
|
||
- **Its `ops` member in the registry** ([integer-operators](extensions/integer-operators.md)
|
||
§11). `registry/capabilities.json` gives `wai.audio.int_codec` the member
|
||
`{"set": "wai-ops/1", "profile": "acc64", "subset": "full", "lowering": "wam1/1",
|
||
"stage": {"rans64": {"table": "t4097", "escape": "lenient", "end": "canonical"}}}`.
|
||
SPEC §5's row cites the payload ([integer-payloads](extensions/integer-payloads.md) §5)
|
||
and the lowering (integer-operators §13.3), and SPEC §7 names the capability among
|
||
those that decode through the operator set.
|
||
|
||
### `wai.splat.int_codec` through the integer operator set
|
||
|
||
- **The decode.** `int_splat::decode_typed` (so the `WIS1` dispatch, the staged sink and
|
||
the browser crate) plans the networks as
|
||
[integer-payloads](extensions/integer-payloads.md) §7 orders, lowers `g_s.whm` by
|
||
`whm1/1` ([integer-operators](extensions/integer-operators.md) §13.2, with the RGB8
|
||
step of §13.0), loads the graph under `acc64` with its bounds certificate, decodes the
|
||
stream, and runs the graph through the operator set's interpreter with the fast
|
||
transposed-convolution kernel behind it. Its `check` ranges are the checks the direct
|
||
decode made before each layer, so the same latents are refused (`field_range`).
|
||
Every committed output is unchanged, byte for byte, and every committed refusal
|
||
fixture keeps its code. The direct decode is kept, test-only (feature
|
||
`legacy_int`): `tests/int_ops_differential.rs` decodes random sets and clouds both
|
||
ways, every push and for an hour per build profile each night.
|
||
- **Planned breaks** (SPEC §8, integer-operators §15.3 item 3): new refusals of sets no
|
||
committed corpus contains, each `model_mismatch` before any stream is decoded. A layer
|
||
with a bias of magnitude `2^63 − 1` or more, or a `check` range its producer cannot
|
||
meet, refused every decode with `field_range` when the network ran; a network with no
|
||
layer of tag 0, 1 or 2, or with a header `shift` or `gf` of 128 or more that no layer
|
||
uses, decoded where the shapes allowed. A scan of every committed splat set and
|
||
payload finds none affected.
|
||
- **The interpreter's memory.** A run releases each tensor once its last reader has
|
||
run, takes its inputs by value, and holds a transposed convolution's uncropped plane
|
||
in `i64` (each cell is within the certified `convT.acc`). With its set loaded, a splat
|
||
decode through the operator set holds no more than the payload's declared working
|
||
bytes (integer-payloads §7, *Memory and work*); the declared cost of a `WIS1` payload
|
||
is unchanged. `int_payload::declared_cost` now refuses, with the decode's code, a set
|
||
whose lowering does not load.
|
||
- **The byte-equality matrix** (`wai-byte-equality.yml`, `byte-exact-conformance`) now
|
||
decodes the committed `WIA1` and `WIS1` payloads of `wai.audio.int_codec` and
|
||
`wai.splat.int_codec` on every leg, under the reference and the fast kernels, to their
|
||
pinned PCM and attribute digests; it compiled neither decoder before.
|
||
- **Its `ops` member in the registry** ([integer-operators](extensions/integer-operators.md)
|
||
§11). `registry/capabilities.json` gives `wai.splat.int_codec` the member
|
||
`{"set": "wai-ops/1", "profile": "acc64", "subset": "full", "lowering": "whm1/1",
|
||
"stage": {"rans64": {"table": "t4097", "escape": "lenient", "end": "canonical"}}}`.
|
||
SPEC §5's row cites the payload ([integer-payloads](extensions/integer-payloads.md) §6)
|
||
and the lowering (integer-operators §13.2), and SPEC §7 names the capability among
|
||
those that decode through the operator set.
|
||
|
||
### wai-quantum 0.5.0: provenance, sampling members, device programs, and three more members
|
||
|
||
Breaking: `Certificate` gains a public field, `provenance`, and `FabricClass` is now
|
||
`#[non_exhaustive]`, so a class can be added in a minor release. A silicon certificate
|
||
hashes exactly as before; every pinned digest is unchanged. The `quantum_fabric` feature now
|
||
includes `quantum_toolchain`.
|
||
|
||
- **Provenance in the certificate.** `Provenance::{Silicon, Emulator(name), Simulator(name)}`,
|
||
inside the certificate's hash unless it is silicon. Placement ranks silicon first, then
|
||
emulators, then simulators, so a simulated member never stands in for hardware that is
|
||
present.
|
||
- **Sampling members.** The kernels `wai.quantum.circuit/sample` and
|
||
`wai.quantum.circuit2/sample`, the `Sampler` trait, `certify_sampler` (classical fidelity
|
||
against the exact distribution, over stated shots) and the `qpu-v1` corpus. `VirtualQpu`
|
||
samples a stated noise model (global depolarizing and readout flips) and says it is a
|
||
simulator. `VirtualDevice` adds a coupling map and a native basis: each circuit is
|
||
compiled, the program verified, and the gate errors charged for the program it would
|
||
run, routing included.
|
||
- **Programs for devices** (`quantum_toolchain`). `device::device_qasm` writes OpenQASM 2
|
||
or 3 in the dialect's standard gates only, with every multi-controlled gate lowered
|
||
exactly (dyadic angles throughout; refused, never rounded, past the table) and every
|
||
qubit measured into `c[i]`; `counts_from_bitstrings` and `counts_from_integers` read a
|
||
service's counts back. `native::compile` targets a processor's own basis (`rz`/`sx`/`x`/`cz`,
|
||
`prx`/`cz`, or `gpi`/`gpi2`/`zz` in turns) on its own qubits, routed over its coupling
|
||
map; `NativeProgram::verify` checks a program against its circuit by simulation before
|
||
it is sent. `from_qasm` now reads `ch`.
|
||
- **The OpenCL member** (`quantum_fabric_opencl`, opt-in): the exact 64-bit kernel in
|
||
OpenCL C, built by each device's own compiler; the ICD loader is opened when first used.
|
||
Certified bit-exact on GPUs and CPUs of several vendors; an OpenCL accelerator is refused,
|
||
since no fabric class names it.
|
||
- **The FPGA member, simulated** (`quantum_fabric_fpga`, opt-in): the datapath in Verilog,
|
||
each 32×32 product from four 16-bit partial products, compiled at run time by Verilator
|
||
into a cycle-accurate simulation. Its certificates say they were earned in a simulator.
|
||
- **The dense f64 state vector on accelerators** (`quantum_fabric::sv64`, with
|
||
`quantum_sv`): the CPU's own launches (diagonal with the exact-one skip, the one-qubit
|
||
formula, and ordered row sums), each an IEEE binary64 operation with no fused
|
||
multiply-add, on the CUDA member (`CudaMember::sv`) and on OpenCL devices with binary64
|
||
(`OpenClMember::sv`). Certified against the pinned corpus digest `SV_DEFAULT_PIN`; a
|
||
device without binary64 refuses, saying so.
|
||
- **wai-quantum-qpu 0.1.0** (new crate): clients for three quantum processing protocols
|
||
(`sampler_v2`, `prx_circuits`, `native_turns`), each compiling exactly to the device,
|
||
verifying before submission and reading counts back onto the circuit's qubits;
|
||
`QpuSampler` makes a service a fabric member; the `wai-qpu` command reads a device,
|
||
runs the bit-order probe, and certifies. `curl` is the transport, with secrets kept off
|
||
command lines. Tested against services that follow each protocol's published contract.
|
||
- **CI.** Workflows for the OpenCL member, the simulated FPGA member and the QPU clients;
|
||
the CUDA and OpenCL hardware jobs certify the f64 state vector; the hardware host
|
||
declares its devices in its runner's environment.
|
||
|
||
### wai-quantum 0.4.4: the fabric's CUDA member
|
||
|
||
Additive. The default features, `full`, and every pinned digest are unchanged.
|
||
|
||
- **The CUDA member** (`quantum_fabric_cuda`, opt-in, native targets only): the gate
|
||
kernel in CUDA C with exact 64-bit integers, compiled at run time by the runtime
|
||
compiler for each device's own architecture (`--fmad=false`), one launch per gate. The
|
||
driver and the compiler are loaded when first used, so a build with the feature runs on
|
||
a host without CUDA and finds no member there, saying why. Each device is probed against
|
||
the CPU's arithmetic and certified bit-exact by reproducing the pinned corpus digests; a
|
||
result that would leave 32 bits is refused, never returned.
|
||
- **CI.** `wai-quantum-cuda.yml`: the device tests on a hardware host's GPU (every
|
||
certificate, every kernel mutant caught), and the same build on a host with no CUDA.
|
||
|
||
### Reader limits for the extension formats' durations
|
||
|
||
A score, haptic signal, audio scene, splat4d clip or film declares its length in its
|
||
contract, and rendering it allocates in proportion. Nothing bounded the declaration
|
||
(found while fuzzing: a score's `mixdown` allocated `duration` samples outright).
|
||
|
||
- **The limits** (each extension's §2.1, `wai::limits`), shared with SPEC §2:
|
||
- score and haptics: `duration × channels` at most 2²⁷ samples;
|
||
- audio scene: `duration` at most 2²⁶, so its stereo floor is at most 2²⁷;
|
||
- splat4d (`n_frames`), film (`frames`) and a reel's shots together: at most 2²⁰
|
||
frames.
|
||
|
||
A reader may refuse past a limit (`duration_too_long`), when it reads the contract.
|
||
- **Film tick arithmetic.** `start_tick + f × ticks_per_frame` wrapped in release
|
||
builds and panicked in debug ones. A reader now refuses a schedule whose last tick
|
||
passes 2⁶⁴ − 1, and `frame_tick` saturates rather than wraps.
|
||
- **Tests.** `tests/extension_duration_limits.rs` rewrites a committed vector of each
|
||
format to sit exactly at its limit (it reads) and one past it (refused by name), and
|
||
checks the film schedule at and past 2⁶⁴ − 1. The old readers fail all three tests,
|
||
and every committed corpus verifies unchanged.
|
||
|
||
### Coverage-guided fuzzing of the reader
|
||
|
||
`wai-rs/fuzz/` adds four libFuzzer targets, built by cargo-fuzz with AddressSanitizer:
|
||
- `envelope`: the `WAI1` and `WAI2` readers and the listing form;
|
||
- `int_payload`: every integer capability, against the committed parameter sets;
|
||
- `media`: the decoders of the floor and the recommended set;
|
||
- `containers`: the extension containers.
|
||
|
||
They are seeded from the repository's own vectors. `wai-fuzz.yml` runs each target for
|
||
15 minutes every night and keeps the corpus between runs; it never runs on a pull
|
||
request. `fuzz/run.sh` runs the same targets anywhere a nightly toolchain is installed.
|
||
SECURITY.md now says what is fuzzed and what is not.
|
||
|
||
- **Found and fixed.** In its first seconds, `containers` found the audio scene reader
|
||
reserving entries for a `u32` count read from the input, before it read one: 48 GiB
|
||
asked for by a few bytes. The world, reel, score, haptic and splat4d readers did the
|
||
same. Each now reserves no more than its remaining bytes could hold.
|
||
`tests/container_reservations.rs` rewrites a committed vector of each to declare
|
||
2³² − 1 entries in four bytes and requires a refusal with no allocation past a few
|
||
megabytes; on the old readers the world asset table alone asked for 256 GiB.
|
||
|
||
### Reader limits for picture, audio and video payloads
|
||
|
||
The last decoders that allocated what a payload declared now hold it to limits
|
||
(SPEC §2), as manifests and text payloads already were.
|
||
|
||
- **The limits.** A reader must accept a picture up to 65 536 pixels on a side and
|
||
2²⁷ pixels, audio up to 2²⁷ samples counting every channel, and AV1 up to 2²⁰
|
||
temporal units of such pictures. It may refuse past them (`image_too_large`,
|
||
`audio_decoded_too_long`, `video_too_large`), from what the payload declares wherever
|
||
it declares it. The writers refuse the same.
|
||
- **Payload framing** (SPEC §5). The AV1 framing that the reference has always used,
|
||
and packaging §7 writes, is now in the standard itself. So are the FLAC and Ogg Opus
|
||
forms, and the rule that an image payload decodes only as its own format.
|
||
- **Defects fixed in the reference decoders.**
|
||
- A FLAC `STREAMINFO` declaring 2³⁶ − 1 samples made the decoder ask for 256 GiB up
|
||
front.
|
||
- A 24-bit FLAC decoded to about ±256 instead of ±1.
|
||
- An Ogg Opus stream whose source rate was not an Opus rate (44.1 kHz) was refused.
|
||
- `png_decode` and `jpeg_decode` decoded any format the bytes resembled.
|
||
- `jxl_decode` refused a deeper-than-8-bit picture and returned one or four channels
|
||
where it promised three.
|
||
- The AV1 decoder decoded every packet before truncating to `n`. It panicked on
|
||
monochrome, misread 4:2:2, 4:4:4 and 10- and 12-bit frames, and let dav1d allocate
|
||
whatever a sequence header declared.
|
||
- **Streaming AV1.** `av1_decode_each` hands on one frame at a time. `av1_decode` and
|
||
`wai_video_av1_decode`, which return every frame at once, refuse past 4 GiB of output
|
||
from the header (`video_decoded_too_large`). `wai decode` streams frames to its file.
|
||
- **Unchanged.** Every 8-bit 4:2:0 frame decodes byte for byte as before. The AVIF path
|
||
now calls libavif directly with the reader limits as its decoder limits (it drops
|
||
`libavif-image` and the rav1e build inside libavif), and `include/wai.h` is
|
||
regenerated.
|
||
- **Tests.** `tests/media_decode_bounds.rs` checks each limit at its edge and one past
|
||
it, and each defect above. CI runs it in `codec-wrappers`. Five mutations, each
|
||
reintroducing one defect, fail it.
|
||
|
||
### Vector graphics (Draft): a title card and credit roll in the corpus
|
||
|
||
`wai/vector-conformance/` gains `motion/stroke-titles`: a 1280 × 720 document at 25 fps
|
||
in the default stroke set, a lower third that slides in, holds and fades as a group, and
|
||
a credit roll that rises three design pixels a frame. Each line's window is cut to the
|
||
frames at which it meets the canvas (graphics-vector §5.4, *Where a block is*). Every
|
||
frame's digest is in its expected file, so the native, WASI, Node and three-engine
|
||
browser legs and the independent checker each check all of them.
|
||
|
||
### `wai.neural.int_synth` through the integer operator set
|
||
|
||
The first capability whose reference decoder decodes through `wai-ops/1`.
|
||
|
||
- **The decode.** `int_synth::decode_bytes` checks the payload
|
||
([integer-payloads](extensions/integer-payloads.md) §13), lowers the set by
|
||
`int_synth/1` ([integer-operators](extensions/integer-operators.md) §13.1), loads the
|
||
graph under `acc64` (load steps 1–8, the last being the bounds certificate) and runs it
|
||
through the operator set's interpreter, with the fast transposed-convolution kernel
|
||
behind it (`wai-rs/src/int_ops.rs`). Every committed output is unchanged, byte for
|
||
byte, on every platform the byte-equality matrix covers and in the browser crate, and
|
||
every committed refusal keeps its code (`payload_malformed`). The direct decode it
|
||
replaced is kept, test-only (feature `legacy_int`): `tests/int_ops_differential.rs`
|
||
decodes random sets and payloads both ways and requires the same output or both
|
||
refusals outside the planned breaks, and `wai-ops-differential.yml` runs it for an
|
||
hour in each build profile every night.
|
||
- **Planned breaks** (SPEC §8, integer-operators §15.3): new refusals of input no
|
||
committed corpus contains. `wai.neural.int_synth` refuses a payload whose reserved byte
|
||
is not 0, whose `C` is 0 or whose latents exceed `2^26` values; a set with no layers,
|
||
with a layer whose `out_ch`, `k` or `stride` is 0, with a field of `2^32` or more, or
|
||
with a shift of 128 or more; a set or payload a cap refuses (a tensor, or a transposed
|
||
convolution's uncropped output, past `2^26` elements); and a set whose certificate does
|
||
not meet `acc64`. A shift from 64 to 127, which a build used to mask or panic on, now
|
||
has its exact meaning. A scan of every committed `int_synth` set and payload finds
|
||
none affected. The text now states the two rules the lowering already made and the
|
||
payload text did not: at least one layer, and `out_ch ≥ 1`.
|
||
- **Its `ops` member in the registry** ([integer-operators](extensions/integer-operators.md)
|
||
§11). `registry/capabilities.json` gives `wai.neural.int_synth` the member
|
||
`{"set": "wai-ops/1", "profile": "acc64", "subset": "full", "lowering":
|
||
"int_synth/1"}`, and the schema describes the member. SPEC §5's row cites the payload
|
||
and set ([integer-payloads](extensions/integer-payloads.md) §13) and the lowering
|
||
(integer-operators §13.1), and SPEC §7 says that a `NeuralIntegerExact` capability
|
||
that decodes through the operator set names it in its `ops` member.
|
||
|
||
### Vector graphics (Draft): the sink as a WebAssembly 1.0 module, in three browser engines
|
||
|
||
Additive. No payload, outline set, vector or digest changes.
|
||
|
||
- **The sink's exports.** `wai-vector/wasm` exports the sink of
|
||
[graphics-vector](extensions/graphics-vector.md) behind a C ABI, with no bindings
|
||
generator and no imports. `wv_add_set` holds an outline set a payload may name;
|
||
`wv_open` loads a payload to a handle, or to its refusal's code; `wv_render` gives a
|
||
frame's `RGBA8P` at a listed size and rate, or the request error's code; `wv_cost`
|
||
gives a listed size's cost; `wv_transcript` gives the transcript, and `wv_projection`
|
||
the projection at a frame of a listed rate, each as the JSON of §5.4, or the request
|
||
error's code; `wv_sha256` gives a digest; `wv_close` releases a handle. A result is
|
||
read at `wv_out_ptr`, `wv_out_len` bytes of it. No export takes or returns a 64-bit
|
||
integer: a frame number goes in as two halves.
|
||
- **The package.** `wai-vector/wasm/build-pkg.sh` builds the module a site serves: the
|
||
sink's exports alone, with no glue script. It is WebAssembly 1.0 and nothing later:
|
||
no SIMD, no threads, no bulk memory, no sign-extension operators, no reference types.
|
||
The target is `wasm32v1-none`, whose precompiled `core` and `alloc` are 1.0
|
||
themselves, so the crate is now `no_std` and carries its own allocator. The build
|
||
then reads the module back: `wasm/check-mvp.mjs` decodes every section, type, segment
|
||
and instruction, and refuses anything later than 1.0, any import, any 64-bit integer
|
||
at an exported function, any floating point, and any exported function the script
|
||
does not list.
|
||
- **Three browser engines.** `wai-vector/browser/run.mjs` serves a page, the package and
|
||
the corpus, and in each engine loads every payload of `vector-conformance` to its
|
||
expected code, checks its SHA-256 and each listed size's cost, and renders its frames
|
||
through the page's hook, `window.__wai_overlay.frame(size, rate, g)`, which returns
|
||
the renderer's own buffer: every listed frame at sizes up to 1280 × 720, and sampled
|
||
frames above. The page hashes each frame with the module's SHA-256 and with the
|
||
platform's digest. The sampled frames are also sent to Node as bytes, and Node hashes
|
||
them itself. Where a payload's expected file states a text record, the page's hook
|
||
also gives the sink's transcript and its projection at every frame of every listed
|
||
rate, `transcript()` and `projection(rate, g)`; written out as the record's lines,
|
||
they must be the committed `<name>.a11y.jsonl` byte for byte. The recomputation
|
||
module then recomputes every expected file and authoring form in each engine.
|
||
Nothing is read from a canvas. A run passes only when all three engines ran: a
|
||
missing engine is a failure.
|
||
- **CI.** `wai-vector.yml` gains the `browser` job: the package built and read back,
|
||
then the three engines, in a container image pinned by digest.
|
||
|
||
### Vector graphics (Draft): text and accessibility, §5 of `wai.graphics.vector`
|
||
|
||
- **[graphics-vector](extensions/graphics-vector.md) §5 is written.** A payload carries
|
||
its words beside its glyphs, and this revision says what a sink computes from them and
|
||
what it owes the viewer:
|
||
- **The projection and the transcript, byte for byte (§5.4).** A block's active set is
|
||
the union of the effective windows of the runs and paths that draw it, cut to the
|
||
document's window, sorted, with windows that overlap or touch merged. The projection
|
||
at a frame is every block active at the frame's tick, in reading order; the
|
||
transcript is every block with its active set. Both are JSON with a fixed member
|
||
order and exactly three string escapes. A block's `purpose` is written as the byte
|
||
carried, an unregistered code included; `mode` is in every node; a ruby link is
|
||
written as the base block's index with the range's start and length; `decorative`
|
||
blocks are in neither. Opacity, position and clips do not enter either value. A
|
||
payload's *text record* is its transcript and every frame's projection at every
|
||
listed rate, one value a line: text-equivalence is byte equality of it.
|
||
- **Encoder rules (§5.1, §5.3)** that a verifier checks and a sink does not: one base
|
||
direction for a block, one joining sequence in one run, vertical text and its
|
||
`mode`, Normalization Form C, every byte of a string in exactly one cluster, no
|
||
zero-glyph cluster for a visible character, and no colour glyphs.
|
||
- **The purpose vocabulary (§5.2),** with how each purpose is exposed and whether it is
|
||
announced by default.
|
||
- **Exposure (§5.5).** A presenting sink exposes the current projection to the
|
||
platform's accessibility interface, updates it when the projection changes, inserts
|
||
strings as text and never as markup, and keeps within stated announcement limits.
|
||
What assistive technology then says is not claimed.
|
||
- **Whether the words match the glyphs (§5.6):** the chain from a parent font to an
|
||
outline set, from a text block to glyph IDs, and from glyphs to the overlay layer;
|
||
what an authoring receipt records; and what the chain does not reach.
|
||
- **Captions under the viewer's control (§5.7).** Captions travel as timed text. A
|
||
sink that cannot read the platform's caption preferences does not draw `caption` or
|
||
`subtitle` items, and a frame it draws without them is not the published frame.
|
||
- **`wai-vector`: the text layer.** `wai_vector::text` computes `text_hash`, block
|
||
identities, effective windows, active sets, the projection, the transcript and the
|
||
text record, in a sink's build, and a verifier's check of the cluster rule. The tool
|
||
gains `wai-vector a11y` (the text record, the transcript, or one frame's projection)
|
||
and `wai-vector text` (the text digests and each block as a reviewer reads it). The
|
||
corpus-seeded fuzz holds the text layer of every payload it accepts: the transcript
|
||
is JSON that gives back each tag and string, and a projection lists exactly the
|
||
blocks the frame procedure draws at that tick.
|
||
- **`vector-conformance`: the `a11y` group.** Each payload's expected file states the
|
||
SHA-256 of its text record, and the record is committed beside it, so every fabric leg
|
||
recomputes it: the worked example of §5.4, windows and a fade to nothing, a
|
||
`decorative` block, text alternatives of paths, reading groups, ruby over part of a
|
||
base, registered and unregistered purposes, merged and clipped intervals, windows cut
|
||
by enclosing groups, the three writing modes, four rates over one document, strings
|
||
that take each escape and strings that take none, and a payload with no text. The
|
||
`canonical` and `refusals` groups gain text-block cases: an ungrouped block between
|
||
groups, ruby links to a later block and ranges that end inside a character or at the
|
||
string's end, and pairs that pin where `TEXTS` sits in the record pass and that the
|
||
text-block rules come before any outline set is read.
|
||
|
||
### Reader limits that bound memory: manifest values, and text decode
|
||
|
||
Two reader limits join the manifest length limit (SPEC §2, *Reader limits*), and with
|
||
them a reader's memory is bounded by what the specification states, not by what an
|
||
envelope asks for. `SECURITY.md` listed both as known gaps; it now states the bounds
|
||
and the tests that measure them.
|
||
|
||
- **Manifest values.** A reader need accept no manifest of more than 2²² JSON values,
|
||
the manifest object and every value in it at any depth. The reference readers count
|
||
the values before they build anything, refuse one past the limit
|
||
(`manifest_too_many_values`), and build what they read at the size counted: at most
|
||
128 bytes a value and one a byte on a 64-bit target, where a manifest of small
|
||
values at the length limit took several gigabytes to parse. The stdlib Python
|
||
verifiers refuse the same manifests, and the HTTP corpus holds a case at the limit
|
||
and one past it.
|
||
- **Text decode.** A `wai.text.zstd` or `wai.text.xz` payload decodes to at most 2²⁷
|
||
bytes and declares at most 2²⁷ bytes of window or dictionary; a reader may refuse
|
||
more (`text_decoded_too_long`, `text_window_too_large`). The reference decoders
|
||
write into a buffer never sized past the limit, refuse a Zstandard frame that
|
||
declares a longer content size before decoding anything, and give the decoding
|
||
library the window limit, so a declaration past it is refused from the stream's
|
||
header. The reference encoders refuse input past the length limit. The C entry
|
||
points `wai_text_zstd_decode` and `wai_text_xz_decode` are bounded the same way; the
|
||
header gains `TEXT_DECODED_MAX_LEN` and `TEXT_WINDOW_MAX_LEN`.
|
||
- **Planned break for writers.** A conforming encoder writes no manifest past the value
|
||
limit and no text payload past either text limit. No committed envelope or vector is
|
||
past any of them, and no digest changes.
|
||
- **The codec wrappers in CI.** `wai-conformance.yml` gains `codec-wrappers`, on the
|
||
macOS runner: the wrappers' fuzz smoke, the text decode limits and the C entry
|
||
points, built with `codecs`. The step fails if any of its test binaries ran no test,
|
||
which is what a build without the feature does.
|
||
|
||
### Integer operators: the reference crate, the bounds pass and its certificates
|
||
|
||
The crate, the tool and the corpus that
|
||
[integer-operators](extensions/integer-operators.md) (Draft) names exist. No capability
|
||
decodes through them yet: each capability's reference decoder is still the one its
|
||
payload extension names.
|
||
|
||
- **`wai-ops`**, the reference crate, `no_std` with `alloc` and no dependencies: the
|
||
512-bit saturating integer of §8.1, the canonical JSON writer of §8.5, the `WHG1`
|
||
parser and writer, the load-time and run-time plans, every graph operator of §5, the
|
||
stage operators E1–E13, and the lowerings of §13.
|
||
- **Load step 8.** `wai_ops::bounds` runs the bounds pass of §8.1–§8.5 and writes the
|
||
canonical certificate. The interpreter runs only a loaded graph, so nothing decodes a
|
||
parameter set its certificate does not admit. `interp::run_observed` reports every
|
||
value a run forms; `bounds::Within` holds a run to its certificate, and every golden
|
||
decode runs that way.
|
||
- **`wai-verify`**: `bounds`, `lower` and `vectors`, with `--check`, `--witness` and
|
||
`--advise`.
|
||
- **`wai/ops-conformance/`**: per-operator, stage, graph, load and lowering vectors, the
|
||
lowered graph of every committed parameter set, and each set's certificate.
|
||
- **Two certifiers.** `tools/wai_ops_check.py`, written from the extension text and
|
||
sharing no code with the crate, recomputes every vector and every certificate byte
|
||
for byte.
|
||
- **`wai_ops::limb32`**, a reference model of the 32-bit arithmetic the `acc32` profile's
|
||
two products need, checked against 64-bit arithmetic.
|
||
- **Model checking.** The operator set's harnesses are in `wai-ops/src/proofs/`, hosted
|
||
by `wai-verify` and run by `wai-formal-verification.yml`. A harness that does not yet
|
||
verify within the job's budget is kept, behind `cfg(kani_open)`, and is not run or
|
||
counted as proved.
|
||
- **`wai-ops.yml`** runs the crate's tests and `vectors verify` natively, under
|
||
wasm32-wasip2 and under wasm32 in Node, and runs the checker.
|
||
|
||
### wai-quantum 0.4.3: metered runs, placement by run, and the fabric wrapper
|
||
|
||
Additive. The default features, `full`, both implementations of the WebGPU member, and
|
||
every existing pin and digest are unchanged; runs, receipts and members stay unmetered
|
||
unless a caller supplies a meter.
|
||
|
||
- **Run meters.** `quantum_fabric::RunMeter`: `start` just before a run's first
|
||
submission, `stop` after its last readback, returning the microjoules in the window and
|
||
their `EnergyClass`, or none when the window cannot be attributed to the run alone. The
|
||
trait is member-neutral, and no implementation ships in the crate. A reading reaches a
|
||
result only when `check_label` accepts it; otherwise the run is unmetered (zero, no
|
||
class). Every start is matched by one stop, a refused run's reading is discarded, and a
|
||
meter that cannot start is never stopped.
|
||
- **The WebGPU member, metered.** `WebGpuMember::simulate_metered(c, start, meter)`
|
||
returns the state with the accepted reading. Its window runs from the first submission
|
||
to the last readback: the device's work, and the host's waits and copies between them;
|
||
planning, buffer creation, the upload's staging and the conversion to a state vector
|
||
are outside it, and the member's `detail` says so. A circuit refused
|
||
before any submission opens no window. `member_metered(corpus, meter)` certifies as
|
||
`member` does and gives each entry the figure its runs earn;
|
||
`blocking::{simulate_metered, member_metered}`.
|
||
- **Placement figures from metered calibrations only.** `EnergyCalibration` (`new`,
|
||
`add`, `runs`, `finish`) turns metered runs into a `KernelEntry::energy` figure: the
|
||
total energy over the operations performed (`pair_updates`: one pair of amplitudes under
|
||
one gate), in nanojoules rounded up, with the runs' class, whose uncertainty is the
|
||
largest of the runs' plus what the rounding adds, over their windows together. An
|
||
unmetered run, a refused label, a second class, or an uncertainty that no longer fits
|
||
its field once the rounding is added leaves no figure.
|
||
`detect_local_metered(corpus, meter)` gives the CPU reference its figures. Because
|
||
`place` ranks a known figure before none, figures go on the CPU reference and on the
|
||
members placement compares it with, or on none. `place` is unchanged; `place_for` and
|
||
`Fabric` hold the rule themselves, and read figures on only some candidates as figures
|
||
on none.
|
||
- **Placement for a run.** `place_for(members, kernel, required, predict_pj)` places by
|
||
the least predicted picojoules when every candidate has a prediction, ties falling as
|
||
in `place`. Otherwise predictions are not used, and the order is `place`'s, except that
|
||
figures on only some candidates count as figures on none. Under synthetic models with a
|
||
fixed cost per GPU run, a 3-qubit circuit stays on the CPU and a 24-qubit one goes to
|
||
the GPU.
|
||
- **The fabric wrapper.** `Fabric` holds members with their `Runner`s (`CpuReference`,
|
||
and `WebGpuMember` for its own implementation) and optional meters (`add`,
|
||
`add_metered`, `set_model`, `order`, `run`). `run` places the circuit, falls back down
|
||
the placement order when a member refuses with `MemberError::{TooWide, OutOfRange,
|
||
Memory, Timeout, Device}` (`falls_back`), stops at an invalid circuit or a defect, and
|
||
seals the `FabricReceipt` with the member and implementation that produced the result
|
||
and its accepted reading. The refusals (`Refused`) are listed beside the receipt in
|
||
`FabricRun`, or in `FabricError`; the receipt's signed bytes are unchanged.
|
||
`WebGpuError` converts into `MemberError`. A runner states the member it is
|
||
(`Runner::member_id`), and a run that reaches a member paired with a runner of another
|
||
id is refused there as a defect, so a receipt cannot name a member that did not produce
|
||
the result. `ready(future)` gives the output of a future that is ready on its first
|
||
poll, which is how a native caller drives `run` without an executor; `&R` is a `Runner`
|
||
and `&mut M` a `RunMeter`.
|
||
- **Tests.** CPU-side tests, native and under wasm32-wasip2 (each required by name in the
|
||
`quantum-wasip2` job), cover the readings, the calibration, figures on both members or
|
||
neither, `place_for`, the fallback, and metered fabric receipts. The device test
|
||
`g13_metered_runs_and_the_fabric` runs the member under a test meter, calibrates it on
|
||
random-v1 with the pinned digests, and runs the fabric with the member held to three
|
||
qubits: a wider circuit falls back to the CPU reference, a narrower one is produced on
|
||
the member and metered. It runs the tile implementation the same way, whose receipt
|
||
names it, and requires a member paired with another member's runner, or with the other
|
||
implementation's, to be refused. The member script requires it, and a fabric line per
|
||
declared adapter.
|
||
|
||
### wai-quantum 0.4.2: tile runs, and the WebGPU member in a browser
|
||
|
||
Additive. The default features, `full`, the per-gate implementation and every existing pin
|
||
and digest are unchanged.
|
||
|
||
- **Tile runs: a second implementation, `wgsl-lanes32-tile/54e443520c020013`.**
|
||
`plan::Config` (`PER_GATE`, `TILED`; `tile_size`, `run_cap`), `plan::Step::Tile`,
|
||
`compile_with`, `schedule`, `execute_from`, `PlannedGate::{new, is_identity,
|
||
is_well_formed}` and `TILE_BITS_MAX`; `PLAN_REVISION` is 2. A tiled plan applies each
|
||
maximal run of consecutive gates that act inside a tile (diagonal gates on any target,
|
||
others with their target inside the tile) to tiles of up to 1024 amplitudes, in
|
||
op-log order, and leaves out exact identities only (circuit/1's `P(19..=32)` is
|
||
`2^30 − 1`, not the identity, and stays). The CPU executor mirrors a tile kernel's
|
||
index arithmetic and equals the per-gate program on any words; every configuration
|
||
certifies with the lanes32 pins. A program whose gate's `diag` disagrees with its
|
||
words, or whose op index is `u32::MAX`, is refused by the executor and by the member.
|
||
`wgsl::TILE_SHADER` (`lanes32.wgsl` followed by `lanes32_tile.wgsl`, entry point
|
||
`apply_tile`: one workgroup of 256 per tile, the tile updated in place in the state
|
||
planes, a storage barrier between gates, the tile size in the run record, never in
|
||
the text; a run record whose tiles or grid do not cover the state exactly once is
|
||
refused) has its own id (`tile_implementation_id`), records (`tile_gate_record`,
|
||
`tile_run_record`), grid (`tile_grid`) and mutants (`TILE_MUTANTS`, with the
|
||
detectors `Race` and `Malformed`, `race_witness`, `malformed_tile_run`).
|
||
`WebGpuMember::tiled()` gives the same device running it, probed and certified on its
|
||
own; the member splits a run into dispatches of at most `SUBMIT_BUDGET` pair-updates.
|
||
Discovery opens per-gate members, which are the fallback. Given both implementations'
|
||
entries for one member and no energy figures, `place` breaks the tie by
|
||
implementation name and places the tile implementation (`wgsl-lanes32-tile/…` sorts
|
||
first). The checker holds `TILE_SHADER` to its own rules, barrier placement and
|
||
barrier uniformity included, and its SPIR-V to the validator; the member's device
|
||
tests, the member script and the browser leg require the four pinned digests under
|
||
both implementations and every tile mutant caught. `tests/fabric_expected.rs` (not
|
||
in the package) holds `quantum-conformance/fabric/expected.json` to the crate.
|
||
- **The member in a browser.** wai-web's feature `quantum_webgpu` binds the member for JS
|
||
(`quantum_webgpu_discover`, `QuantumWebGpu`: `certify`, `statevector_hash_hex`) in a
|
||
bundle of its own (`--out-name wai_quantum_webgpu`); the default build and its bundle
|
||
are unchanged. `demo/quantum_webgpu.html` runs the member in a worker, its kernel
|
||
compiled by the browser's own WGSL compiler. `quantum-conformance/browser_webgpu.mjs`
|
||
drives the page headless and requires the four certificates bit-exact with the pinned
|
||
digests (`quantum-conformance/fabric/expected.json`), every vector's statevector hash,
|
||
every reject file refused with its class, and a circuit wider than the member holds
|
||
refused. The `checks` job builds the bundle, and a `browser` job runs the page on macOS.
|
||
|
||
### wai-quantum 0.4.1: the fabric's WebGPU member
|
||
|
||
Additive. The default features, `full`, and every existing pin and digest are unchanged.
|
||
|
||
- **Certification one case at a time.** `quantum_fabric::Certification` (`new`, `add`,
|
||
`cases`, `finish`) folds a member's outputs into a certificate as they arrive, for a
|
||
member that produces them asynchronously. `certify` is now a loop over it and gives the
|
||
same certificates.
|
||
- **Corpora.** `Corpus::Random { seed, cases }` is the existing random corpus
|
||
(`random-v1`). `Corpus::Fabric { seed, cases, wide }` (`fabric-v1`) continues the same
|
||
stream after it with circuits of 11 to 17 qubits (`lanes::random_wide_circuit`), so that
|
||
every dispatch shape a 32-bit-lane member uses is exercised. `FABRIC_DEFAULT` is seed 7,
|
||
200 + 24 circuits; `GENERATOR_FABRIC`, `GENERATOR_FABRIC2` and `fabric_generator` name
|
||
it per gateset, and `Corpus::{generator, name, circuits}` describe any corpus. The
|
||
fabric-v1 lanes32 digests are pinned: `41c726a0…3df7` (circuit/1) and `9a84b55a…7809`
|
||
(circuit2). `lanes::certify_corpus` certifies the lane core on any corpus;
|
||
`certify_lanes` is it on the random corpus, with the same generator string.
|
||
- **The lane core, word by word.** `lanes::GateWords` and `gate_words` (a gate as the
|
||
eight 32-bit matrix words and the target and control masks a kernel reads),
|
||
`fxmul_wide` (the exact 64-bit `fx`), `PROBE_EDGES`, `apply_words` and `run_words` (the
|
||
general 2×2 path computed from exact 64-bit values, flagging a gate exactly when the
|
||
reference's result leaves 32 bits, and refusing malformed words), `simulate_checked`,
|
||
`output` and `reference_output`. `lanes::horizon(n, gs)` is the gate count below which a
|
||
valid circuit provably cannot leave 32 bits, computed in integers from the gateset's
|
||
table: 46,340 (circuit/1) and 46,339 (circuit2) at 26 qubits. The module documentation
|
||
and `fxmul` no longer claim that every result of a valid circuit fits in 32 bits; it
|
||
holds within the horizon.
|
||
- **`quantum_fabric::plan`**: `Program` (one step per op, the start reduced modulo 2^n),
|
||
`PlannedGate`, `Step`, `compile`, `LaneRun` and `execute`, the CPU executor a member's
|
||
output is held to; `PLAN_REVISION` is 1.
|
||
- **`quantum_fabric::wgsl`**: `SHADER`, the lane core transcribed into WGSL
|
||
(`src/quantum_fabric/lanes32.wgsl`: unsigned 32-bit values only, an exact 64-bit copy
|
||
of every result, literal shift amounts, no float, division, conversion, workgroup
|
||
memory or barrier); `implementation_id()` (`wgsl-lanes32/dc8e19f61e0e87ab`, a BLAKE3
|
||
over the shader text and `HOST_LAYOUT`, so any edit is a new implementation that earns
|
||
its certificates again); `gate_record`, `Grid`, `grid` and `gate_grid` (the 64-byte gate
|
||
record and the dispatch grid; `WORKGROUP` and `X_GROUPS_MAX` are 64); `AdapterIdentity`
|
||
with `member_id` (`local/gpu-…` or `local/swgpu-…`, sorting after every
|
||
`local/cpu-<arch>`), `key` and `class`; and `MUTANTS`, `mutate` and `Detector`, the
|
||
shader mutants every device must catch. The module documentation carries the argument
|
||
that the kernel is bit-exact. `wai/tools/quantum-wgsl-check` (unpublished) holds the
|
||
shader to that argument's rules on naga's IR and translates it to MSL, SPIR-V and HLSL.
|
||
- **The WebGPU member, feature `quantum_fabric_webgpu`** (opt-in, not in `full`). The
|
||
kernel through the platform's GPU interface natively (Metal, Vulkan, DX12) and through
|
||
a browser's WebGPU on `wasm32-unknown-unknown`; wgpu 30.0.1 enters only those two target
|
||
tables, and on every other target, `wasm32-wasip2` included, the module is compiled
|
||
out. `discover()` opens each adapter at the WebGPU downlevel limits with only the buffer
|
||
limits raised, pins the DX12 shader compiler (translator `naga+dxbc`), probes the
|
||
device's arithmetic against the lane core, and lists each adapter it cannot use with
|
||
the reason (`Discovery`, `Refusal`). `WebGpuMember` runs lanes, programs and circuits,
|
||
certifies a gateset on a corpus, and builds a fabric `Member`; a bit-exact member
|
||
reproduces the pinned random-v1 and fabric-v1 digests. A result that would leave 32
|
||
bits is refused (`WebGpuError::OutOfRange`), never returned. Every entry point is async
|
||
with no executor; natively `blocking` resolves each call in one poll, and a future still
|
||
pending is an error. Natively each submission is waited on before the next, so
|
||
`POLL_TIMEOUT` bounds one chunk of gates, not a run; a failed wait, which the GPU
|
||
interface reports by panicking, is caught and returned as `WebGpuError::Device`, and
|
||
the member treats its device as lost from then on (a `panic = "abort"` build still
|
||
aborts). With no energy figures, placement keeps the CPU reference, and a receipt over
|
||
a member's run is unmetered.
|
||
- **wai-rs.** The feature `quantum_fabric_webgpu` forwards to it and re-exports the
|
||
module. `wai_quantum_vectors verify <dir> --webgpu` runs the corpus check once per
|
||
WebGPU member, each state computed by the member; each accept file must also run on the
|
||
member as it does natively, so the 255-qubit files are refused as `too-many-qubits`.
|
||
Without the feature `--webgpu` is an error; with no member it exits 2.
|
||
- **CI.** `wai-quantum-webgpu.yml` checks the shader under naga and a SPIR-V validator,
|
||
the browser build, the feature's absence from the `wasm32-wasip2` dependency tree, the
|
||
shader file in the package, and the MSRV; and runs the member's tests
|
||
(`scripts/wai-quantum-webgpu-member.sh`) on software Vulkan, Metal, and a discrete GPU
|
||
through Vulkan. Each member job declares the adapters its runner must have, and fails
|
||
if the adapters found differ, if any test did not run, if a declared adapter lacks a
|
||
bit-exact certificate for each pinned digest, or if a shader mutant survives. The
|
||
`wasm32-wasip2` job requires the fabric's new unit tests by name.
|
||
|
||
### Packaging: refinement layers, stills, a piece's ladder, live windows and delivery
|
||
|
||
- **Refinement layers in packages** ([packaging](extensions/packaging.md) §5.4). For
|
||
each refinement rung of a piece's ladder the packer writes a `wai.video.int_refine`
|
||
object over the piece's `wai.video.int_motion` base: the manifest `wai stage refine`
|
||
writes, and a `WIR1` from `wai-rs`'s refinement encoder (`staged::wir1::encode`), one
|
||
unit per frame, toward the source from the stage below as a sink reconstructs it. Each
|
||
layer is applied to the decoded base by the reference sink before it is kept; the
|
||
index states each prefix's tier, digest, SSE and deadline.
|
||
- **A piece's ladder, and stills** (§5.7). A rule's `then.ladder` chooses the rungs a
|
||
piece is coded in; otherwise the component's first base rung, its refinement chain and
|
||
its floors. §3.4 item 10 now requires a rule's ladder to name exactly one base, every
|
||
refinement over a rung it names, and every floor of its component. A base of
|
||
`wai.image.png` is a still: the piece's first frame, held for the piece, as a PNG every
|
||
byte of which is stated (the `Sub` filter, one fixed-Huffman deflate block whose
|
||
matches are stated), so an independent implementation writes the same file.
|
||
- **Catalogs** (§8.3, §8.4). The MoQ catalog is now a stage set of moq-streaming-format
|
||
§4.1, written by `wai::staged::moq`: `wai_staged` on every track, a receipts track and
|
||
an alias per media track, `depends` on a track of every layer below; it replaces
|
||
`priority` (MOQT's own, §8.5) and `wai_layer`, which the registry no longer lists. Over
|
||
HTTP, whose variants cannot mix bases with no capability in common (http-delivery §4.2),
|
||
a still's piece is played from the floor; its stage view and MoQ tracks list it.
|
||
- **Live windows** (§9.2). `wai-pack live` packs a Y4M stream from standard input as it
|
||
arrives, with the coder the on-demand packer uses, in windows sealed by version-2
|
||
receipts chained `live-window`; its bases and refinements are the on-demand package's,
|
||
byte for byte. `wai-pack listing --primary` lists what the two share. It writes as it
|
||
goes: each object when it is published and each window when it is sealed, every file
|
||
whole, and the plan file and `LISTING` at the end. It does not keep an object's bytes
|
||
once written, or a record per file: each listing line goes to a hidden file and is
|
||
sorted into `LISTING` at the end. A source that fails mid-stream keeps every sealed window and the objects
|
||
it lists, removes the rest, and exits 1 saying what it kept.
|
||
`wai::pin::listing_from_sha256` writes a listing from per-file digests.
|
||
- **Stills read by PNG's rules** (§5.7). Both reference readers take a PNG of 8-bit RGB
|
||
without interlace and agree on what that admits. They read a suggested palette,
|
||
ancillary chunks and split image data. They refuse an interlace, bytes after `IEND`
|
||
or after the zlib stream, a short row, a zlib window past 32 KiB, and every other
|
||
chunk fault (`stills/`). Both inflate as the zlib library does: no over-subscribed
|
||
Huffman code, and no incomplete one but a lone one-bit code. wai-pack's reader has
|
||
its own inflater, and `png_diff.py` runs it against `pack_verify.py`'s zlib.
|
||
- **The longest piece** (§3.4, §4.4). `max_ms` is at most ten minutes, and at most
|
||
`2^24` units on every component's timebase (`policy-piece-bounds`). A verifier
|
||
refuses a piece longer than the policy's `max_ms` before it decodes a frame. It
|
||
checks each floor object's unit count before decoding (§11.3). It hashes a still's
|
||
frame held for its piece without repeating the frame.
|
||
- **Manifests checked by both verifiers** (§11.3). A base's manifest must be §5.2's and a
|
||
refinement's §5.4's (`object-manifest`, a new reason). A base that does not decode
|
||
is `prefix-digest`.
|
||
- **A repeated object over HTTP** ([http-delivery](extensions/http-delivery.md) §4.2).
|
||
A repeated object contributes once, with its layers, and a layer joins every
|
||
occurrence of its base. `http_verify.py` and `wai-rs` now agree on a playlist's
|
||
codecs and floor where a piece repeats. `pack_verify.py` checks every golden playlist
|
||
with `http_verify.py`'s union, floor and adjacency checks.
|
||
- **One stall bound.** `stage_measure::sim::stall_bound_for` is the only bound: a
|
||
model's, which takes each refinement's own timeout. wai-web's simulator prints it, as
|
||
the native tool does.
|
||
- **Delivery, simulated** (§8.9). `wai_pack::delivery` and `wai-pack sim` run
|
||
staged-measurement's simulator over what a live packer publishes and what a class
|
||
subscribes to: per-track delivery timeouts, §8.5's priorities, the subscriber's cap,
|
||
group receipts on each track's chain. The simulator's model gains a refinement's own
|
||
`timeout_us`, and §10.4's bound takes the longest refinement timeout
|
||
([staged-measurement](extensions/staged-measurement.md) Appendix B); its corpus adds a
|
||
per-track model.
|
||
- **The corpus**: the golden `video-staged-01` (a moving shot, a bumper played twice, a
|
||
still shot, a second moving shot; two refinement layers, a still, the event's deadline,
|
||
three classes), its supplied floor and how it was written (`supplied_floor.py`),
|
||
`staged-mutations/`, `delivery/`, `stills/` and `live/` (the golden packed live in
|
||
windows of two pieces, whose chain `pack_verify.py` checks). `stage_diff.py` runs
|
||
refinement layers, stills (wide ones among them, so every deflate distance code is
|
||
used), piece plans and deliveries differentially against `pack_verify.py`, which
|
||
re-derives every layer and still from the source byte for byte. `sim_diff.py` runs the
|
||
delivery simulator differentially against `sim_verify.py`. `pack-refinement-unsupported` and
|
||
`pack-rule-ladder-unsupported` are gone.
|
||
- The base golden `video-base-01` keeps its objects, index and receipt; its MoQ catalog
|
||
and its `LISTING` change.
|
||
|
||
### The record made true: training, paths, protected content, reader limits, security
|
||
|
||
- **`int_synth`'s weights are quantised after float training.** SPEC §5 and
|
||
[determinism-tiers](extensions/determinism-tiers.md) §5 said the capability decodes
|
||
"QAT weights". No committed model was trained that way: the trained corpus vector,
|
||
`photo`, was made by `tools/wai_int_synth_real.py`, float training then post-training
|
||
quantisation with each layer's shift derived from its measured activation range, and
|
||
the weights of the synthetic vectors and of the cross-architecture golden come from a
|
||
seed. Both texts now say so, and so do the tools:
|
||
`tools/wai_int_synth_qat.py` is marked experimental (it trains against one fixed shift
|
||
and made no committed model), and `tools/wai_int_synth_export.py` names the working
|
||
path.
|
||
- **Stale paths.** The int_synth decoder is `int_synth.rs` at the crate root, not
|
||
`neural/int_synth.rs`. The quantum-ops engine and the quantum-sim receipt live in
|
||
`wai-quantum`, whose features `wai-rs` forwards. README and SPEC Appendix A no longer
|
||
send macOS builders to a `.cargo/config.toml` that no longer exists: `wai-rs/build.rs`
|
||
finds libFLAC.
|
||
- **Protected content: two lanes** (SPEC §6.5). Lane A, hardware-protected content,
|
||
decodes inside the platform's protected media path; WAI's records about it (among
|
||
them the served manifest, the segment map, the key-release decisions, the ad
|
||
decisions and impressions of `wai.video.ssai`, the program-as-run windows of
|
||
`wai.video.channel`, and the content hashes of WAI objects) cover no decoded frame.
|
||
This revision registers no `security.encryption` value for it. Lane B, end-to-end
|
||
encryption at the application layer (`mls-sframe`), decrypts in the application, so
|
||
the tier and the output digests of the sink that holds the key hold as they do for
|
||
plaintext; staged-measurement counts no encrypted object, and a verifier without the
|
||
key checks none of a `reconstruct-session` claim's rules that need a payload. Lane B
|
||
is not a protected-path robustness level. `wai.video.keys` and the confidentiality
|
||
extension say which lane they belong to.
|
||
- **Where the exact tier holds** (determinism-tiers §5). `decode-equivalence` is shown
|
||
on native CPU and on wasm32, on no GPU or NPU; a WebAssembly decoder at the tier uses
|
||
no relaxed-SIMD instruction and depends on no NaN's bits; and the tier never covers
|
||
output a sink cannot read.
|
||
- **Reader limits** (SPEC §2). A reader need accept no manifest longer than 2²⁷ bytes
|
||
(128 MiB) or nested deeper than 127, and refuses a longer one from `man_len` alone
|
||
(`manifest_too_large`). The length limit is sized to the largest manifest a
|
||
conforming staged layer has: a `prev` (staged-delivery §3) naming a different
|
||
producer for each of the 2²⁰ units a `WIR1` layer may cover (staged-delivery
|
||
Appendix A.3) is about 94 MB. It bounds what a reader reads, not the memory a parse
|
||
takes, which in the reference readers is up to nearly 40 times the manifest's length
|
||
(`SECURITY.md`). `wai-rs`'s readers, its HTTP head parser (`head_malformed`) and the
|
||
Python verifiers apply the length limit, and the HTTP corpus holds it at its edge;
|
||
the writers, `wai-web`'s included, refuse to write past it, and refuse a payload whose
|
||
length does not fit its `u32` field.
|
||
*Envelopes this makes non-conforming:* none of the layout's; an envelope whose manifest
|
||
is longer than 128 MiB, or nested deeper than 127, is one a conforming sink may refuse,
|
||
so a conforming encoder no longer writes it (SPEC §7).
|
||
- **`SECURITY.md`.** How to report a vulnerability, and where the reference
|
||
implementation reads untrusted input: the manifest limits and the memory a manifest's
|
||
parse takes, the native codec libraries and their gaps (unbounded decompression output
|
||
for `wai.text.zstd` and `wai.text.xz`; no fuzzing in CI), module authorisation, and the
|
||
fingerprinting risk of `held_sets`. CI checks its section references.
|
||
|
||
### Vector graphics (Draft): the extension text of `wai.graphics.vector`, §1–§3
|
||
|
||
- **A new extension, [graphics-vector](extensions/graphics-vector.md) (Draft).**
|
||
`wai.graphics.vector` carries titles, credits and other authored graphics as integer
|
||
drawing instructions, which the sink rasterises into an overlay layer. This revision
|
||
writes §1–§3: overlay-equivalence and text-equivalence, which cover the overlay layer
|
||
and the computed text and nothing after them; the `WVG1` payload and the `WOS1`
|
||
outline-set format, with their canonical rules, caps and the order in which a sink
|
||
refuses, including a per-frame cost each listed size states and a sink verifies at
|
||
load; the digest sidecar; and the arithmetic, as the raster stage family RS1–RS9, in
|
||
which no value wraps. Outline sets, text and accessibility, and packages follow in
|
||
later revisions of the Draft. Nothing is registered: the string is registered once its
|
||
conformance corpus is complete.
|
||
|
||
### wai-web on older engines: the reach builds
|
||
|
||
- **Three builds from the shipped package, and a loader** ([wai-web/reach](wai-web/reach/README.md)).
|
||
`reach/build.sh` derives them from `wai_web_bg.wasm` and `wai_web.js` with no change to
|
||
the Rust:
|
||
- **L1** legalises the binary's JS interface (`wasm-opt --legalize-js-interface`), so
|
||
engines where i64 does not cross as `BigInt` run the full module;
|
||
- **L2** lowers it to MVP WebAssembly and legalises it;
|
||
- **J** translates the MVP module to ES5 JavaScript with `wasm2js`.
|
||
|
||
L2 and J come in profiles, kept by `wasm-metadce` from the exports each needs: `full`,
|
||
`verify_decode`, `verify` and `receipt`. An ES5 runtime supplies the missing builtins
|
||
and joins and splits i64 values at the boundary: a `BigInt` where the engine has one, a
|
||
`Number` (exact below 2^53) where it does not. The glue is the shipped glue compiled to
|
||
ES5, with counted changes. One of them rebuilds a memory view whenever the memory's
|
||
buffer changes, not only when it is detached.
|
||
- **`wai-load.js` chooses by feature probes alone.** Tiny modules passed to
|
||
`WebAssembly.validate` (MVP, sign-extension, non-trapping float-to-int, bulk memory), one
|
||
instantiated module that returns its i64 argument, and checks for typed arrays decide
|
||
between shipped, L1, L2 and J. The probes never throw. An early WebAssembly engine line
|
||
(WebAssembly and the stack-trace capture API, but no RegExp `dotAll` flag: the feature
|
||
`noDotAll`) gets J, or the L2 receipt build, because the full MVP module has crashed an
|
||
engine of that line on arm64. A rung can be forced, and the loader says why it chose.
|
||
- **Pinned and recorded.** binaryen `version_123` is checked against each platform
|
||
archive's SHA-256, and TypeScript, esbuild, acorn and binaryen's WebAssembly build are
|
||
pinned by `package-lock.json`; J's `wasm2js` is that WebAssembly build, so J comes out
|
||
the same on every host.
|
||
`reach/manifest.json` records the SHA-256 and sizes of every output built from the
|
||
package the site serves, and the commit that package was built from; `reach/record/`
|
||
keeps that package. Given it, the build refuses any output that differs.
|
||
- **Checked output for output.**
|
||
- `reach/test/conformance.js` (ES5) runs workloads over the repository's corpora on any
|
||
rung, and every output must equal the shipped build's. The workloads cover receipts,
|
||
the staged corpus, the integer payloads, int_synth, the menu, the output tree and the
|
||
i64 boundary.
|
||
- `reach/test/run-modern.mjs` forces every rung and profile on a current Node, and runs
|
||
the demo verify scripts against each full build.
|
||
- CI (`wai-web-reach.yml`) runs a leg per package. On the package of record, kept in
|
||
`reach/record/` and held to the manifest's SHA-256, every output must equal the
|
||
manifest's; on the tree's own wai-web, the reach builds must still build and pass.
|
||
Each leg runs the conformance on the Node images of the engine lines each rung serves
|
||
(J on `node:0.12`, the early-engine routing on `node:8.0.0`, L2 on `node:8` and
|
||
`node:10`, L1 on `node:12` and `node:14`) and the shipped build on a current Node.
|
||
|
||
The images stand in for device classes, which the README names by capability. No real
|
||
old device has been run.
|
||
- **The receipt vector's signer id is a label.** The receipt workloads verify an asset and
|
||
its provenance receipt in `reach/test/vectors/provenance/`, signed with `wai_prov`'s
|
||
published test seed under its label `did:example:wai-prov`. `wai_prov --carry
|
||
<receipt.json>` measures nothing: it carries each step's figure, and its label, from an
|
||
earlier receipt for the same lineage and signs them under this revision's id, and it
|
||
refuses a receipt that does not verify or whose steps are not this run's. The vector
|
||
carries the figures `wai_prov` measured when it first wrote the asset, and the
|
||
documented command, carrying the receipt onto itself, writes both files again byte for
|
||
byte.
|
||
|
||
### Generated pictures and scores, made natively and checked by SHA-256
|
||
|
||
- **The payload of `wai.generate.spectral`** (SPEC §5, *Payload of
|
||
`wai.generate.spectral`*). The registration is completed (§8): the payload is an
|
||
instruction object of form v0, one canonical JSON object naming the model by its
|
||
SHA-256 and giving the prompt, the seed, the steps and every size a sink makes. One
|
||
serialization per object, so an envelope's content hash names the instructions; a
|
||
sink makes only listed sizes, with that model only, and checks each output by its
|
||
SHA-256, never by a 64-bit FNV-1a alone. The intent of an envelope that names the
|
||
generator is `create`, and an envelope with another intent is non-conforming. The
|
||
model is the generator's parameter set (§3.1) and `model` its identity, so an
|
||
envelope that names the generator carries no `prior` pin, and a sink refuses one that
|
||
does (§3.1 says so beside the pin rules). `wai inspect` and `wai unwrap` report the
|
||
payload, the intent and any pin of such an envelope, ✓ or ✗ with the reason. Vector:
|
||
`wai-rs/tests/vectors/generate/instr-v0.json`, the object, the SHA-256 at each of
|
||
its four listed sizes, twelve objects at the inclusive bounds a sink accepts (each
|
||
with the SHA-256 of its first listed size), and 36 objects a sink refuses, each with
|
||
the rule the reference reader finds broken first, which its tests assert.
|
||
- **`wai generate`** (feature `generate`) makes a picture with the reference generator
|
||
at any size form v0 allows and prints its SHA-256; `--instr` makes a listed size of
|
||
an object, read as a sink reads it, and `--write-instr` writes one. `sizes.json` pins
|
||
the committed prompt at 256×128 and 640×320. The wasm32 workflow checks that the
|
||
browser binding makes every pinned picture and score with the SHA-256 the native
|
||
tool wrote (`wai-web/demo/verify_generate_wasm.mjs`), so a page can hold its own
|
||
output to these pins. `wai wrap` refuses a `wai.generate.spectral` payload that is not a
|
||
form-v0 object, and any intent but `create` over that generator, in every build.
|
||
- **`wai score build|render`** (feature `score`). `build` writes a `WSCR` from integer
|
||
note fields only (score §2.3); `render` prints `samples`, `pcm_sha256`,
|
||
`mixdown_blake3` and `score_hash`, and `--pcm` writes the samples. Every
|
||
`score-conformance` vector now states `pcm_sha256`, and `notes/<name>.json` holds it
|
||
in the authoring form, which `build` turns back into the committed `WSCR` byte for
|
||
byte.
|
||
- No library source changed: the generator, the score synth, the world floor and the
|
||
browser binding are as they were, so pins made with this tool bind the binding pages
|
||
already load.
|
||
|
||
### Quantum circuits: `wai.quantum.circuit2`, and `wai.quantum.circuit` stated exactly
|
||
|
||
- **A new capability, `wai.quantum.circuit2`** ([quantum-sim](extensions/quantum-sim.md),
|
||
SPEC §5). Magic `WQC2`, contract `ext` `wai.quantum.circuit2/1`, numeric
|
||
`wai.det.amp64`, gateset `cliffordT+dyadicP.rn`. The container layout, the gate IR and
|
||
the arithmetic are those of `wai.quantum.circuit`; the phase table is not. Each
|
||
`P(k)` holds the integers nearest to `2^30·cos(2π/2^k)` and `2^30·sin(2π/2^k)`, so its
|
||
controlled-phase ladder gives the QFT. The literal table is normative (§3.2, Appendix
|
||
B.2, digest `d34cb793…3510`); an exact-integer interval procedure certifies every entry,
|
||
informatively. The contract and the measurement are byte-canonical, and the op-log ends
|
||
exactly after its last op. Registered in the machine-readable registry and the
|
||
vocabularies, with `wai.det.amp64` as an identifier.
|
||
- **`wai.quantum.circuit` is frozen, and its table stated for the first time** (§3.1,
|
||
Appendix B.1, digest `3fa9805f…8466`). The text gives the exact recurrence — the sine
|
||
formula, the truncating halving, the floor square root — and the deviation of every
|
||
entry from the ideal phase: from `P(8)` on the imaginary part is too large, by up to
|
||
7032 units of `2^−30`, and `P(19..32)` apply no phase. The claim that this table gives the
|
||
QFT is withdrawn. Writers SHOULD NOT emit new circuit/1 content.
|
||
- **Both capabilities are specified to the bit** (§2–§4): little-endian integers, section
|
||
bounds computed without overflow, a repeated known section refused, the contract and
|
||
the measurement read as I-JSON (RFC 7493) by rules stated in full: no repeated member
|
||
name, no surrogate or noncharacter, no number of magnitude 2^1024 − 2^970 or more, at
|
||
most 127 levels deep, and an integer read exactly; the three refusal
|
||
classes (malformed, contract, invalid) in the order a reader checks them, the
|
||
complex-product composition, the index convention, the gate matrices, the hash layouts
|
||
and the shot sampler. `circuit_hash` covers the canonical contract, so it names the
|
||
capability and its table; `statevector_hash` does not. §5 states the error per
|
||
capability, and a new §8 why circuit2 is a capability of its own and what an older
|
||
reader does with it (it refuses `WQC2` at the magic).
|
||
- **Newly non-conforming circuit/1 payloads** (quantum-sim §8, SPEC §5): a contract whose
|
||
`ext`, `numeric`, `frac` or `gateset` is missing or carries another value; an
|
||
`n_qubits` that is not an unsigned integer of at most 255; a known section kind more
|
||
than once; section bounds that overflow; a contract or measurement that repeats a
|
||
member name within one object, holds a noncharacter, or holds a number of magnitude
|
||
2^1024 − 2^970 or more that an earlier build's float parser let through (some builds
|
||
also refused numbers just below it, which are now accepted). No file a reference writer
|
||
wrote is among them.
|
||
- **Corpus.** `quantum-conformance/` keeps its four original vectors and manifest entries
|
||
byte for byte and adds qft5_x22, dyadic_ladder and qft8_ones, which see the phase table:
|
||
under either table, `dyadic_ladder`'s statevector hash changes with every ±1 change of
|
||
any `P(1..32)` component. `v2/` holds the same seven circuits as circuit2; `reject/` at
|
||
least one payload for each refusal a payload can carry, with its class; and `accept/`
|
||
payloads at the edges of the same rules that a reader must accept, with how each reads.
|
||
`tools/referee.py`, integers only, derives both tables, requires Appendix B to state
|
||
them, reads every JSON section by the same rules, and rebuilds and checks every file. The
|
||
Appendix B tables are rendered from the reference constants by
|
||
`wai_quantum_vectors appendix`, and a test fails when the text differs.
|
||
|
||
### wai-quantum 0.4.0: circuits carry their gateset
|
||
|
||
- **`Gateset { V1, V2 }`** names the capability a circuit is reconstructed with, and
|
||
`Circuit` carries one in a new field. `Circuit::new` and `Circuit::qft` build V2
|
||
(`wai.quantum.circuit2`); a circuit read from a container takes the container's, and one
|
||
derived from another keeps it. `PHASE_V1` and `PHASE_V2` are literal constants: no
|
||
recurrence runs at load. `simulate`, `to_wqc`, `circuit_hash`, receipts, the fabric's
|
||
lanes and the toolchain follow the circuit's gateset. `from_wqc` reads both magics,
|
||
checks a circuit/1 contract's values and a circuit2 contract's bytes, reads the contract
|
||
and the measurement as I-JSON with a strict reader of its own that decides every rule
|
||
from the text, the number range on the literal's digits (so neither a build feature nor
|
||
a float parser's limits changes a verdict), refuses repeated sections and
|
||
wrapping bounds, and no longer sizes an allocation from a claimed op count.
|
||
`reject_corpus()` and `accept_corpus()` return the payloads a reader must refuse, each
|
||
with its class, and must accept, each with its reading; the corpus's `reject/` and
|
||
`accept/` trees are written from them.
|
||
- **Every circuit built in code changes its `circuit_hash`**, since the contract names the
|
||
capability, and its statevector wherever `H`, `T`, `T†`, `P(3)` or `P(k ≥ 5)` acts on a
|
||
nonzero amplitude (`P(1)`, `P(2)` and `P(4)` are the same in both tables). Every
|
||
circuit/1 artefact reproduces: the four original vectors, the legacy receipt pins, the
|
||
lanes32 certificate corpus and the other pins are unchanged, built in `Gateset::V1`, and
|
||
new V2 pins sit beside them.
|
||
- **wai-rs** registers `wai.quantum.circuit2`; `quantum_envelope` tags an envelope with the
|
||
circuit's capability and refuses one whose capability and magic disagree. The
|
||
`wai-quantum` CLI takes `--gateset v1|v2` (default v2) and names the capability in its
|
||
JSON; with `--gateset v1` and a phase of `k ≥ 8` it prints the circuit/1 deviation on
|
||
standard error. `wai_quantum_meter` seals the capability the file names. The browser
|
||
sink reads both magics; the IDE has `set_gateset`, and its `add()` sets the parameter of
|
||
every gate but `P` to 0. The serve component, `wai-quantum-serve` 0.5.0, takes
|
||
`gateset=v1|v2` on `/run`, `/qasm` and `/receipt`.
|
||
- **Migration from 0.3** (a breaking release):
|
||
- `Circuit::new` is V2. To reproduce or verify a 0.3 artefact, read its `WQC1` file, or
|
||
set `c.gateset = Gateset::V1` on a circuit built in code; a receipt's `gateset_of`
|
||
says which gateset a circuit's ops match it under.
|
||
- `quantum::phase`, `quantum::inv_sqrt2` and `quantum::CAPABILITY` are removed in favour
|
||
of `Gateset::phase`, `Gateset::inv_sqrt2` and `Gateset::capability`.
|
||
- `to_wqc`, `circuit_hash` and `quantum_toolchain::circuit_id` return a `Result`; they
|
||
fail only on a circuit its gateset does not allow, never on one that has simulated.
|
||
- `Circuit` and `QuantumError` are `#[non_exhaustive]`; `QuantumError` gains `Contract`
|
||
and `kind()`.
|
||
- `quantum_fabric::certify_lanes` and `lanes::random_circuit` take a gateset, with a
|
||
kernel and generator id per gateset; the circuit/1 ids are unchanged.
|
||
- `validate` refuses a gate with more than 255 controls, and in V2 repeated controls and
|
||
a parameter on any gate but `P`. `from_wqc` refuses the payloads quantum-sim §8 lists.
|
||
|
||
### Integer operators (Draft), and the payload text of two integer codecs
|
||
|
||
- **A new extension, [integer-operators](extensions/integer-operators.md) (Draft).**
|
||
`wai-ops/1` is the integer operator set the integer-exact capabilities decode
|
||
through: each operator's exact arithmetic over the integers, with rounding, bias
|
||
placement and shift scope as parameters of requantisation; a range policy on every
|
||
tensor (`derived`, `clamp` or `check`); the `acc64` and `acc32` accumulator profiles;
|
||
static bounds rules, saturating at `±2^255` so that a 512-bit integer holds every
|
||
value they form, and a canonical bounds certificate that decides which profile a
|
||
parameter set meets; the accelerator subset `wai-ops/1-npu`; the `WHG1` graph form;
|
||
the stage operators of the drivers, among them the mANS decode of
|
||
`wai.image.jpegai` (E8) with its scale path (§13.6.1); and canonical lowerings of
|
||
the existing network forms (`int_synth.json`, `WHM1`, `WAM1`, `WMB1`) to `WHG1`.
|
||
The reference crate, the tool and the conformance corpus it names follow this
|
||
Draft; until they land, each capability's reference is the decoder its payload
|
||
extension names.
|
||
- **[integer-payloads](extensions/integer-payloads.md) writes down two more formats.**
|
||
§13 is the `wai.neural.int_synth` payload and its set, `int_synth.json`; §14 is the
|
||
`WIM1` payload and the `WMB1` bundle of `wai.neural.int_mlicpp`, including that the
|
||
entropy network reads every element of `y` not yet decoded as 0, and that the three
|
||
networks and the tables share one `afrac` in `1..=30`. §9 gains the members and
|
||
checks of `entropy.json`, and states that the layers' arithmetic is
|
||
integer-operators §13.2 and §13.3. No decoder changes: §13 lists the
|
||
`wai.neural.int_synth` checks the reference decoder makes once it decodes through
|
||
its lowering.
|
||
|
||
### Staged measurement and the session claim in the browser
|
||
|
||
- **The measurement core, bound for JS** ([staged-measurement](extensions/staged-measurement.md)).
|
||
`wai-web` binds `wai::stage_measure` (`src/measure.rs`), the code `wai_stage_measure`
|
||
runs, compiled to wasm32: `evaluateTitle` gives the text `eval --json` prints for a
|
||
title (counted bytes, quality integers, first presentable bytes, the layering
|
||
overhead's points, deduplication, the output tree), `simulate` and `simulateModel` the
|
||
text `sim` prints, `countObject` one envelope's counted bytes or the code it is not
|
||
countable with, `qualityRgb8` one picture pair's integers, and `outputRoot` the output
|
||
tree's root. A title is read from a map of files; `titleMissing` names the next file it
|
||
needs, so a page fetches them one at a time. Sums of squares are decimal strings, as the
|
||
corpus writes them.
|
||
- **The session claim, sealed in the browser** ([jwp-receipts](extensions/jwp-receipts.md)
|
||
§2.4.12). The claim classes (`receipt_claims`) build for wasm32 with the measurement
|
||
core, and CI builds them so. `sessionBegin` records a session at its entry object
|
||
(`wai::session_recorder::SessionRecorder`): the decoder that decoded each envelope, each
|
||
presentation with its output's digest, each object received. `finish` seals the claim
|
||
with a key whose seed the page supplies, checked against the record first; a browser
|
||
session's `energy` is `null`. `verifyClaim` checks a claim against the presentations and
|
||
the objects as the claim-conformance runner does.
|
||
- **Proved byte for byte.** `demo/verify_measure.sh`, run in the wasm job, requires every
|
||
golden, refusal title, counted object, quality pair and simulator result of
|
||
`staged-measure-conformance/` from the wasm32 build, and the native `eval --json` of every
|
||
fixture to equal the wasm32 evaluation. A session recorded the way a page records one is
|
||
the native tool's claim byte for byte and commits to the golden output tree; the wasm32,
|
||
native and Python verifiers give the same verdict on it and on its altered variants.
|
||
- **A recorder that holds the envelopes checks the claim against them.** A sink hands
|
||
`SessionRecorder` each object's bytes (`received_envelope`): the recorder derives its
|
||
content hash, counted and transferred bytes, and whether an entry names it, and
|
||
`seal_checked` checks the claim against every envelope it holds, as a verifier holding
|
||
the `envelopes` context does, so an entry naming a capability its envelope does not give
|
||
never leaves the sink (`session_source_mismatch`). An object the sink does not hold is
|
||
`received_unheld`, by its content hash; the recorder no longer takes an object's figures
|
||
as told. The native tool's session over a title and the browser's `SessionHandle` both
|
||
hold their envelopes; `decoderFor` refuses a capability that is neither the envelope's
|
||
nor its fallback and requires the decoder's `first_unit`, `received` takes the bytes
|
||
alone — bytes that are not an envelope included, recorded as an inert object neither
|
||
counted nor presented, so the session accounts for every byte transferred —
|
||
`verifyClaim` takes the envelopes, and integers from JS are refused unless whole and in
|
||
range.
|
||
- **An object is listed once, as its envelope gives it** (§2.4.12, the objects). No
|
||
(content hash, path) is listed twice (`session_object_duplicate`): a sink that fetches
|
||
an object again lists it once, its transferred bytes every byte of every fetch, as the
|
||
recorder merges repeats. After the sums, an object is `presented` exactly when a decoder
|
||
entry names it (`session_presented_mismatch`). With the envelopes, an object listed with
|
||
a held envelope's content hash states that envelope's counted bytes, `null` exactly when
|
||
it is not countable (`session_counted_mismatch`).
|
||
- **Envelopes no entry names are received, not refused.** Inert bytes — of neither
|
||
magic, or whose `security` member is not a security object — are refused as unreadable
|
||
only where a decoder entry names them (`session_envelope_unreadable`); those, and an
|
||
encrypted envelope, no entry names are a received object, uncounted and not presented,
|
||
and one listed with counted bytes, even 0, is refused (`session_counted_mismatch`). A
|
||
page that receives such bytes lists them and seals its session.
|
||
- **The layers and units an envelope gives.** A decoder entry states `first_unit`, the
|
||
unit at which its object's first unit is presented. With the outputs and the
|
||
envelopes, each presentation through an entry whose envelope is held is at the layer
|
||
count that envelope gives — 1 for a base or a rendition with no `refines`,
|
||
`refines.layer + 1` for a layer (`session_layers_mismatch`) — and of a unit it covers:
|
||
from `first_unit` plus the span's start, the span's count of units, or the base's units
|
||
where a layer has no span or the object is a base (`session_unit_outside_object`). A
|
||
re-decode decodes unit `unit − first_unit` of the entry's object. A base's units are read
|
||
in its payload's own format — of its manifest capability's and its fallback's pinned
|
||
formats, the one whose magic the payload starts with — whichever capability the entry
|
||
names. `claim_verify.py` reads
|
||
the units from the integer payloads as `wai-rs` does, and `differential.py` adds random
|
||
sessions holding a base's and its layers' envelopes, as `WAI1` envelopes and as `WAI2`
|
||
components.
|
||
- **A layer is one a sink can apply, at its base's first unit** (§2.4.12). An entry naming
|
||
a held layer a sink refuses is refused (`session_layer_not_applicable`): not a
|
||
refinement or with a fallback, its `refines` malformed or absent, a `WAI2` refinement
|
||
component that does not conform (SPEC §7.1), its base a layer, not admitted by the
|
||
layer's capability or decoded only through its fallback, its span or runs outside a
|
||
held base's units, or its payload not `WIR1` with a unit for each it covers
|
||
(staged-delivery §4.1 steps 1–4 and 6). A `WAI2` component and its exploded envelope
|
||
are one base in either form (staged-delivery §9). A held layer's entry states the
|
||
`first_unit` of an entry it is applied over — naming its base through its own
|
||
capability — and an entry naming a `stage` claim's base or a layer it lists states the
|
||
unit `stage_sets` names that claim at (`session_first_unit_mismatch`).
|
||
- **An entry names what a sink dispatches** (§2.4.12 rule 4): never a `WAI2` companion,
|
||
a rendition or refinement whose payload does not match its `sha256`, a capability that
|
||
is not dispatchable, or the fallback of a `WAI1` whose capability is companion-only
|
||
(`session_source_mismatch`).
|
||
- **One verdict for either name, by capability, and for a key holder** (§2.4.12). An entry
|
||
naming a `WAI2` rendition by its exploded envelope's content hash, with only the `WAI2`
|
||
held, is read as naming the rendition by rules 4–8; the exploded envelope's own bytes,
|
||
held, are a separate delivery. A manifest or rendition is a layer only when its
|
||
capability is refinement-only: a base carrying a stray `refines` is a base, as
|
||
dispatch and selection already treat it (SPEC §4, staged-delivery §3), and `wai stage
|
||
apply` now routes by capability. An encrypted envelope an entry names is accepted —
|
||
a sink holding its key decrypts and dispatches it (SPEC §6.4 step 3) — and what the
|
||
entry states of it is the signer's statement.
|
||
- **A WAI2 offers its refinements only when every component conforms** (SPEC §7.1,
|
||
*Selection*). The text now states the reference sink's rule: an envelope one of whose
|
||
components breaks a rule of §7.1 or §5 (`check_component_roles`, now on `ManifestV2`)
|
||
offers none of its refinements, and may still present the rendition it picked. `wai
|
||
stage explode` follows it, refusing to explode a component of such an envelope that is
|
||
of a refinement-only capability or marked a refinement; rule 5 matches it
|
||
for the whole envelope; and http-delivery §7 step 4 states it for a client planning
|
||
from a verified head: each refinement of a nonconforming envelope is dropped
|
||
(`components_nonconforming`), never requested, one stating no `sha256` dropped for that
|
||
too, on the range path and the whole-object path alike. A refinement is one whose
|
||
`refines.base`, read on its own, is the pick's id (`container::refinements_naming`), so
|
||
one whose `refines` is otherwise malformed is reported too; of a conforming envelope
|
||
that is exactly the refinements whose `refines` is well formed. The HTTP corpus adds
|
||
ONC's conforming twin, OCF, whose refinement is fetched and delivered on both paths,
|
||
and ONM and ONX, a `refines` of the pick's id alone (reported) and of no entry's (not
|
||
reported); `OR`'s refinement, which names the pick, is now reported. `falsify/` adds
|
||
`conforming-withheld` and `refines-read-whole`, each with its `http_verify.py
|
||
--mutate`.
|
||
- **An encrypted envelope's clear parts are checked** (§2.4.12). SPEC §6.4 encrypts
|
||
payloads only: rules 4–9 now read an encrypted envelope's manifest and rendition table
|
||
as any held envelope's — the rendition an entry names, its capability, the component
|
||
rules, a base's layer count, a companion-only capability, a fallback, and the classical
|
||
length its table declares — and leave to the signer only what needs a payload: whether
|
||
a payload matches its `sha256`, a base's units and a layer's `WIR1` payload. A `sha256`
|
||
that is not 64 lowercase hex digits matches no payload, so an entry naming its rendition
|
||
is refused, encrypted or not (rule 4, `session_source_mismatch`).
|
||
- **The recorder states the classical length.** `SessionRecorder` derives
|
||
`classical_declared` at the tally from the `WAI2` envelopes it holds, readable or
|
||
encrypted, exactly as rule 9 counts them (`claim_receipt::session::classical_length`,
|
||
which rule 9 calls too); `SessionRecorder::classical`, which a sink called by hand and
|
||
the browser binding never could, is removed. A page that receives a `WAI2` it does not
|
||
decode now seals its session. `verify_measure` seals two such sessions in wasm32 — over
|
||
`counting/wai2-classical.wai`, whose second rendition is PNG, and its encrypted twin,
|
||
both new in the counting set — checks the length against the rendition table, and the
|
||
native and Python verifiers give the same verdicts. A limitation of this revision: the
|
||
length comes from the `WAI2` envelopes held in full, so a sink that received a `WAI2`
|
||
only as byte ranges, holding its head but not the envelope, cannot yet state that
|
||
envelope's classical rendition (§2.4.12, `classical_declared` and rule 9). Recording
|
||
such a session is HTTP session work still to come.
|
||
- **A record or a derivation is no component in any role** (SPEC §7.1, *Component
|
||
identity*; `record-or-derivation-as-component`).
|
||
- **A record or a derivation is no companion** (SPEC §7.1, rule 1). A companion's
|
||
capability is companion-only or unregistered; a capability registered in another
|
||
kind — a payload, a record or a derivation — is refused
|
||
(`standalone-as-companion`), which the reference now reads from the registry rather
|
||
than from its list of media capabilities, which left records and derivations out.
|
||
- **Every refusal accounted for.** The claim-conformance README's table lists each
|
||
refusal a sink makes that decides whether a presentation can happen — staged-delivery
|
||
§3, §4.1, §4.3–§4.5, §9; SPEC §4, §5, §7.1 — with the §2.4.12 rule and vector that
|
||
refuse it, or the sentence of §2.4.12 that leaves it to the signer's statement.
|
||
- **The browser claim is a release build.** `verify_measure_wasm.mjs` requires the wasm32
|
||
claim's `host.build.profile` to be `release` and its target `wasm32-unknown-unknown`.
|
||
- **The build profile is the optimisation level.** `release` is a build at `opt-level`
|
||
2, 3, `s` or `z`, whatever its profile is named, and `debug` one at 0 or 1
|
||
([jwp-receipts](extensions/jwp-receipts.md) §2.4.8's `build`): a WebAssembly build
|
||
optimised for size is `release`.
|
||
|
||
### One Ed25519 rule for every signature
|
||
|
||
- **SPEC §7 *Signatures*.** Every Ed25519 signature WAI defines is verified strictly: the
|
||
key is its point's one canonical encoding and not of small order, `S` < L, `R` is not of
|
||
small order, and the cofactorless equation holds with `R` compared byte for byte. The
|
||
receipt extensions cite the rule instead of restating it; jwp-receipts §3.1 step 3
|
||
keeps its two reasons, `bad_signer_key` for steps 1 and 2 and
|
||
`signature_verify_failed` for the rest. OER/2's chain signature keeps the rule
|
||
oer-receipts §5 settles.
|
||
- **One verifier in wai-rs.** `wai::sig::verify_strict` is the only Ed25519 check written
|
||
in the crate but OER/2's. The video, SSAI, key-release, channel and package receipts
|
||
(both versions), the provenance, ternary, spatial-audio, volumetric, film, score and
|
||
haptics receipts, and a world's session receipt, ops and seal moved onto it; before,
|
||
each read its key with the library's lenient decoder and checked the lenient
|
||
equation, which accepts a key of small order (under the identity, `(R, S) = (B, 1)`
|
||
verifies over every message), a second spelling of a key, and an `R` of small order.
|
||
Claims, JWP groups and the key log, which held the rule in a copy of their own, call
|
||
it too. It builds under the `ed25519` feature, the dependency alone.
|
||
- **One verifier in Python.** The claim, JWP, signer, HTTP and packaging verifiers share
|
||
`claim-conformance/_ed25519.py`, the rule written from RFC 8032 with the standard
|
||
library. `pack_verify.py` had checked signatures leniently.
|
||
- **Corpora.** Each receipt family's corpus carries the three signatures a verifier
|
||
checking only the equation accepts — the identity as the key with `(R, S) = (B, 1)`,
|
||
the identity spelled `y` = p + 1, and an `R` of small order under the base point —
|
||
and every conforming verifier refuses them: new cases in jwp-conformance,
|
||
signer-conformance and pack-conformance (`mutations/` and `receipts/`); claim- and
|
||
http-conformance had them. No existing vector's expectation changed.
|
||
|
||
### The int_codec model and vectors: trained and coded on a generated signal
|
||
|
||
- The int_codec model and vectors are now trained and coded on a generated signal;
|
||
earlier revisions used a third-party recording.
|
||
- `tools/wai_int_audio_export.py` reads no audio file. It writes its training signal
|
||
and the clip it codes from a seed (16 kHz mono: tones, chirps, noise bursts and
|
||
speech-like formant sweeps), in integer arithmetic, so a seed gives the same samples
|
||
on every machine; `--signal-only` writes one with the Python standard library alone.
|
||
Training is seeded from the same seed with deterministic algorithms and a fixed
|
||
thread count: two runs gave byte-identical files. The seed and the command are in
|
||
[`wai-rs/tests/vectors/int_audio/README.md`](wai-rs/tests/vectors/int_audio/README.md).
|
||
- Regenerated through their generators: `wai-rs/tests/vectors/int_audio/` (the model,
|
||
both bitstream forms, `audio.json`, `sha256.json`); `int-payload-conformance`'s
|
||
`vectors.json` and its thirteen `wia1_*` refusal fixtures; the staged-delivery case
|
||
`audio_int_codec_1l` and `index.json`; the staged-measurement audio title,
|
||
`goldens.json` and `sim/results.json`. The `WIA1` format, the capability and the
|
||
model's shape are unchanged. `wai_int_payload_vectors` now reads the place of a
|
||
`WIA1`'s first table from the payload's header for the two fixtures that change it, so
|
||
they keep their meaning on tables of another length (on the earlier vector the
|
||
offsets are the same).
|
||
- `tools/wai_audio_provenance_check.py`, run by CI: every committed audio vector is read
|
||
to PCM and correlated, at every lag, with the reference recordings the CI host has;
|
||
above 0.5 it fails, and a host with none skips it and says so.
|
||
- Every corpus states its content's provenance: a `**Provenance.**` line in each
|
||
`*-conformance/README.md` (OER/2's settled corpus aside) and a row per directory in
|
||
`wai-rs/tests/vectors/README.md`, required by `tests/corpus_provenance.rs`.
|
||
CONTRIBUTING states the rule. Two vectors are flagged there as not generated and to
|
||
be replaced: `int_video`'s `video_real` and `neural-conformance`'s `photo`.
|
||
|
||
### Staged tracks over MoQ: a track per layer, a receipts track per track, delivery timeouts per hop, chains that recover
|
||
|
||
- **Re-read against the drafts.** [moq-streaming-format](extensions/moq-streaming-format.md)
|
||
names the revisions it was last read against, `draft-ietf-moq-transport-22`,
|
||
`draft-ietf-moq-msf-01` and `draft-ietf-moq-cmsf-01`, and cites their sections. The
|
||
re-read corrected: MSF's packaging values (`catalog` is a track name, not a value); what
|
||
orders delivery (MOQT, subscriber priority first, then publisher priority, MOQT
|
||
§5.1.2); how properties are carried (MOQT's track and object properties, of which MSF
|
||
defines one); `SUBGROUP_DELIVERY_TIMEOUT`, which runs from the subgroup's FIN to its
|
||
acknowledgement, not from an object's arrival; that MSF maps every object to a stream
|
||
of its own, so a staged object never travels as a datagram; and that MOQT-22 replaced
|
||
the Joining FETCH with fill fetch streams.
|
||
- **§4.1 Staged tracks.** A stage set is carried one track per layer: base tracks and a
|
||
track for each layer index, each object one whole envelope. Each entry carries
|
||
`wai_staged` (`set`, `layer`, `lead_ms`). A set's extent is its tracks, layers 0 to the
|
||
highest with a track at each, so an MSF delta update that adds, removes or clones a
|
||
track changes it alone; `lead_ms` is read only while the set is live. Tracks are named
|
||
by namespace and name (a namespace inherited as MSF says), unique in a catalog, one
|
||
namespace to a set; the check needs the catalog track's namespace, and a checker that
|
||
does not know it refuses a set that mixes declared and inherited namespaces; `renderGroup` is an integer that fits 64 unsigned bits and
|
||
`depends` an array of strings, each naming a track of the catalog in the entry's own
|
||
namespace; a layer track depends on a track of every lower layer and on none of its
|
||
own or above. A sink checks a set's entries in a fixed order and reports the first
|
||
failure (`staged-malformed`, `staged-set-incomplete`, `staged-depends`,
|
||
`staged-grouping`, `staged-lead`, `staged-receipts`), treating a failing set's layer
|
||
tracks as absent. On a live track each group carries one object in a subgroup of its
|
||
own. The base track's publisher priority is first among the set's (lower numbers first),
|
||
though a subscriber's own priority comes before it. A layer object's
|
||
`OBJECT_DELIVERY_TIMEOUT` is at most its slack to the last unit it covers, settable
|
||
per object; a base's never shorter than the time to its last unit.
|
||
Timeouts run per hop and never decide lateness; a sink's longer target latency cannot
|
||
extend a publisher's timeout. A subscriber caps its stage, and its receipts, by
|
||
subscription.
|
||
- **Receipts.** `wai_receipt_alias` (§4) is the `track_alias` a track's receipts sign:
|
||
MOQT's Track Alias is chosen per session by each publisher on the path, so it is not
|
||
that number past the first hop. Every alias in a catalog is an integer, compared as a
|
||
number. `wai_receipts_track` names each media track's own **receipts track**
|
||
(`wai-receipts`, at the media track's priority, no delivery timeout), whose group *g*
|
||
carries the receipts of the media track's group *g*. No two tracks share an alias or a
|
||
receipts track: a rendition of a layer, a clone among them, overrides both and has a
|
||
chain of its own. Each receipt says which MOQT object it is about — `group_id` the
|
||
MOQT Group ID, `object_id` the Object ID, the publisher's key and the track's alias —
|
||
and a subscriber refuses one that does not (`moqt_mismatch`, a new jwp-receipts §3.3
|
||
reason) before verifying it, holding nothing of it. A subscriber sets no delivery
|
||
timeout on its subscription to a receipts track. A MOQT relay that carries receipts as
|
||
opaque payload is not a jwp-receipts §4 relay; the subscriber verifies every receipt
|
||
(jwp-receipts §4.1 amended).
|
||
- **Recovery.** A missing receipt is recovered by FETCH of its receipts-track group. A
|
||
subscriber states its bound in counts: receipts held live for each chain (a time only
|
||
beside it, never alone), held as history for each chain, and held across its chains.
|
||
Past the live count the held receipts are refused (`parent_not_recovered`, a new
|
||
jwp-receipts §3.3 reason) with their layers, the chain re-anchors at its next validly
|
||
signed group, and the layers of the groups the anchor skips are refused. A subscriber
|
||
that joins mid-stream declares a join and anchors at its first group. Each anchor
|
||
leaves a gap, which a group linking to either end fills as history, in any order — the
|
||
ascending order of a FETCH or a fill fetch stream included — and never as a fork;
|
||
history waits under its own count. A group admitted before, offered again with the
|
||
same bytes (MOQT can deliver one object twice, by a subscription and a fill), changes
|
||
nothing (`accept again`), never `chain_break`, and its objects verify against it as
|
||
against the head. jwp-receipts §3 step 3 defines holding,
|
||
re-anchoring, joining and history, and says what an anchor does not prove; §6.1 says
|
||
how anchors are reported and why the bounds hold across chains.
|
||
- **Other text.** staged-delivery §8, §9 and §13; jwp-receipts §4.4 and §5.9;
|
||
http-delivery §10.2 and Appendix C. The registry lists `wai_staged`,
|
||
`wai_receipt_alias`, `wai_receipts_track` and `wai-receipts` as wire identifiers.
|
||
- **Reference implementation.** `staged::moq` (feature `staged`): `StagedTrack`,
|
||
`SetTrack::to_catalog_json`, `receipts_track_json`, `check_catalog` and
|
||
`check_catalog_in`, `depends_for`, `live_object_rules_ok`, `priorities_ok`,
|
||
`effective_timeout_ms`, `expired_at_hop`, `layer_timeout_ms`, `base_timeout_floor_ms`.
|
||
`jwp_delivery::ReceiptChains` gains `with_hold`, `with_join`, `admit`, `expire`,
|
||
`take_events` and `anchors`, and for MoQ `for_moq` (every count at most
|
||
`MAX_HELD_GROUPS`) and `admit_on`; `HoldBound` states live, history and total counts
|
||
(`from_json` refuses a statement without them); `ObjectReceipt::check_moqt`. Each
|
||
group's signature is verified once, and a held group is offered again only when a
|
||
group it links to is admitted (`verifications`, `reoffers`, `held_total` count them).
|
||
Without recovery it behaves as before. `tests/staged_moq.rs` runs the corpus trace
|
||
with signed receipts through `for_moq` under each subscriber's stated bound,
|
||
per-track chains, dropped objects checked by inclusion and named by the relay's
|
||
`delivery-gap` claims, and sealed `stage` claims.
|
||
- **Corpora.** `staged-conformance/moq/`: the set's MSF catalog; catalogs around §4.1's
|
||
check, among them type cases, namespace inheritance under the catalog track's
|
||
namespace, another and none known, MSF delta updates (a clone of a layer refused
|
||
unless it overrides its alias and receipts track) and conversion to on demand; and a
|
||
trace of the set through one relay to four subscribers, two layer objects dropped on
|
||
timeout and one forwarded within its timeout and late, one subscriber's receipt lost,
|
||
recovered by re-anchoring and the gap then fetched and filled as history, and one
|
||
subscriber joining mid-stream. `rate_port.json`:
|
||
the `rate` predictor beside the f64 interpolation of the layered-PCM ladder idea,
|
||
equal wherever that form is exact, differing (pinned) where it rounds next to a half.
|
||
`jwp-conformance/` gains recovery vectors — among them a gap fetched in ascending
|
||
order after a re-anchor, a joiner's ascending fill under a live bound of 4, the
|
||
history bound passed, the bound across chains, and a group delivered twice — appended
|
||
after every existing one. `staged_verify.py` and `jwp_verify.py` check all of it independently;
|
||
`chain_gap_differential.py` lets both chain verifiers recover.
|
||
- **joule-moq** (`joule-code/`): `staged` (the `wai_staged` member, the layer read from the
|
||
envelope, the prototype `mrl_*` header compared and never obeyed), `ReceiptChains` per
|
||
key and track, verifying each group's signature itself and knowing a group admitted
|
||
before by its bytes, with the same recovery and bounds, and a `MockRelay` that forwards staged tracks on
|
||
a caller-driven clock: per-hop timeouts, capping by subscription, one object to a live
|
||
group, a receipts track per media track, `RelayStats` keyed by track, group and object,
|
||
and send times past the clock refused. Its test `staged_tracks_drop_on_timeout_keep_receipts`
|
||
runs the same trace.
|
||
- No existing envelope, receipt or claim byte changes. Two existing jwp-conformance
|
||
vectors change their expected outcome: an exact replay of a group the chain admitted
|
||
(`chain_head_predecessor_replayed`, and g1 in `chain_legacy_replay_loop`) is now
|
||
`accept again`, not `chain_break`.
|
||
|
||
### The session claim: what a sink reconstructed
|
||
|
||
- **The `reconstruct-session` class** ([jwp-receipts](extensions/jwp-receipts.md)
|
||
§2.4.12, the next number after `stage` and `delivery-gap`): a sink's statement of one
|
||
session, about its entry object — the decoders it used, on which host and against
|
||
which parameter sets; the path each unit took and why it switched; the presentations
|
||
it made after their deadline; a commitment to every output it presented, by the root
|
||
and leaf count of [staged-measurement](extensions/staged-measurement.md)'s output tree;
|
||
the bytes it counted and received, and the objects it could not count; the classical
|
||
rendition it did not need, by its `WAI2` table length; the `stage` claims it signed
|
||
about the stage sets it observed; and, where metered, the decode figure of one
|
||
completion with its acquisition class (`null` when unmetered, never 0). Its rules at
|
||
§3.1 step 4, and step 6 against the presentations (the tree, first-use order of the
|
||
decoder entries, paths, units, missed presentations, and re-decodes through decoders
|
||
whose replicate class makes them byte-exact), the objects received (its entry object
|
||
and every decoder entry's object among them), the envelopes received and the `stage`
|
||
claims it names. Each rule
|
||
and check has its reason in §3.1's table. Its capped lists are exact: the switches and
|
||
the missed presentations are each listed in full up to the list's cap, and counted in
|
||
full.
|
||
- **It restates no `stage` claim.** A `stage` claim states one set's layer objects, its
|
||
units at each stage on time and its delivered stage; the session names it by link hash
|
||
with the title's unit that is the set's unit 0, and commits to every output across the
|
||
title, late ones included. A sink signs one for each epoch of a set — before it forgets
|
||
the set's base, and at the end — and names them all. Step 6 binds the two both ways:
|
||
each set's units were presented; the session presented, at each stage, no fewer units
|
||
than a `stage` claim counts; no unit at a stage beyond the layers that the claims whose
|
||
sets hold it — every epoch's, and every base's over the unit — list as `applied` or
|
||
`partial` (a `late` or `refused` layer applied to no unit); and each claim's base and
|
||
arrived objects are among the objects the session received. Nothing binds a `stage`
|
||
claim to a session beyond its base and key: one an earlier session of the title signed
|
||
can be named.
|
||
- **A decoder entry names the object it decoded**, and the envelope binds it. Its
|
||
`object` is the content hash of the `WAI2` envelope whose rendition it decoded, or of
|
||
the `WAI1` envelope; it is among the objects received (`session_source_not_received`).
|
||
The envelopes a verifier holds — every `WAI2` envelope received, and any `WAI1` one —
|
||
are among the objects received, read as plaintext envelopes
|
||
(`session_envelope_unreadable`), and an entry whose object is one of them names what it
|
||
gives: a rendition and that rendition's capability, or the `WAI1` manifest's capability
|
||
or fallback (`session_source_mismatch`). A signer cannot relabel an entry whose envelope
|
||
the verifier holds out of the re-decode check, by any route; which object an output came
|
||
from remains its statement. An entry decodes one object, so a title's output-tree
|
||
entries are (capability, pin, object) as well (staged-measurement Appendix C), and a
|
||
session over a title still commits to the title's tree.
|
||
- **The classical rendition.** An envelope's classical rendition is the first selectable
|
||
one whose capability the codec glossary links from an entry of era `classical` and
|
||
whose media is the envelope's; WAI's own formats, neural codecs, records, parameter sets
|
||
and text never qualify.
|
||
- **Authorities** (§7.5): a session needs `reconstruct-session`, not pinned-only. Its
|
||
energy figure is a second item for confirmation, needing `reconstruct-session` and the
|
||
energy authority of its class; a verifier reports the two side by side, so an origin
|
||
can stand behind a session and not behind its figure. A session's subject's origin is
|
||
not expected for it: a relying party names the origin it expects, if any.
|
||
- **Figure profiles** ([energy-measurement](extensions/energy-measurement.md) §6.1): the
|
||
`moq-jwp:claim-link` row widens to a session whose `energy` is not `null`, so a
|
||
`measurement` claim publishes the bracket behind a session's figure.
|
||
- **Delivery telemetry**'s `claim` member names a registered class now
|
||
([delivery-telemetry](extensions/delivery-telemetry.md) §2.1).
|
||
- **staged-measurement §8.6–§8.7**: a tool probes a power reader's rail at rest before it
|
||
brackets a decode — absent, stale or live by energy-measurement §3.1's rule — and reads
|
||
an energy counter as power by its successive differences across its wrap, ending the
|
||
samples at an interval that cannot be a reading — one implying more power than a
|
||
processor rail draws, one longer than ten sampling intervals, or a difference of more
|
||
than half the counter's range (a counter reset); a tool that signs a session claim
|
||
seals a figure only where its verdict is clean, beside the `measurement` claim on it,
|
||
its burst run on a power of two of workers, which the session's host states as its
|
||
`threads`, so the claim names no more of the machine than the session
|
||
(jwp-receipts §6.7).
|
||
- **Reference implementation.** `claim_receipt::session` (the class),
|
||
`session_recorder` (a sink's tally, each decoder named with the object it decodes; it
|
||
verifies each `stage` claim as it records it — one for each epoch of a set — requires
|
||
it signed by the key that finishes the session, and checks the sealed session against
|
||
what it recorded and against those claims before it is returned),
|
||
`key_log::Item::figure_of_claim`, `energy_report::rail_liveness`,
|
||
`energy_burst::counter_power_samples` and `rapl_power_reader`,
|
||
`energy_burst::{measure_on, power_of_two_workers}`, `stage_measure::run::completion`,
|
||
`stage_measure::decode::{steps, prepare}` and `stage_measure::session`, and
|
||
`wai_stage_measure bench`, which times each case of a title — one base's pinned decode
|
||
or one layer's offer, its checks and hashing after the clock — over rotating rounds,
|
||
compares every run's outputs with the evaluation's and aborts on a difference, records
|
||
each run and the operator's statement of whether the machine was idle, meters the
|
||
decode where a live rail allows, and with `--sign` seals the title's session and the
|
||
report on its figure. A lib test replays every `staged-conformance/` case as a sink and
|
||
requires the session it records, naming each epoch's `stage` claim, to seal. The
|
||
output tree builds under `receipt_claims`; the switch reasons live in the ungated
|
||
`delivery_terms`, which `telemetry` re-exports, so `receipt_claims` does not take
|
||
`telemetry`.
|
||
- **Corpora.** `claim-conformance/` gains the session vectors (§5.11) — every reason the
|
||
class adds, output trees of 1 to 33 leaves, every context, the classical rule across
|
||
the registry's kinds of capability, the caps at their edges, a base forgotten and
|
||
decoded again (`staged-conformance/`'s `record_budget`, its envelopes and each epoch's
|
||
`stage` claim) and every route by which an entry could be relabelled — and
|
||
`signer-conformance/` the session's authority and its figure's; `claim_verify.py`,
|
||
`signer_verify.py` and `differential.py` read the class. `claim_verify.py` reads
|
||
envelopes with `staged-conformance`'s strict readers, and `differential.py` compares the
|
||
two implementations across both stage bounds and many epochs, the caps, malformed and
|
||
encrypted `WAI1` and `WAI2` envelopes, entries naming their objects or not, and every
|
||
registered capability as a classical candidate. Every vector already in both corpora is
|
||
unchanged, and the claim corpus pins the vectors before the class by a digest computed
|
||
before it was added. `staged-measure-conformance/goldens.json` restates each title's
|
||
decoder entries with the envelope each decoded, and so its output root.
|
||
- **Wire.** No existing receipt, claim, pin or corpus vector changes bytes; a verifier of
|
||
an earlier revision refuses the new class as `unknown_class`. A title's output root
|
||
under staged-measurement Appendix C changes, its entries now naming their envelopes.
|
||
|
||
### Stage receipts: chains per key and track, objects verified by inclusion, `stage` and `delivery-gap` claims
|
||
|
||
- **One receipt chain per publisher key and track** ([jwp-receipts](extensions/jwp-receipts.md)
|
||
§2.4.1, §3 step 3, §6.1). `group_id` and `parent_receipt_hash` run per
|
||
(`signer_pubkey`, `track_alias`). The text disagreed with itself: the signing
|
||
payload's comment and §6.1's last bullet said per track, §2.4.1 and §6.1's first
|
||
bullets per key; all now say per key and track. Receipts issued with `track_alias`
|
||
`0`, or chained per track, verify as before. Receipts a publisher chained per key
|
||
across non-zero tracks, as the earlier §2.4.1 allowed, now fail at §3 step 3: each
|
||
group whose parent is on another track names nothing its own chain holds
|
||
(`parent_not_held`). A stage set carries each layer on a
|
||
track of its own, and a subscriber verifies the tracks it subscribes to without the
|
||
receipts of the ones it does not. A group that passes §3 steps 1–3 can head its chain
|
||
whether or not its objects arrived, so a dropped object does not break its track's
|
||
next link. §6.1's checks are now real in the reference verifier
|
||
(`jwp_delivery::ReceiptChains`): the monotonic `group_id` guard per key and track
|
||
(`group_id_not_increasing`) and the freshness bound on `group_end`
|
||
(`group_not_fresh`), off by default for labelled groups.
|
||
- **A legacy link authenticates nothing** (§3 step 3, §6.1). The legacy payload signs
|
||
neither `parent_receipt_hash` nor, through the link hash, anything that covers it, so
|
||
a relay that rewrote one link — the first group's, to name the head — made the
|
||
chain's whole history link again as new. The legacy payload does sign `group_id`, so a
|
||
verifier now refuses a legacy group whose `group_id` does not exceed its chain head's,
|
||
whatever its opt-in setting. A labelled replay fails its signature.
|
||
- **Forks are told from gaps** (§3 step 3). With a chain's head held, a `null` parent or a
|
||
parent that is another group the verifier holds is `chain_break`; a parent naming
|
||
nothing it holds is `parent_not_held`. A chain's live edge is the highest `group_id`
|
||
of a group that verified and arrived inside the freshness window, linked or not; a
|
||
group whose signed `group_id` is below it is history, exempt from the bound, so a
|
||
group a relay delayed past the window no longer wedges the chain behind it, and a gap
|
||
of any length fills in any order. Both payloads sign `group_id`, so no rewritten link
|
||
earns the exemption. A previous group of another key or track
|
||
is not held: a `null` parent beside one starts the chain, any other parent is
|
||
`parent_not_held`. `group_chain_break` in `jwp-conformance/` now holds a second chain
|
||
head; its earlier input, a parent that is not the previous group held, is
|
||
`group_parent_not_the_previous` and is `parent_not_held`.
|
||
- **`inclusion-verified`** (§3, *Verified by inclusion*; §4.1, §4.4): an object receipt
|
||
checked without the object's bytes — steps 1, 2, 3 and 5 — establishes that the
|
||
publisher signed an object with that content hash at that position, and is never
|
||
acceptance or delivery. A relay forwards the receipts of every object it does not
|
||
forward, whatever the reason, and **MAY** say why in a `delivery-gap` claim (§4.2).
|
||
`ObjectReceipt::verify_against(group, envelope: Option<&[u8]>)` returns
|
||
`Evidence::{DeliveredVerified, InclusionVerified}`, which converts into staged
|
||
delivery's `ReceiptEvidence`.
|
||
- **The `stage` class** (§2.4.10): what a party observed of a stage set, about its base —
|
||
the stage requested, committed, delivered and presented, as points of staged
|
||
delivery's vocabulary `urn:wai:staged-delivery:1` (registered in
|
||
[energy-binding](extensions/energy-binding.md) §7.2; §2.4.9's vocabulary row names
|
||
it); units by stage; the tier and stage hash of what was presented; every layer
|
||
object with its units, bytes, status, evidence and staged-delivery §4.2 reason;
|
||
conflicts, deadline misses and envelope bytes. Six rules at §3.1 step 4, in order:
|
||
the points; the stage hash; the units; the objects (in order and within the set,
|
||
except an object refused before its span was checked; evidence `delivered-verified`
|
||
for an applied layer wherever a delivered stage is stated; bytes for every object
|
||
that arrived and none for a dropped one, fewer in all than the set's; no layer applied
|
||
twice to a unit; a partial object over at least two units); the units against the
|
||
objects, per unit (an applied object of layer *s* lies within the spans of layer
|
||
*s* − 1, a partial one meets them, and the units at stage *s* or above are no more
|
||
than layer *s* can have reached, a partial object counting at most its span less
|
||
one); and the delivered stage, which must be the
|
||
one the objects give. Delivered and presented are separate measures: a set whose
|
||
layers were all refused meets its commitment with stage 0 presented. A delivered stage
|
||
may be stated with no commitment beside it, unlike §2.4.9's rule 3: the observer
|
||
states what arrived. Whether a commitment was met is derived, never stated.
|
||
- **The `delivery-gap` class** (§2.4.11): a relay's statement that it did not forward
|
||
the object at (`track_alias`, `group_id`, `object_id`) — `delivery-timeout`,
|
||
`discretion` or `not-received` — checked at step 6 against the object receipt at
|
||
those coordinates (`gap_subject_mismatch`).
|
||
- **Authorities** (§7.5): a `stage` claim needs `stage`, a `delivery-gap` claim
|
||
`delivery-gap`; neither is pinned-only, and `delivery` grants neither.
|
||
- **staged-delivery §10** is split: §10.1 attestation of layers, which now names
|
||
`delivered-verified` and `inclusion-verified` and the per-track chains, and §10.2
|
||
claims. `staged::claim::StageObserver` states a sink's `stage` claim from what it
|
||
observed: the layers that arrived, with the layer and span their own `refines`
|
||
states, those known only by their receipts, and every report the sink gave, the
|
||
latest deciding. Every event carries the sink's epoch for the base
|
||
(`StageSink::base_epoch`, which counts the times the sink has forgotten it): a later
|
||
epoch starts the observation over, keeping only the envelopes the sink still holds for
|
||
the base, and a claim never spans two epochs. Every case of `staged-conformance/` is replayed through a
|
||
sink and its observers, and each claim seals and states what the sink did. [http-delivery](extensions/http-delivery.md) §10.5 says what an
|
||
abandoned refinement is: `inclusion-verified`, and `dropped` or `late` in a `stage`
|
||
claim; [staged-measurement](extensions/staged-measurement.md) §10.2 says the same of
|
||
an object its simulator drops.
|
||
- **Corpora.** `jwp-conformance/` gains the `chain` procedure — vector
|
||
`v6_per_track_chain` (§5.9) and its neighbours: interleaved tracks, a track joined
|
||
mid-chain, a link across tracks, forks, the guard and the freshness bound — and the
|
||
`inclusion` procedure. `claim-conformance/` gains both classes (§5.10), every reason
|
||
they add, and a context for a gap's object receipt; `signer-conformance/` gains the
|
||
authorities they need. `jwp_verify.py`, `claim_verify.py` and `signer_verify.py`
|
||
report every new outcome; `differential.py` generates `stage` bodies around every
|
||
rule and gaps against receipts at and away from their coordinates, and
|
||
`chain_differential.py` signs chains around §3 step 3 and §6.1 for both verifiers.
|
||
Every vector already in the claim and key-discovery corpora is unchanged, and the
|
||
claim corpus pins the ones before the new classes by a digest computed before they
|
||
were added. In `jwp-conformance/`, `group_chain_break` alone changes, as the item on
|
||
forks and gaps says.
|
||
- No receipt or claim byte of existing material changes; one existing vector,
|
||
`group_chain_break`, is given a genuine chain break, as above.
|
||
|
||
### HTTP delivery (Draft)
|
||
|
||
- **The extension** [http-delivery](extensions/http-delivery.md) binds envelopes to
|
||
HTTP, with no envelope byte and no manifest member changed:
|
||
- the proposed media type `application/wai`, not registered;
|
||
- `codecs` elements `wai1.`/`wai2.` that list the exhaustive union of the
|
||
capabilities a variant may dispatch to or apply, with the floor every object of it
|
||
offers carried separately (`X-WAI-FLOOR`, a DASH `SupplementalProperty`);
|
||
- objects named by their receipt content hash (`<cid>.wai`), immutable, with no
|
||
content coding and no `Vary`, and exactly one envelope each;
|
||
- the `WHP1` head proof: the BLAKE3 chaining values that prove an envelope's head
|
||
against its content hash without the rest of the object;
|
||
- verified range fetch of one `WAI2` component that picks as whole-envelope
|
||
selection does, and falls back to the whole object when a pick has no digest, a
|
||
digest fails, or the plan needs most of the object;
|
||
- receipt, key, grant, parameter-set and playlist sidecars; cache rules; priorities,
|
||
deadlines and the carriage of enhancement companions, refinement components and
|
||
layer objects ([staged-delivery](extensions/staged-delivery.md));
|
||
- HLS and DASH adjacency for on-demand, live and low-latency live; a capability
|
||
probe that never asks a user agent about a WAI element.
|
||
- **jwp-receipts**: §3 step 4 accepts the content hash a verified head proof
|
||
establishes for an object fetched in part, and checks a receipt about a component's
|
||
exploded envelope over the envelope the verifier builds. New §4.4 (objects not
|
||
delivered) and §4.5 (an HTTP cache is not a relay). §1 and §7.9 point to the HTTP
|
||
locations. moq-streaming-format §6 names the HTTP binding.
|
||
- **SPEC**: §2 and §7.1 name the HTTP object and the `WAI2` head; the
|
||
`wai.video.manifest` row admits `render` and `extras` on a playlist receipt.
|
||
- **The registry's wire identifiers.** `codecs::registry::WIRE_IDENTIFIERS` lists the
|
||
names the container, the two delivery bindings and key discovery put on the wire
|
||
outside a manifest, each with its kind, defining section and status; `wai registry
|
||
export` writes them to `registry/capabilities.json` (`wire_identifiers`) and the table
|
||
in `REGISTRY.md`, and a library test checks each against the section it names.
|
||
- **The reference binding** (`wai-rs`, feature `http`): `http_binding::{head, head_proof,
|
||
plan, session, codecs, origin}` — the head and §5.1, the `WHP1` proof built and checked
|
||
with BLAKE3's tree, the root checked before any covered byte is read as a head; plans by
|
||
priority class; a sans-IO fetch session with §7.1's response checks, component digests,
|
||
the whole-object fallback and refinement deadlines; the `codecs` grammar, union, floor
|
||
and support; an origin's headers and Range answers. `container::select_entries` is SPEC
|
||
§7.1's walk with the digest test passed in, which `WaiMulti::pick` and a client holding
|
||
only the head both run, and `companion_indices` / `refinement_indices` bind by entry.
|
||
The `wai` tool gains `wai http publish|codecs|origin|vectors|bench`.
|
||
- **The corpus** [`http-conformance/`](http-conformance/): objects with their head proofs
|
||
and ETag sidecars (a pinned learned image beside AVIF, JPEG XL and PNG of the one
|
||
picture; AV1 at two rates with HDR10+ bound by id; a missing and a wrong digest; an AVC
|
||
base with an LCEVC enhancement; a segment sequence; Appendix D's envelope; a head of
|
||
several chunks) and cases for §4–§7. `wai_http_vectors verify` and the independent
|
||
`http_verify.py` (standard library, written from the text, checked first against
|
||
published BLAKE3 vectors) agree on every case. Falsification patches and Python
|
||
mutations each must be caught, and a differential checks wai-rs's proofs of random
|
||
envelopes in Python (`.github/workflows/wai-http.yml`).
|
||
- **Cache conformance** [`cache-conformance/`](cache-conformance/): a driver for §9's
|
||
rules, run against the reference origin (which must pass), against four
|
||
negative-control proxies (which must each be caught) and against labelled caches where
|
||
an environment provides them; reports name a cache by label, version and configuration
|
||
digest only, and CI uploads them.
|
||
- **Playlists and live** (http-delivery §8.6, §10.4, §11, Appendices G and H):
|
||
- `hls_render::render_hls_with` renders on-demand, live and low-latency WAI media
|
||
playlists (media and discontinuity sequences, a fixed target duration, parts with
|
||
their server control, discontinuities, program date-times, `#WAI-LAYER` lines with
|
||
`N` only, the open segment and its preload hint) and, under the legacy parameters
|
||
with no extras, gives `render_hls`'s bytes; the module needs no dependency, and
|
||
`wai_video_manifest` re-exports it under its old paths;
|
||
- the playlist receipt (`ManifestReceipt`) carries `render` and `extras`; its
|
||
signature binds a render digest only when a rendering is present, so every receipt
|
||
sealed before verifies unchanged (one sealed by the earlier tree is pinned), and a
|
||
rendering has one form; `seal_with`, the receipt file's strict JSON form and
|
||
`mr/<manifest_hash>.json` are new, and a live playlist's revisions chain;
|
||
- `http_binding::hls` reads media playlists, holds a WAI playlist adjacent to its
|
||
classical one (durations to the millisecond, rounded half up, read exactly), renders
|
||
the WAI media and multivariant playlists (bandwidths computed from the objects),
|
||
and keeps the classical master to its one session-data line (`classical_changed`);
|
||
`http_binding::dash` writes `wai.mpd` on the classical timeline;
|
||
`http_binding::playlist_receipt` seals and checks playlists and live chains
|
||
(`playlist_receipt_malformed`, `_mismatch`, `_invalid`, `_unchained`;
|
||
`playlist_malformed`);
|
||
- `wai http hls|mpd|seal-playlist|check-playlist`; the origin serves playlists to be
|
||
revalidated on every use;
|
||
- the corpus gains a staged pair with a layer over its second segment (O11), a
|
||
low-latency set of two segments of twelve parts each, every part its own object
|
||
(O12), a player fixture's WAI segments (O13) beside a playable classical
|
||
presentation, the WAI playlists beside their classical ones, their receipts (the
|
||
live revisions chained) and the published classical master. O10 was already the
|
||
layer set, so these took the next numbers.
|
||
- **Adaptation** (http-delivery Appendix F): `http_binding::abr`'s four reference
|
||
policies (throughput, buffer, utility over buffer, look-ahead) in integers, the budget
|
||
rule that never uses an unlabelled figure, an entry order the session applies on both
|
||
paths (the selector's order now sees the verified head), and layer depth; seeded
|
||
vectors per policy, the integer log, entry orders and depths, which `http_verify.py`
|
||
reproduces.
|
||
- **Playlists and adaptation, after review** (http-delivery §8.6, §11, Appendices F, H
|
||
and I):
|
||
- a live client walks back to the head it holds across revisions it skipped: every
|
||
receipt is also served at `mr/<receipt_hash>.json`, the head fetched again is
|
||
unchanged, and a walk is bounded (`playlist_receipt_parent_missing`,
|
||
`playlist_receipt_walk_exceeded`); no revision lists another cid for an alias or a
|
||
media sequence number an earlier one bound (`playlist_listing_rebound`);
|
||
- Ed25519 is verified strictly (`wai::sig::verify_strict`: a canonical key of large
|
||
order, `S` < L, `R` of large order compared byte for byte), by the playlist receipt
|
||
and `http_verify.py`;
|
||
- DASH adjacency is checked over an XML subset (Appendix I); `wai.mpd` takes the
|
||
classical MPD's period, timescale, `t` and `d` and leaves a gap out; MPD values are
|
||
escaped and checked as XML;
|
||
- adaptation states have stated bounds (`abr_state_out_of_bounds`) and are computed
|
||
exactly within them; under a budget only a labelled option within it is chosen;
|
||
- a gap segment carries no parts or layer lines; the session-data line has one place
|
||
in the classical master; a receipt file holds no member twice, no lone surrogate
|
||
and no number past 2^53 − 1, a grant of no joule ceiling written `null`;
|
||
`seal-playlist --grant`; the origin answers a far-future `_HLS_msn` with 400.
|
||
- an MPD is held as written: timelines in runs, never expanding `@r`, compared run by
|
||
run, at most 256 per document, and an element's attributes read in linear time
|
||
(Appendix I);
|
||
- a budget never raises the policy's choice: with nothing at or below it within the
|
||
budget, option 0 and the budget `unmet` (replacing `floor`);
|
||
- past the walk bound, a revision ahead of the head re-anchors the chain
|
||
(`reanchored`), keeping its bindings, the revisions between unsigned; bindings key a
|
||
line by its URL resolved against the playlist's (RFC 3986), a gap never withdraws a
|
||
listed segment, and a binding is kept while one of the last 256 revisions lists it.
|
||
- **What review found, fixed** (http-delivery §3.1, §4, §7, §7.1, §10, §13, Appendix D):
|
||
- a layer object (a `WAI1` whose capability is refinement-only) is never dispatched:
|
||
it is fetched whole at refinement urgency, verified against its cid and handed to
|
||
staged delivery, and joins a union after `+` through `refines.base`
|
||
(`not_a_layer`, `codecs_layer_unbound`, `layer_tag_malformed`). A `WAI1` whose
|
||
capability is a payload is an ordinary object even when it carries `refines`: it is
|
||
dispatched, described by its capability, and `not_a_layer` when listed as a layer.
|
||
A layer is declared only in the `alt` of its own base's capability;
|
||
- a refinement never moves its base: only base-class bytes count toward the 90 %
|
||
rule, a refinement is always requested on its own, a refinement with no digest or a
|
||
failed one is dropped alone on the range path and on the whole path alike, and a
|
||
refused refinement response abandons only it;
|
||
- a client presents a base as soon as it verifies and applies its refinements as they
|
||
arrive; a refinement's deadline runs from that moment on the caller's clock
|
||
(`FetchSession::base_ready`, `tick`), and `wai http bench` reports the time to the
|
||
verified base apart from the time to the complete fetch;
|
||
- after a component mismatch every byte past `covered_len` is discarded before the
|
||
rest is fetched again; a base request never answered is `base_unanswered`;
|
||
- `Content-Range` numbers are ASCII digits only; an element beginning `wai` in any
|
||
case must be a well-formed WAI element; an empty element or a capability named
|
||
twice is malformed; a `WAI1` naming a record beside a payload fallback is described
|
||
by its fallback; a `codecs` value with non-ASCII text, and a head proof whose lengths
|
||
reach the end of 64 bits, are refused rather than panicking;
|
||
- the reference origin caps what it reads of a request head (16 KiB); gives the whole
|
||
head a deadline from the connection's accept (default 10 s, `--read-timeout-ms`),
|
||
answered by 408 however slowly its bytes trickle in; bounds each connection's whole
|
||
life from its accept, writing included (default 60 s, `--max-connection-ms`), so a
|
||
peer reading a large response slowly is cut off then; serves at most 64 connections
|
||
at once (`--max-connections`), answering 503 to one more, with a cap per peer
|
||
address only when `--max-per-peer` is given (it listens on 127.0.0.1, where every
|
||
client has one address, and a default cap would let a few reconnecting clients shut
|
||
the rest out); decodes `/__hits` as bytes; and refuses `\`, `:` and NUL in a path.
|
||
The wire-identifier test reads each name in its section's body.
|
||
- a verified base is decided: a component that verified is never read again, and of a
|
||
refinement's response only the range requested is kept, so a refinement answered
|
||
`200` with the whole object, a byte changed, never reaches the base. Every response
|
||
carries the caller's clock (`FetchSession::on_response(…, now_ms)`), the base is
|
||
stamped with the time of the response that verified it, and the deadline's
|
||
arithmetic saturates. The corpus's `fetch_timed` cases run the session on a clock;
|
||
`http_verify.py` agrees on them.
|
||
The corpus gains a case for each, and `http_verify.py` agrees with wai-rs on them and
|
||
on a seeded differential outside the corpus, in which a refusal must match by its
|
||
code and each family reports how many cases it generated and how many were refused.
|
||
- **Receipts, keys, grants and parameter sets** (http-delivery §8.1–§8.5, §10.5):
|
||
- a receipt file `rcpt/<h>.json` holds one group receipt (in the labelled payload) or
|
||
claim in JCS, at most 1 MiB, read as jwp-receipts reads one and refused unless its
|
||
link hash is its name (`receipt_name_mismatch`, `receipt_not_served`,
|
||
`receipt_too_large`); a legacy group, whose name does not cover its parent, is
|
||
refused (`receipt_legacy_group`);
|
||
- an index, at most 4 MiB with at most 256 delivery items and 256 claims
|
||
(`receipt_index_too_large`), is read item by item (`receipt_index_malformed`), each
|
||
receipt file fetched once per index, at most 1024 in all
|
||
(`receipt_fetch_budget_exhausted`) and 64 MiB read in all, each file read no further
|
||
than what remains of that (`receipt_fetch_bytes_exhausted`), each cap with its reason
|
||
stated: each delivery item's group goes through
|
||
jwp-receipts §3 against the client's chains, a missing parent fetched by its link up
|
||
to 64 of them, never more, and never skipped (`parent_not_held`, with where the walk
|
||
stopped), a joining client anchoring instead; its object receipt through steps 4
|
||
and 5 against what the client holds — the hash recomputed over the envelope, the cid
|
||
a head proof established (§8.3), or nothing, which is `inclusion-verified` and gives
|
||
no property reading (§10.5); each claim by its subject against the items that
|
||
passed, its parent fetched by its link, then §3.1 step 6 against what those items
|
||
deliver and the receipts it names (`figure_not_held`, `context_not_held` where the
|
||
client does not hold them; an energy claim naming an OER receipt, which this revision
|
||
does not carry over HTTP, is `context_not_held`), a declaration it names read as
|
||
§8.1 reads every file fetched by name before step 6 compares it, a recovery record
|
||
checked with the client's matchers whether or not it holds the object, and a soft
|
||
binding of another object attesting the ancestor only through §3.2's recovery,
|
||
under the held publisher key that signed it;
|
||
- a key log anchors only from the origin's own location over HTTPS, answered `200`
|
||
with no redirect (`key_log_not_from_origin`), from a retrieval the verifier made
|
||
itself, asking with `Cache-Control: no-cache`; a key log is read to 1 MiB and a longer
|
||
one refused, anchored or cached (`key_log_too_large`); a log through any cache adds revisions
|
||
and never moves the anchor, an older copy reported (`key_log_rolled_back`); a grants
|
||
file is a locator (`grants_file_malformed`); a key status is served for a quarter of
|
||
the head's `status_max_age_s` at most; a module grant is §7.9's form, and a JCP grant
|
||
is carried only by reference in the receipt that names it;
|
||
- a parameter set served by its digest is held only by its pin and §7.9
|
||
(`pin::verify_bytes_with`, `int_payload::resolve`), never by its name.
|
||
- `wai-rs`: `http_binding::receipts` and `http_binding::keys`;
|
||
`jwp_hash::EstablishedHash`, built only from bytes or by a fetch session whose head
|
||
proof verified (`FetchSession::established`); `ObjectReceipt::accept_hash` and
|
||
`verify_against_hash`, `WaiReceiptProperty::check_against_object_hash` and
|
||
`ReceiptChains::verify_admitted_object`, which the existing checks call;
|
||
`energy_figure::HeldObject`, an object receipt's figure for step 6; `wai http
|
||
receipts`, which fetches under a deadline and the caps above, follows no redirect,
|
||
and with `--key-origin` retrieves the key log from the origin itself over TLS
|
||
(the `cli_tls` feature: a TLS client validating against the operating system's
|
||
trust store; SNI and the certificate check name the origin's host, port 443 by
|
||
default, each address the host resolves to tried in turn), every step within one
|
||
deadline however slowly a peer answers; a build
|
||
without it refuses to anchor rather than fetch the log over plaintext, and the
|
||
test-only `--test-plaintext-connect` anchors with its own path, `test-plaintext`,
|
||
wherever the anchor or a confirmation is reported, as anchoring nothing a verifier
|
||
may rely on. A group or
|
||
object receipt whose `kind` is not a string is `wrong_kind`
|
||
(jwp-receipts §3 step 1), as the independent verifier already read it.
|
||
- The corpus serves receipts, indexes, a key log, a status, a grants file and a set;
|
||
`http_receipts.py` checks them for `http_verify.py` through the jwp-receipts, claim
|
||
and key-discovery verifiers, and the differential adds receipt files, indexes,
|
||
grants files and key logs.
|
||
- No envelope byte or receipt byte changes for existing material.
|
||
|
||
### Packaging: objects, the stage index, the floor, catalogs and the package receipt
|
||
|
||
- **Objects** ([packaging](extensions/packaging.md) §5): each one envelope, named by its
|
||
receipt content hash, with no manifest member naming a position, so a repeated piece
|
||
is one object. The packer's base encode calls `choose_q` per piece.
|
||
- **The stage index** `WSIX` (§6, `wai-rs` `stage_index`, feature `video_package`): per
|
||
component, every object of the package by content hash, its lengths, role, layer,
|
||
the tier and decoded-output digest of the prefix it completes, its SSE, and the class
|
||
plan; its digest and a Merkle stage root; a named reason for every rule a verifier
|
||
applies to an index and to the objects it lists.
|
||
- **The floor** (§7): a supplied AV1 floor, checked against the keyframe plan
|
||
(`wai-pack plan --out`) and the listing the policy pins, written as floor objects
|
||
that carry `derived_from` and as CMAF files (`init.mp4` with `av01`/`av1C`, one
|
||
fragment per piece).
|
||
- **SPEC §7.1, *Derived renditions***: `derived_from` on a manifest or a rendition
|
||
entry states that a payload presents another object and was made from it; an
|
||
OPTIONAL member under the MINOR rule that never breaks a parse (§8).
|
||
- **Catalogs** (§8): on http-delivery's Mode N, a classical master and floor playlist
|
||
that name nothing of WAI; per class, its master (with the one SESSION-DATA line
|
||
http-delivery allows), its WAI playlist with the codecs union and floor, and a WAI
|
||
media playlist adjacent to the floor's, with `#EXT-X-GAP` for a piece the class
|
||
takes as floor only and a `#WAI-LAYER` line for each refinement layer it lists (no
|
||
package of this revision carries one: the packer refuses refinement rungs, §11.4);
|
||
the classical and per-class DASH MPDs; the MoQ catalog; and a stage view per class.
|
||
- **The version-2 package receipt** (§9, `wai-rs` `package`): binds the policy digest
|
||
and each component's index by digest and stage root, with entry, object and byte
|
||
counts every verifier recomputes; RFC 8785 JSON both ways; a measurement claim beside
|
||
a metered figure. The version-1 receipt gains the same JSON form and keeps its
|
||
bytes. Both share the figure profile `wai:video-package-receipt-id`.
|
||
- **`wai-pack pack`, `verify` and `listing`**, and the corpus's golden package
|
||
`video-base-01`, its index, object and receipt mutations with their reasons, and
|
||
version-1 receipts as JSON. The registry lists packaging's wire identifiers.
|
||
- No envelope byte or receipt byte changes for existing material.
|
||
|
||
### Packaging: a declared policy, its digest, and pieces cut at scene changes
|
||
|
||
- **The extension** [packaging](extensions/packaging.md), first sections: a package is
|
||
made under one TOML policy, `wai.pack.policy/1` (§3), read in a fixed order with
|
||
every refusal named: value kinds a canonical JSON cannot carry exactly (floats,
|
||
dates, negatives, integers past `2^53 − 1`), then the schema, every default written
|
||
in, then the constraints across members (ladders and their refinement chains,
|
||
classes, rules, deadlines against layer lags). The effective policy's digest is
|
||
BLAKE3 over its RFC 8785 form, so two spellings of one policy have one digest.
|
||
- **Pieces** (§4): `wai.pack.scenecut/1` cuts a video component at scene changes by
|
||
integer histogram distance, with a bounded lookahead whose return and revisit
|
||
clauses keep a short flash from cutting; a decision reads no frame past the
|
||
lookahead, so live and on-demand packing cut alike. Pieces are bounded by `min` and
|
||
`max`, aligned across components to the lead, and classified static, synthetic,
|
||
high-motion or natural. Y4M and WAV sources convert by stated integer rules.
|
||
- **`wai-pack`**, the reference packager, as far as a plan (`wai-pack policy`,
|
||
`wai-pack plan`), with float arithmetic denied for the whole crate, and the corpus
|
||
[`pack-conformance/`](pack-conformance/) with a second implementation in Python.
|
||
- **What a packager may emit** (§3.6): only capabilities SPEC §5 registers as payloads
|
||
with a pinned format, in a role each can play, and under `royalty_free_only` only
|
||
royalty-free ones; every refusal names the capability (`capability-unknown`,
|
||
`capability-not-payload`, `capability-candidate`, `capability-role`,
|
||
`license-unclassified`, `license-refused`).
|
||
- **One integer video encoder** (§5.5): `int_video_enc` codes RGB8 frames as the
|
||
`WIV1` keyframe-0 payload SPEC §5 pins to `wai.video.int_motion`, with integer block
|
||
motion search (whole- or half-pel) over the decoder's own prediction, residuals
|
||
quantized in a closed loop, and one residual table for the clip; it states the SSE
|
||
of its reconstruction. `int_payload::encode_wiv1_zero_motion` is its zero-motion
|
||
case and writes the bytes it wrote before. The corpus holds its payloads for every
|
||
piece of every fixture, decoded independently to the same frames.
|
||
- **`wai::jcs`**: the canonical JSON form moves out of `claim_receipt` into a module
|
||
of its own, compiled under every feature set, unchanged in behaviour.
|
||
- **Choosing the step** (§5.6), specified and built ahead of the stage that writes
|
||
objects: the largest of `q_candidates` (read as a set) whose SSE per sample, in
|
||
thousandths, is at most `max_sse_px_milli`, else the smallest
|
||
(`wai_pack::encode::choose_q`), with goldens recomputed independently.
|
||
- **Reading what a packager is given**: a policy is read from its bytes (UTF-8, no
|
||
byte-order mark, nesting at most 32 deep, `policy-toml` otherwise), ids are unique
|
||
across every kind, a cut window holds at least 3 scores, a WAV block alignment is
|
||
compared without wrapping, and no declared size sizes a buffer; the segmenter's state
|
||
is bounded by its lookahead and window. The encoder codes in two passes and states
|
||
what it holds (`int_video_enc::working_bytes`), and `rans::RansReverseEncoder` codes
|
||
values last-first into the same bytes.
|
||
|
||
### Staged delivery in the browser, a base writer, a trace and a bench
|
||
|
||
- **A base is bounded before it is decoded, and its chain from the start**
|
||
([staged-delivery](extensions/staged-delivery.md) §4.5, §4.2, §14). A base whose units'
|
||
output, with 32 bytes for each stage its kept record holds, passes the chain budget is
|
||
not added: its envelope is reported `chain-over-limit`, no chain is kept, its kept
|
||
record is untouched, and its layers stay held. Where a base's capability declares a
|
||
cost before decoding (the integer payloads SPEC §5 pins), a sink applies its cost
|
||
limit to that declared cost first, and SPEC §4 treats a base past it as one it does
|
||
not decode, moving on to the fallback; a capability that declares no cost
|
||
(`wai.neural.int_synth`, `wai.neural.int_mlicpp`) decodes under its format's caps. The corpus gains `base_over_budget`;
|
||
`staged_verify.py` implements the rule independently.
|
||
- **The sink in the browser.** `wai-web` binds `wai::staged::StageSink` for JS
|
||
(`src/stage.rs`). `StageBase.decode` decodes a base envelope by SPEC §4 through the
|
||
integer path, with its pin verified against the parameter set the page holds in
|
||
memory and under the page's cost limit, which is a required argument (`null` for the
|
||
format's caps only; it bounds a base whose capability declares a cost);
|
||
`StageBase.fromStage0` takes a base the page decoded elsewhere, which is **claimed**:
|
||
its capability must be the manifest's or its fallback and one SPEC §4 would take (an
|
||
inert envelope is refused), its tier is never above what the capability gives an
|
||
unpinned base, and every report, unit and stage hash of its chain says `claimed`. A base's facts cannot
|
||
be raised from JS (no `viaFallback` setter; `attested` only rises), and `addBase`
|
||
takes the base. `StageSink` adds bases, offers layers, expires holds, removes, attests
|
||
and forgets bases, and reads back every unit's stage, producer, frozen flag, tier,
|
||
shape, output, stage hash (with its `claimed` mark) and squared error against a
|
||
reference. `stageLayerInfo` reads a layer's `refines`, `WIR1` header, declared cost and
|
||
byte split (framing, manifest, payload), and a base's declared decode cost, before
|
||
either is offered. Reports and units come back in the corpus's own form. Media reach
|
||
the page as `WAI1` bytes only.
|
||
- **The browser equals the native sink.** `wai-web/demo/verify_staged_wasm.mjs`
|
||
replays every case of `staged-conformance/` through the wasm32 build: each integer
|
||
base is decoded in wasm32 and must give its `stage0.bin`, its tier and its fallback
|
||
flag, and every report, final unit, stage hash and clip hash must equal what the
|
||
native sink wrote. `wai-web-wasm.yml` runs it and watches the corpus; the crate's
|
||
native tests replay the corpus through the binding's pure-Rust half as well.
|
||
- **One base path.** `staged::decode_base` runs SPEC §4 for a base through
|
||
`Wai::dispatch_verified`: its capability, then its fallback, each only where it is
|
||
dispatchable, so a refinement-only or companion-only capability makes the envelope
|
||
inert (`missing_capability`) and never reaches its fallback. It takes the sink's cost
|
||
limit (checked before any symbol is decoded or output allocated, `cost_over_limit`)
|
||
and its key log (SPEC §3.1 check 5 over a pin and over an unpinned set; none skips
|
||
it). `decode_base_via` asks for one of the two capabilities and refuses any other
|
||
(`not-offered`). The refusals come in a stated order: `not-wai1`, then `encrypted`
|
||
(SPEC §6.4 before §4), then `not-offered`, then §4's; a parameter set is read only
|
||
for a capability that reads one. Its own tokens are staged-delivery's kebab-case sink
|
||
vocabulary; SPEC and payload codes pass through in snake_case. A pin counts toward
|
||
the base's tier only where it resolved. A
|
||
one-file set pinned without a form offers every held file to the pin, the member the
|
||
capability reads first. The CLI and the browser share this path.
|
||
- **A keyframe-0 base writer.** `int_payload::encode_wiv1_zero_motion` codes RGB8
|
||
frames as a `WIV1` keyframe-0 payload (zero motion, one block per frame, a closed
|
||
group of pictures): the reference encoder the corpus generator used, now in the
|
||
library. The corpus regenerates byte for byte through it. It refuses a clip past the
|
||
format's caps before allocating (`too_large`). `wai stage base` writes a
|
||
`wai.video.int_motion` base with it from raw frames, with its dimensions multiplied
|
||
checked, and decodes it back before writing.
|
||
- **A trace to check against.** `wai stage apply` is now a sink replay: envelopes are
|
||
taken in order, routed by content (one with `refines`, or a refinement-only
|
||
capability, is a layer; any other is a base, decoded by SPEC §4), and a base given
|
||
after its layers releases the layers held for it. `--json` prints each step's reports
|
||
in the corpus's form (bytes that are not a `WAI1` this sink reads, and a base it
|
||
cannot decode, are the step's error by their code, and the replay goes on; a base
|
||
read with `--stage0` is claimed, and its step, units, chain's reports and final entry
|
||
say so), and every stage a unit reached in the step, in the
|
||
order the sink applied the layers (`StageSink::set_trace`), so a batch that takes a
|
||
unit up several stages lists each; every stage hash a delivery in that order reaches
|
||
is in it. A test replays the corpus through it and requires the cases' reports and
|
||
final units.
|
||
- **`wai_stage_bench`.** Measures one staged set natively: per stage, the envelopes'
|
||
bytes (framing, manifest and payload, and cumulative), PSNR or SNR per unit and over
|
||
the clip from the exact integer squared error against the source at the units' final
|
||
shape (a unit of another shape has no figure at that stage), the time each stage took
|
||
(median and quartiles over at least 20 runs), the direct comparator (one layer per
|
||
base at the stage's step, straight from stage 0, or why it does not apply) and every
|
||
unit's stage hash. Energy is reported as unmetered.
|
||
|
||
### Staged delivery: refinement layers, base admission, and WAI2 refinement components
|
||
|
||
- **The extension** [staged-delivery](extensions/staged-delivery.md): a stage set is a
|
||
base and layers, each layer its own `WAI1` envelope naming, by content hash, the
|
||
base and the producer each unit it covers is expected to have (`refines`: `base`,
|
||
`prev` as one producer or runs, `layer`, `span`). A sink holds per unit a stage, a
|
||
producer and a tier, and applies a layer per unit by one algorithm with a closed
|
||
list of reasons (§4.1, §4.2): every envelope-scoped step (well-formed `refines`, a
|
||
refinement capability with no fallback, the base present, not through a fallback,
|
||
admitted, attested, a well-formed payload within the sink's cost limit and its chain
|
||
budget) before the unit steps (conflict, readiness, geometry, deadline). Layers a unit is not ready for are held
|
||
to a declared bound and released when its stage advances; layers released together
|
||
form a batch, so two conflicting layers released together are both refused and the
|
||
unit frozen, whatever their order. A layer that differs from one that already passed
|
||
for the same unit freezes it at its stage. A unit's tier is the weakest of its base
|
||
and layers, `entropy-consistency` does not pass through a layer, and a stage hash is
|
||
stated only at `decode-equivalence`. Deferral, live deadlines (a sink never delays a
|
||
unit for a layer, and counts the units presented below the stage their layers would
|
||
have reached on time), attestation (a receipt verified without the layer's bytes
|
||
never authorizes an application) and byte counting (envelope bytes; receipts,
|
||
framing and parameter sets separately; never JSON or base64) are stated.
|
||
- **Resource bounds** (§4.5, §14, A.5): a sink declares a chain budget per base, over
|
||
its units' output, 32 bytes per stage, and the payload and declared working bytes of
|
||
every layer it retains with one unit's output and 32 bytes for each unit it covers
|
||
and has not been applied to; a layer that would pass it is refused
|
||
(`chain-over-limit`) before any stream is decoded, and a layer no unit is held for is
|
||
dropped. The declared working bytes count the stream index. A sink's work per layer
|
||
is linear in its payload and declared symbols up to a logarithmic factor in its
|
||
units, and §14 states how. A sink that drops a base's chain discards the layers held
|
||
for it (`base-absent`) and keeps each unit's record that is not empty (its
|
||
producers, a pending producer, its frozen flag), within a declared record budget
|
||
whose oldest bases are forgotten first; the base decoded again advances only along
|
||
that record, and a frozen unit replays only its own record, in order.
|
||
Decoding a base again keeps its chain, and a receipt verified for the base, before or
|
||
after it was decoded, makes it attested from then on, across dropped chains; a layer held for its base and offered
|
||
again keeps the strongest receipt evidence, and over an attested base counts as
|
||
arriving when its receipt was verified; a set split across bases shares one live
|
||
clock (`first_unit`).
|
||
- **The residual payload `WIR1`** (Appendix A): one rANS stream per unit;
|
||
`clamp(pred + r·q)` with the identity, up2 (the half-pel arithmetic, odd sizes
|
||
included) and rate (linear interpolation, `rdiv`'s halves away from zero, channels
|
||
interleaved) predictors. It follows the integer payloads' rules: a parse in a
|
||
stated order with the closed list of their codes, caps, a declared cost a sink reads
|
||
before decoding, refusal at the first symbol a stream runs out on, canonical stream
|
||
ends; and it reads no parameter set.
|
||
- **Refinement capabilities** (SPEC §5, *Refinement capabilities*):
|
||
`wai.image.int_refine`, `wai.video.int_refine`, `wai.audio.int_refine` and
|
||
`wai.splat.int_refine`, each refinement-only, royalty-free, `StandardDefined` and
|
||
exact given the base it refines. SPEC §8 adds the refinement-only role; SPEC §4 never
|
||
dispatches one, and a sink that does not implement the extension finds it
|
||
unsupported. The registry gains the `refinement` kind and role, and the export is
|
||
regenerated. *Envelopes this makes non-conforming* (they still parse): a `WAI1`
|
||
carrying a refinement capability without `refines`, with a fallback, or as a
|
||
fallback; `refines` on any other capability; a refinement capability as a `WAI2`
|
||
rendition or companion; a companion bound to one.
|
||
- **Base admission is one table** (SPEC §5, *Base admission*;
|
||
`codecs::BASE_ADMISSION`): the bases of each refinement and of the LCEVC enhancement
|
||
layer, whose bases are unchanged. `enhancement_bases`, `companion_applies_to` and
|
||
`admits` read it.
|
||
- **WAI2 refinement components** (SPEC §7.1), on the component identity (`id`,
|
||
`sha256`, `prior`; no other digest is defined): an entry with
|
||
`role: "refinement"` carries `refines` naming entries by `id`, follows every
|
||
selectable entry, is never selected, and is checked by `check_component_roles` in a
|
||
stated order. *Exploding a component* writes its `WAI1` envelope, from
|
||
`envelope_manifest` verbatim or from the `WAI2` manifest's members as written, with
|
||
ids replaced by the content hashes of the named entries' exploded envelopes, so a
|
||
component and the layer carried alone are the same object; `WaiMulti::pack` packs
|
||
`WAI1` envelopes with their manifests verbatim, and they explode back to the same
|
||
bytes; it refuses an envelope it could not give back (bytes around a manifest's JSON
|
||
value or after a payload, or a manifest that inside a `WAI2` passes the depth limit)
|
||
and checks its output before returning it. Each entry explodes on its own, so one
|
||
that cannot leaves the others intact. A refinement's `envelope_manifest` names no
|
||
fallback. A malformed `refines` or a mismatched `envelope_manifest` does not make a
|
||
manifest unreadable.
|
||
- **SPEC §2**: member names are unique within each object of a manifest, at any depth,
|
||
and an integer member is written as an integer (`-0`, `1.0` and `1e0` are not); both
|
||
readers refuse a manifest that breaks either. `span: null` is `span` absent.
|
||
- **Reference implementation**: `staged::{StageChain, StageSink, wir1, predict,
|
||
wir1_arith, tier, schedule}` (feature `staged`); `container::{Refines, Prev}`;
|
||
`WaiMulti::{explode, explode_each, explode_all, pack, check_envelope_manifests,
|
||
refinements_of}`; `container::check_unique_members`; `wai stage
|
||
refine|apply|inspect|pack|explode` (`refine --out-dims` gives a layer's shape);
|
||
`StageSink::{remove_base, forget_base, mark_attested}`; `wai wrap` refuses a refinement
|
||
capability, and `wai inspect` shows `refines`. `RansEncoder` writes the bypass
|
||
escape, and `int_entropy::hist_to_cdf` builds a table from a histogram in integer
|
||
arithmetic. In `ternary::rans_encode_trits`, the encode call sat inside
|
||
`debug_assert!`, so a release build coded no symbol; it now runs in every build.
|
||
- **Conformance**: the corpus [staged-conformance](staged-conformance/) (image, video,
|
||
audio and splat sets; cases for every reason of §4.2 and every `WIR1` refusal code,
|
||
in the order the steps check them, and for §3's rules, holds and their bound,
|
||
batches, frozen units, the chain budget, a base decoded again, receipt evidence and
|
||
live deadlines, one clock across bases included; `WAI2` cases for each refinement
|
||
rule of SPEC §7.1, for exploding entry by entry and for what a packer refuses; the
|
||
live schedule), written and verified by `wai_staged_vectors` and verified
|
||
independently by `staged_verify.py`, which shares no code with `wai-rs`; the pure-
|
||
Python BLAKE3 moves to `claim-conformance/_blake3.py`, shared by both Python
|
||
verifiers. Cross-architecture goldens pin every stage of an image, a video and an
|
||
audio set; Kani proves, over bounded windows, the arithmetic step, the up2 bounds,
|
||
`rdiv` and the rate indices. `rdiv` and the rate indices are computed in checked
|
||
64-bit arithmetic.
|
||
|
||
### Staged-delivery measurement: counted bytes, integer quality, the output tree
|
||
|
||
- **The extension** [staged-measurement](extensions/staged-measurement.md): how a
|
||
figure about staged delivery is measured and published. The counted bytes of an
|
||
object are its envelope to the byte (`12 + man_len + P` for `WAI1`,
|
||
`8 + man_len + 2 + 8n + Σ P` for `WAI2`), and an object is countable only where WAI
|
||
checks the format of every payload it carries: the pinned integer formats and `WIR1`,
|
||
by the parse a sink runs, for the capability and for a `WAI1` fallback with a pinned
|
||
format. Any other capability, a classical codestream or an unregistered string among
|
||
them, is not countable (`no-format-check`), and neither is a `WAI2` component whose
|
||
`sha256` is not its payload's, nor an encrypted payload; no estimate stands in for a
|
||
figure that is not countable. A figure carries the framing label `registered`. Quality is integers per
|
||
presentation and per prefix, against the source and against the final prefix: SSE
|
||
over RGB and over an integer BT.601 luma, a Q32 structural similarity over clipped
|
||
8×8 windows at stride 4 with an edge-aligned last window on each axis, so every pixel
|
||
is in a window (bounded in 128-bit integers), PCM SSE and reference energy,
|
||
and tensor SSE and largest difference, each pooled over units; decibels are display
|
||
only. First presentable, layering overhead as two rate–quality points (never a
|
||
ratio), delivery simulation, decode time and energy (only a clean attribution verdict is a figure; a rail
|
||
that is not live is unmetered), deduplication, dated records, what CI asserts, and a
|
||
media-class matrix are stated. The output tree (Appendix C) commits to every
|
||
presentation, one SHA-256 leaf per (unit, layers), under RFC 6962's tree hash, by its
|
||
root and leaf count together, with audit paths checked as RFC 9162 checks them under
|
||
the committed count (`output_tree::OutputCommitment`).
|
||
- **Measured titles** (Appendix D): a staged set over a staged-conformance base, read
|
||
where it is, with only what the stage model does not carry beside it (unit duration,
|
||
capture times, the source, single-layer references, the fixture tool's own
|
||
reconstruction digests), and a content identity over the measured files.
|
||
- **Reference implementation.** `wai-rs` feature `stage_measure` (pure integer, no
|
||
ONNX, wasm32-clean; CI builds it for wasm32): `measure_framing`, `output_tree`,
|
||
`stage_measure::{title, decode, run}`, which decodes each base through the sink's own
|
||
base decode, `staged::decode_base_pinned` (which also gives the pin it verified),
|
||
under the cost limit, and applies the layers through `staged::StageSink`
|
||
under the chain budget, handing each stage's output over as it is reached. The tools'
|
||
stated limits are `staged::TOOL_DEFAULT_LIMIT` and `staged::TOOL_DEFAULT_CHAIN_BUDGET`
|
||
(staged-measurement Appendix D states each figure and why); `wai stage` (`apply`,
|
||
`refine`, `base`), `wai_stage_measure eval` and `wai_stage_bench` decode every base
|
||
under them, hold every layer and chain to them, and take
|
||
`--limit OUT,SYMBOLS,WORKING,MACS`. A title's `limits` lower a limit and never raise
|
||
one: each figure is the lower of the title's and the tool's.
|
||
The corpus's `refusals/` hold titles a tool must refuse, with the code: a base past its
|
||
chain budget, the two declared-clip bound vectors (refused before anything is
|
||
allocated for them; a counting-allocator test shows it), the 1 GiB one again under a
|
||
title that states limits of 2^40, two inert envelopes, and a layer past its title's
|
||
cost limit. `tests/vectors/staged_bounds/` gains a base whose up2 layer declares
|
||
67,092,480 output bytes, which `wai stage apply` and `wai_stage_bench` refuse with no
|
||
flag. Always on:
|
||
`quality` and `content_dedup` (the package receipt's dedup figures now go through
|
||
it; a map that gives one content hash two lengths no longer verifies); native only:
|
||
`bench_env`, the machine record, which `wai_decode_bench` now prints from. The
|
||
`wai_stage_measure` tool evaluates a title (`eval`), verifies the corpus (`verify`)
|
||
and writes it (`gen-fixtures`, `generate-goldens`).
|
||
- **The corpus** [`staged-measure-conformance/`](staged-measure-conformance/): a
|
||
picture, a clip, an audio clip and a splat attribute tensor, each with its figures in
|
||
`goldens.json`; `wai_stage_measure verify` and an independent Python verifier,
|
||
`measure_verify.py`, both reproduce every figure, and CI runs both.
|
||
- **The delivery simulator** (§10, Appendix B; `stage_measure::sim`,
|
||
`wai_stage_measure sim`): a discrete-event model in integer microseconds of a sender
|
||
putting a title's objects on one link (`strict-priority` or `fifo`; `datagram` or
|
||
`subgroup` carriage with delivery timeouts that drop or reset; a layer cap; a VOD
|
||
window or live capture) and a sink decoding and presenting them (workers, `base-first`
|
||
or `fifo` decode, playout deadlines). Its figures are labelled simulated. Bases are
|
||
sent, and decoded under `base-first`, in the order they became available. A
|
||
refinement's stall of its base is then bounded by one chunk (strict priority) or a
|
||
timeout and one chunk (`fifo`), plus one refinement decode (`base-first`): §10.4
|
||
proves it, for every plan with no base timeout, a bounded link schedule and
|
||
`base-first` decode. It is also checked on generated plans drawn over that whole
|
||
range and on the corpus's plans, and for each unbounded part a counter-plan's stall
|
||
passes the bound of the same plan with that part restored. The corpus `sim/` and an independent
|
||
`sim_verify.py` reproduce every result.
|
||
- **Wire.** No envelope, payload, receipt, claim, pin or corpus file of an earlier
|
||
revision changes bytes.
|
||
|
||
### Delivery telemetry: CMCD version 2 and CMSD for WAI objects
|
||
|
||
- **The extension** [delivery-telemetry](extensions/delivery-telemetry.md): an unsigned,
|
||
advisory observation about WAI delivery (no joule figure, no acquisition class, no
|
||
quality figure; content and receipt hashes only as pointers), read strictly with
|
||
exact members; its CMCD version 2 report, in Request mode for requests and Event mode
|
||
otherwise (`e=rr` for responses, play states, custom events for switches, dispatch
|
||
outcomes, layer-cap changes and dropped frames), with header shards; custom keys
|
||
under `science.transaction.wai-`, each carrying a String; switch reasons; CMSD-Static
|
||
and CMSD-Dynamic for a WAI object; session metrics from one session's log (video
|
||
start time, exit before start, start failure, stalls, engagement, units, counted
|
||
bytes); and privacy rules: a content id is a content hash or a short token, and an
|
||
emitter puts no address, query, token or personal data in it; an object URL holding a
|
||
`?`, `#`, `@` or `\` is refused rather than stripped, so no query, signed-URL token or
|
||
user information enters an observation however a parser splits the URL; and a
|
||
Request-mode report goes only to the host serving the object. Cohort assignment and capable share are not in this revision.
|
||
- **Reference implementation.** `wai-rs` feature `telemetry` (no dependency;
|
||
wasm32-clean): `telemetry::{sf, observation, cmcd, cmsd, qoe}`, with the RFC 9651
|
||
subset the reports use; the `wai_telemetry` tool.
|
||
- **The corpus** [`telemetry-conformance/`](telemetry-conformance/): observations of
|
||
every event kind with their reports, refused observations, CMSD descriptions and
|
||
session logs. `wai_telemetry verify` requires every value; a differential encodes
|
||
every report with an independent open-source CMCD and CMSD library at locked versions
|
||
and requires the same bytes, reads them back, and validates them; CI runs both.
|
||
- **Other text.** [moq-streaming-format](extensions/moq-streaming-format.md) §5 requires
|
||
that telemetry **MUST NOT** restate the per-object property's figure, class or evidence;
|
||
[energy-exposure-mapping](research/energy-exposure-mapping.md) gains a dated
|
||
addendum.
|
||
- **Wire.** No envelope, payload, receipt, claim or corpus file of an earlier revision
|
||
changes bytes.
|
||
|
||
### The integer codecs' payloads pinned in place: `WIV1`, `WIH1`, `WIA1`, `WIS1`
|
||
|
||
- **Pinned in place** (SPEC §5, *Payloads of the WAI-native integer codecs*, and the
|
||
new extension [integer-payloads](extensions/integer-payloads.md)): the payload of
|
||
`wai.video.int_motion` is `WIV1` with keyframe 0; of `wai.video.int_hyper`, `WIV1`
|
||
with keyframe 1, a nested `WIH1`; of `wai.neural.int_hyper`, `WIH1`; of
|
||
`wai.audio.int_codec`, `WIA1`, which carries the sample rate; of
|
||
`wai.splat.int_codec`, `WIS1`, which carries each attribute's presentation range.
|
||
These strings' rows named a decoder and no byte layout; three of them
|
||
(`wai.video.int_motion`, `wai.audio.int_codec`, `wai.splat.int_codec`) also named
|
||
the tool that wrote a JSON bitstream, which SPEC §5 called the bytes the codec
|
||
normally emits. So the pin changes what these strings carry. SPEC §8 gains
|
||
*Completing a registration*, which permits it for a WAI-native string whose row
|
||
fixed no byte layout and lists the envelopes it makes non-conforming; from this
|
||
revision their formats are fixed as SPEC §8 states. No new string is registered.
|
||
`codecs::PINNED_INT_PAYLOADS` is the one table of strings and magics.
|
||
- **The JSON is a converter input, never a payload.** The encode and export tools
|
||
still write JSON; `int_payload::{wiv1,wih1,wia1,wis1}_from_json` convert it and
|
||
state what it left implicit (an absent `mv_precision` is 1; the first frame carries
|
||
no motion vectors; a short motion-vector list is padded with `(0, 0)`; the audio
|
||
sample rate comes from the export's `audio.json`; a splat range must be exactly a
|
||
binary32 value; the JSON `afrac` must be the model's; the channel count must match
|
||
the medians, tables and offsets). *Envelopes this makes
|
||
non-conforming:* a `WAI1` or `WAI2` that carries anything but the pinned format
|
||
under these strings, a tool's JSON output among them. It still parses; a sink
|
||
refuses it at decode (a JSON form with `bad_magic`) and does not try the fallback.
|
||
Every emit path refuses to write it: `Wai::to_emit_bytes` and
|
||
`WaiMulti::to_emit_bytes` (so the C packers `wai_envelope_pack` and
|
||
`wai_envelope_pack_multi`, `WAI_ERR_INVALID_ARG`), `wai-web`'s `pack` and
|
||
`packMulti`, and `wai wrap`. They run `container::check_int_payload_framing`, which
|
||
parses the payload with the wire parser a sink runs and the capability's keyframe
|
||
kind, so it checks every parse rule of the extension's §2–§6 and leaves to the
|
||
decode only what needs a parameter set or the streams (§7); `wai inspect` reports
|
||
what it finds. A refused emit is the new `EmitError::IntPayload`. An encrypted
|
||
envelope's payload is ciphertext (SPEC §6), so the emit paths skip it and a sink
|
||
checks the plaintext after decrypting (SPEC §6.4); `wai inspect` says so. No
|
||
committed envelope or conformance corpus carried such a payload. The reference
|
||
dispatchers (`Wai::dispatch_verified`, `WaiMulti::pick`, and so the neural sink,
|
||
its C ABI and `wai-web`'s `dispatchCapability`) implement SPEC §6.4 step 2: they
|
||
open no scheme, so an envelope whose manifest declares an encryption other than
|
||
`"none"` is inert at them (`unsupported_encryption`; `container::Inert::Encrypted`,
|
||
`container::Skip::Encrypted`, `neural::DecodeError::Encrypted`), and a payload sent
|
||
in the clear under a claim of encryption is not decoded as plaintext. A `WAI2`
|
||
manifest's `security` member (now stated in SPEC §7.1) must be a security object to
|
||
be written (`EmitError::Security`, `container::manifest_v2_security`); one that is
|
||
not is read as encrypted.
|
||
- **One integer decode path.** `int_payload::decode_int` decodes the strings above
|
||
and `wai.neural.int_synth` and `wai.neural.int_mlicpp` from parameter files held in
|
||
memory, with no ONNX runtime and no filesystem, natively and for `wasm32`, through a
|
||
dispatchable capability only. A refusal is one code from a closed list
|
||
([integer-payloads](extensions/integer-payloads.md) §8). `int_payload::resolve`
|
||
verifies a pin over sets held in memory and runs SPEC §3.1's check 5 for a pinned
|
||
decode and over the set an unpinned decode reads. The neural sink delegates every
|
||
integer capability to it: integer video, PCM and splat attributes come back as
|
||
`neural::Decoded::Int`, never converted; `ModelRegistry::builtin` advertises
|
||
`wai.video.int_motion`, which reads no set; the C ABI refuses an `Int` output. A
|
||
capability this build has no integer decoder for (`unsupported_capability`) moves
|
||
dispatch on to the fallback, as SPEC §4 treats a capability a sink cannot decode.
|
||
Check 5 covers every identity of the bytes a decode reads (SPEC §3.1): after a pin
|
||
verifies, it runs again over the listing of the members read and a one-file set's
|
||
file digest (`int_payload::read_identities`), so a set revoked under one identity
|
||
is not reached through a pin naming it by another; and a listing beside an
|
||
unpinned set counts only when it matches the files read. Outside the integer path,
|
||
at a sink that holds a key log, an unpinned decode reads what it can read once —
|
||
the registered file of a one-file set, or every file below the directory its
|
||
decoder reads from (`pin::read_dir_set`) — and check 5 runs over every digest a pin
|
||
over it could name: the file's, or the directory's derived listing
|
||
(`pin::listing_of_dir`) and each file's, and each listing beside them that matches.
|
||
The decode reads only those bytes, never the path again; where the key log holds
|
||
the capability's sets to jwp-receipts §7.9, only the part §7.9 authorised (the
|
||
whole, the files a granted listing names, or one granted file). So a revoked set
|
||
is refused with no listing beside it, a granted listing authorises the files it
|
||
names and nothing else, a file changed after the check is not read, and a file the
|
||
decode needs outside the set is `set_incomplete` (SPEC §4, *Module authorisation*).
|
||
`pin::PinAuthority` gains `requires_grant`, with a default.
|
||
*Breaking:* `neural::Decoded` gains the variant `Int` and is now `#[non_exhaustive]`,
|
||
so a `match` on it outside this crate needs a wildcard arm; `neural::ParamSource`
|
||
gains `UnpinnedRead` (an unpinned integer set read into memory once) and
|
||
`Checked` (what an unpinned decode at a sink with a key log reads, read once) and is
|
||
`#[non_exhaustive]` too; `int_payload::IntParams` is opaque, built by
|
||
`int_payload::resolve` (or `IntParams::none()`).
|
||
- **Checked decoders.** `int_video`, `int_audio` and `int_splat` decode a typed
|
||
stream that the binary and JSON forms both read into. Every size is capped at
|
||
`2^26` elements (a `WIV1` clip at `2^30` bytes and `2^20` frames, its motion-vector
|
||
field at `2^26` elements) the same on every target, every network's shapes are
|
||
planned before anything is allocated, every latent and layer is checked for
|
||
exactness, and a stream that runs out is refused at the first symbol it cannot
|
||
give, so a build with overflow checks and one without accept the same bytes. A
|
||
frame, a clip or a latent grows only as its symbols are decoded, to its declared
|
||
size and no further, so a short stream stops early; `int_video` holds frames as
|
||
RGB8. A stream that carries its symbols is decoded in full, and a few kilobytes can
|
||
code a clip at the cap, so the header bounds the work: a sink reads the declared
|
||
cost before decoding (`int_payload::declared_cost`, `container::declared_size`, the
|
||
C ABI's `wai_int_payload_declared`, `wai-web`'s `intPayloadCost`) and applies its own
|
||
size policy (integer-payloads §7, *Memory and work*). The cost counts the networks'
|
||
multiply-accumulates layer by layer, exactly (`DeclaredCost::macs`, a `u128`;
|
||
`macs` on `intPayloadCost`, a `BigInt`). The reference sink applies one:
|
||
`neural::ModelRegistry::with_cost_limit` refuses a payload whose declared cost
|
||
exceeds the limit before any symbol is decoded, with the new code `cost_over_limit`
|
||
(`int_payload::check_cost`, `neural::DecodeError::OverCostLimit`), and dispatch moves
|
||
on as for a capability the sink cannot decode (§8). `neural::declared_cost` reads the
|
||
cost over the set a registry resolved, pinned or not. The C ABI's
|
||
`wai_int_payload_declared` takes the set's path (or null for the header alone) and
|
||
gives the working bytes and the multiply-accumulates too (`wai_int_declared_t`
|
||
gains `working_bytes`, `macs_lo`, `macs_hi` and `cost_known`); its neural decode
|
||
entry points no longer decode integer video, PCM or splat attributes, which they
|
||
have no output kind for, and refuse them before decoding. A `WIA1`'s latent may be no
|
||
longer than its `t_out` needs (`geometry_mismatch`). The JSON reader
|
||
(`int_video::VideoJson`) caps every size before it pads a motion-vector list, and
|
||
reads a field that does not fit the target's `usize` as absent rather than
|
||
truncating it. In the browser, `decodeWiv1` keeps the decoder's frames
|
||
(`DecodedVideo.frame(i)`) instead of copying the clip into one buffer.
|
||
- **One refusal order.** The extension states the order of a decode's checks
|
||
exactly (§7): the set's members, the set against the payload, every network
|
||
planned with its caps, the shapes, then each stream symbol by symbol, a run-out
|
||
before the value it would give. Networks are planned layer by layer in 64-bit
|
||
arithmetic (`int_hyper_synth::plan`, the audio synthesis's plan), a size past 64
|
||
bits being past the cap, so a 32-bit and a 64-bit sink refuse a malformed network
|
||
with the same code; a planned network's parameter lengths are checked before any
|
||
symbol is decoded, and a convolution's padded side is capped. A set's entropy
|
||
tables obey §2's length rule (3 to 4097 entries). The parser reads a table of one
|
||
per channel table by table. A `WIV1` keyframe's planned size is checked against the
|
||
clip's `h × w` before any of the keyframe's symbols is decoded, so
|
||
`int_payload::declared_cost` and the decode refuse the same bytes with the same
|
||
code; the Rust verifier checks that for every fixture. Fixtures pin each case.
|
||
- **One header for a one-frame clip.** A `WIV1` of one frame codes no motion
|
||
vector, so its `mv_precision` and `block` must be 1 (`field_range` otherwise); the
|
||
converters write them so. The extension's §2 says a payload's hash identifies the
|
||
payload, not its output (unused table entries, say, code the same clip in other
|
||
bytes), and that one output is identified by its decoded digest (a clip's
|
||
`clip_hash`).
|
||
- **Canonical streams.** After its last symbol, each rANS stream of these payloads
|
||
must have been read to its last word and end in the state `2^31`, where an encoder
|
||
starts (`rans::RansDecoder::is_canonical_end`); otherwise the payload is refused
|
||
with the new code `stream_noncanonical`. An unread word or another starting state
|
||
coded the same symbols in other bytes. Every committed stream, the JSON forms'
|
||
included, ends canonically. `int_hyper_synth` gains
|
||
`decode_conditional_checked`; `int_synth` refuses a side of `2^64` or more instead of
|
||
shifting by it.
|
||
- **Parameter sets.** SPEC §5 names the members the integer decoders read:
|
||
`g_s.whm`, `h_s.whm` and `entropy.json` for `wai.neural.int_hyper` and
|
||
`wai.video.int_hyper`. `wai.audio.int_codec` (`model.wam`) and
|
||
`wai.splat.int_codec` (`g_s.whm`) are registered as one file, which widens their
|
||
registrations (SPEC §8). `wai registry export` writes nothing new: no exported field
|
||
records a set's shape. A `WHM1` or `WAM1` network holds nothing after its last layer
|
||
(`IntModel::from_bytes` and `AudioModel::from_bytes` refuse trailing bytes; every
|
||
committed network has none), so one network has one file and one digest.
|
||
- **Receipts.** A `wai.video.int_motion` receipt identifies its `WIV1` payload
|
||
(`VideoReceipt::seal_decode_wiv1`, `verify_reconstruction_wiv1`), and
|
||
`wai_video_meter` seals over it. The entry points that read the JSON form are
|
||
deprecated; on a conforming JSON bitstream their output is unchanged, so every
|
||
receipt sealed over a committed one, and every pinned digest of one, still
|
||
verifies. They now read the JSON through `int_video::VideoJson` and the checked
|
||
decoder, so they refuse JSON the old decoder took: a stream that runs out (it
|
||
decoded on from the exhausted state) or does not end canonically, a table that
|
||
breaks the table rules, `q` outside 1..=65535, an `mv_precision` other than 1 or 2,
|
||
other than one motion-vector list per frame, vectors on the first frame or more
|
||
vectors than blocks, a vector outside `i16`, a zero size, and a size past the caps. jwp-receipts §2.1's object content hash moves to its own
|
||
module and feature (`jwp_hash`), which `receipt_claims` enables;
|
||
`claim_receipt::object_content_hash` re-exports it, and no corpus changed.
|
||
- **JSON readers that refuse.** `IntModel::try_from_json` and
|
||
`CondBitstream::try_from_value` read the conditional codec's debug JSON without
|
||
panicking; `wai-web`'s `decodeIntHyper` and `decodeIntHyperBin` read it through
|
||
them, `CondEntropy::try_from_value` and `decode_conditional_checked`, so an empty
|
||
model or bitstream, or sizes past the caps, are refused. `int_video::VideoJson`
|
||
refuses a frame whose motion vectors are not a list.
|
||
- **Browser.** `wai-web` decodes the four formats (`decodeWiv1`, `decodeWih1`,
|
||
`decodeWia1`, `decodeWis1`); the entry points that read the JSON forms are for
|
||
debugging and conversion.
|
||
- **Conformance.** The payloads sit beside their JSON sources in
|
||
`wai-rs/tests/vectors/`, written by `wai_int_payload_vectors`, with refusal fixtures
|
||
and an index in `int-payload-conformance/`; `int_payload_verify.py` checks them
|
||
independently in Python's standard library and decodes the `WIV1` keyframe-0
|
||
payloads in full; `byte-exact-conformance` parses and decodes the `WIV1` and `WIH1`
|
||
payloads on every leg of its matrix. CI runs the new tests in both build profiles,
|
||
the generator in the corpus loop, the verifier, a `cargo check` of each integer
|
||
feature alone, and a `wasm32` check.
|
||
|
||
### The registry as one table, candidate capabilities, the codec glossary under test, and the public text retrofit
|
||
|
||
- **Candidate capabilities** (SPEC §5): a registered string whose payload format no
|
||
revision has pinned — `wai.tensor.nnc` and `wai.splat.sog` — MUST NOT be emitted, and a
|
||
sink treats it as unsupported. This is a MINOR rule: it enforces the candidate
|
||
registrations as they were made (§8). Envelopes it makes non-conforming: a `WAI1` naming
|
||
a candidate as its `capability` or `fallback`, and a `WAI2` with a component of a
|
||
candidate; they still parse. `codecs::is_candidate`, `Wai::check_candidates` and
|
||
`WaiMulti::check_candidates` report them. `Wai::to_emit_bytes` and
|
||
`WaiMulti::to_emit_bytes` check, then serialise: the C packers `wai_envelope_pack` and
|
||
`wai_envelope_pack_multi` emit through them, the browser writers `pack` and `packMulti`
|
||
refuse the same envelopes (`container::check_candidates_v1`,
|
||
`check_component_candidates`), and `wai wrap` refuses a candidate before any other rule,
|
||
in every place including a `WAI2` companion. A candidate's licence class is provisional:
|
||
`wai.splat.sog` is classified patent-pool, conservatively (it was royalty-free, never
|
||
established). `wai.splat.sog` no longer claims an MPEG attribution: no MPEG page or
|
||
output document read names the format it was registered for ([research
|
||
registry](research/media-codec-registry.md), *MPEG's Gaussian-splat and point-cloud
|
||
work*).
|
||
- **One registry table** (`wai-rs/src/codecs/registry.rs`, no feature): every registered
|
||
string with its **kind** (`payload`, `companion`, `derivation`, `record`), its
|
||
**status** (`registered` or `candidate`; `RegistrationStatus`), its **media** class,
|
||
the SPEC §5 heading that registers it, the edition its registration names and the date
|
||
it was registered; the named identifiers that are not capabilities (`wai.det.fixed64`,
|
||
`wai.world.sim.*`); and the receipt labels no revision registers
|
||
(`wai.quantum.{mitigate,decode,compile,rearrange,qbom}`). SPEC §5 states the kinds:
|
||
records and derivations name no payload format. Every payload and companion
|
||
capability carries a licence class (records do not).
|
||
- **Dispatchable** (SPEC §4, normative): steps 3 and 5 take only a dispatchable
|
||
capability, and a capability is dispatchable unless this revision registers it as a
|
||
candidate, a companion-only capability, a record or a derivation. A string this
|
||
revision does not register stays dispatchable — a later MINOR revision may register
|
||
it (§8) — and is decoded only where the sink advertises it. `codecs::is_dispatchable`
|
||
implements it and is the one predicate every dispatch and selection function of the
|
||
reference implementation uses (`Wai::dispatch_verified`, formerly
|
||
`dispatch_capability`; `WaiMulti::pick`; `WaiMulti::primary`;
|
||
`RenditionMeta::is_selectable`; the CLI's `inspect`; and, below, the neural sink, the
|
||
C ABI and wai-web); §7.1's selection sentence says the same. A later dispatch or
|
||
selection function uses it too.
|
||
- **`wai registry export`** writes `registry/capabilities.json`, its JSON Schema
|
||
(`registry/capabilities.schema.json`), the generated table of
|
||
[`REGISTRY.md`](REGISTRY.md) and **vocab v2** (`vocab/v2.{ttl,jsonld,shapes.ttl}`,
|
||
namespace `https://wai.transaction.science/vocab/v2#`, one IRI per registered string,
|
||
enumeration individuals named `<class prefix>-<label>` so no two terms share an IRI,
|
||
the registration date as `registeredOn`; its JSON-LD context maps a `WAI1` or `WAI2`
|
||
manifest as written, with `kind` and a component's `role` under terms of their own;
|
||
its SHACL shapes apply to a manifest with no `@type` and to every `WAI2` component,
|
||
check the capabilities against the registry, and leave `media` open, as SPEC §3 and §8
|
||
require). `--check` fails on drift; a library test and a CI step run it, and
|
||
`vocab/check_shapes.py` validates typed and untyped sample manifests in CI. vocab v1
|
||
is stable: no IRI is removed and no term changes meaning. REGISTRY.md states the
|
||
registration procedure; CONTRIBUTING.md is rewritten around it.
|
||
- **The `wai` binary builds without the `codecs` feature** (encode and decode are
|
||
compiled out); `tests/cli_wrap_intent.rs` and the new `tests/cli_registry.rs` run in
|
||
CI. `wai wrap` now writes the registry's media class: `volumetric` for `wai.mesh.*`,
|
||
`wai.pc.*` and `wai.splat.*` (was `model`), `image` for the learned image codecs such
|
||
as `wai.neural.bmshj2018` (was `binary`). SPEC §3 lists the media values, informational.
|
||
Without `codecs`, `inspect`, `capabilities` and `unwrap` claim no native decode.
|
||
- **Editions** (SPEC §8): a registration that names no edition carries the edition of
|
||
its standard current on the date it was registered, and no later edition or
|
||
amendment, which takes a new string. `wai.pc.gpcc` and `wai.pc.vpcc` therefore do not
|
||
carry the Gaussian-splat amendments of ISO/IEC 23090-9 and 23090-5; Enhanced G-PCC
|
||
(ISO/IEC 23090-38) is not a `wai.pc.gpcc` payload. Which edition of each part was
|
||
current on their registration date is unresolved, and what they admit beyond the
|
||
part's first edition is unspecified until a revision records it.
|
||
- SPEC: `wai.world.replay`, `wai.world.live`, `wai.semantic.clip` and
|
||
`wai.semantic.perco2` are spelled out or given rows, and a test holds SPEC and the
|
||
registry to the same strings in both directions; the opening, §1 and the floor
|
||
paragraph agree with §5; citations: PNG (W3C Recommendation, Third Edition), FLAC
|
||
(RFC 9639), Zstandard (RFC 8878), AVIF (the AOM AVIF specification, HEIF-based,
|
||
MIAF-conforming), JPEG AI (ISO/IEC 6048-1:2025); the neural tables state each model's
|
||
published operating point; Appendices B and C are rewritten; confidentiality's
|
||
implementation is a companion implementation under its own licence.
|
||
- **The codec glossary** links each registered capability that has an entry, with the
|
||
registry's licence and replicate classes, through a reviewed table
|
||
(`GLOSSARY_LINKS`); every other payload and companion capability is listed with a
|
||
reason (`GLOSSARY_NO_ENTRY`). `org` became `body`, from a closed allowlist of
|
||
standards bodies, designations and open-source projects; new modality values `text`,
|
||
`volumetric`, `haptic`, `metadata`; new entries for the registered formats that had
|
||
none; descriptions scrubbed of company names and comparisons; entries titled by the
|
||
format's own name, FourCC, file extension or designation, never by a company, a
|
||
product or a person (the research codecs by their method); HEVC regraded `success`;
|
||
JPEG AI dated 2025; AVS3 video's body is the designation the registry pins. The file
|
||
now carries `"schema_version": 2`: a reader of the earlier form, which had `org` and no
|
||
version, finds no `org` field in version 2.
|
||
`glossary_links_agree_with_the_registry` holds it to its schema and the registry.
|
||
- **quantum-ops vectors regenerated with neutral ids** (`example:lab3-device`,
|
||
`example:qpu-a`) through `wai_quantum_ops_vectors gen`. Changed receipt hashes:
|
||
`cal_q3` `7c53298929a1261959315c91f9540781b4bff0669b5f4b783becee93dbf5f8fa`, `cal_cz`
|
||
`4c6374ee05c956f928455f86326357f10d08f3466e88d569a6ef49975001f526`, `job_bell`
|
||
`08b142d29dadc466b294b89d2127f39ee9fa1aa0b6b5a4c6c5d226baf343b9e3`. The wai-quantum
|
||
calibration pin (`receipt_pins::calibration_legacy_bytes`) was recomputed by the code
|
||
before the acquisition class, with the neutral id. wai-quantum's version is unchanged.
|
||
- **CI**: the feature-gated test targets the conformance workflow names are declared
|
||
with `required-features`, so dropping a feature stops cargo instead of running nothing;
|
||
`tests/int_audio.rs` and `tests/int_splat.rs` now run; the registry export is checked;
|
||
the glossary, SPEC, registry and vocab paths fire the conformance workflow. wai-web is
|
||
built for wasm32 (`wai-web-wasm.yml`, and the int_hyper vectors in
|
||
`wai-sota-vectors.yml`), and its verify scripts fail on a missing artifact when
|
||
`WAI_REQUIRE_SOTA_VECTORS` is set or a directory is named. The Windows x86_64 legs are
|
||
out of the conformance and byte-equality matrices while their runner is offline (each
|
||
matrix says how to restore it). The nested `wai/.github/workflows/ci.yml`, which no
|
||
forge reads, is removed.
|
||
- **Public text**: two checks, `scripts/check-comparative-prose.sh` (a public phrase
|
||
list) and `scripts/check-public-names.sh` (patterns kept private), now run in CI over
|
||
`wai/`'s text, code, data, markup and ignore files. Both fail closed (a grep error or
|
||
a pattern that does not compile exits 2), an opt-out needs a reason, and
|
||
`scripts/test-public-text-checks.sh` proves both on every run. Without the private
|
||
list a push to main fails. The list is given only to the run on a push to main, never to
|
||
a pull request's code; a pull request shows a failed step saying the name check did not
|
||
run. The specification, extensions, research notes, READMEs, CONTRIBUTING, the
|
||
glossary and source comments name no company, vendor or product and make no
|
||
comparisons; functional identifiers (capability strings, model-hub ids, OS API names,
|
||
advisory ids) stay, marked. Dated research records keep their findings: corrections are
|
||
dated annotations (the MoQ Transport revision, the MPEG splat stages, WebTransport's
|
||
status, the October 2026 call for content); names are redacted, and comparative
|
||
framing is restated, in place. In this changelog's history, names are redacted and
|
||
nothing else changes.
|
||
- For later changes: a new dispatch or selection path uses `codecs::is_dispatchable`;
|
||
a newly registered string (for example a prior set) goes into the registry table,
|
||
`wai registry export` regenerates the exports and vocab v2 (vocab v1 is stable and only
|
||
gains terms), and the string is listed in the glossary's `GLOSSARY_NO_ENTRY` or linked.
|
||
The quantum-ops corpus and the wai-quantum calibration pin have the values above; the
|
||
pwsh steps of the conformance workflow remain, inactive.
|
||
- [`extensions/jwp-receipts.md`](extensions/jwp-receipts.md) §5: the MUST is over the
|
||
cases §5.1–§5.5 define; verifying against their vectors becomes a MUST once the
|
||
vectors are published in the tree.
|
||
|
||
### The parameter-set pin, specified; component identity; pin-aware selection
|
||
|
||
- **SPEC §3.1** defines the pin a manifest carries (`conditioning.prior`; a `WAI2`
|
||
rendition's `prior`): `id`, `sha256`, `determinism`, and the optional `form`,
|
||
`tier` and `supersedes`. The digest covers the whole parameter set: a form-less
|
||
pin is admissible only for a capability registered with a one-file set (§5
|
||
registers each capability's shape), and a set of several files is pinned through
|
||
its listing, the new registered prior form `wai.prior.set` (`StandardDefined`,
|
||
royalty-free, the one form listed as established exact). A sink verifies the pin
|
||
before it decodes, reports a refusal with one of an ordered set of snake_case
|
||
reasons (`pin_malformed`, `form_not_registered`, `form_unreadable`,
|
||
`form_required`, `prior_revoked` / `prior_not_authorised` (check 5, run by a sink
|
||
that holds a key log; see *Key discovery, signing authority and module revocation*
|
||
below), `prior_not_held` / `digest_superseded`, `set_malformed`, `part_mismatch`;
|
||
`set_incomplete` during a decode), and decodes only from the set it verified. A pin over a capability this
|
||
revision does not register is verified as several files. `null` in an optional
|
||
member is the same as absent.
|
||
- **Tier** is a closed vocabulary (`decode-equivalence`, `entropy-consistency`,
|
||
`none`): an encoder writes a registered token, a sink reads any other as `none`,
|
||
a verifier reports it (`tier_unregistered`). Pins without `tier` keep the legacy
|
||
reading of `determinism`, now stated normatively, so every existing verdict —
|
||
`tests/vectors/int_mlicpp/stream.wai`'s included — is unchanged.
|
||
- **`supersedes` is informative**: it changes only the reason a sink that holds the
|
||
predecessor reports, a malformed one is a static finding the sink ignores, and a
|
||
successor never satisfies its predecessor's pin.
|
||
- **§4 and §7.1**: a pin that does not resolve makes its capability unsupported for
|
||
that envelope; dispatch continues at the fallback or the next rendition, and an
|
||
inert envelope's error names the reasons: for `WAI1`, the reason kept for the
|
||
capability before the fallback's, `set_incomplete` included; for `WAI2`, every
|
||
entry's (`not_selectable`, `missing_capability`, `component_digest_mismatch`, a
|
||
pin reason, or, under a policy that serves only sound `replicate` claims, the
|
||
entry's `replicate_*` reason). A `WAI1` pin applies to whichever capability
|
||
decodes the payload.
|
||
- **Component identity (§7.1)**: a rendition entry MAY carry `id` (unique, never a
|
||
capability string), `sha256` (its payload digest, checked before the entry is
|
||
decoded or applied: `component_digest_mismatch`) and, on a companion,
|
||
`companion_of_id`, which binds it to one entry — an enhancement layer can now
|
||
bind to one of two renditions of a base capability. `id`, `sha256` and `prior`
|
||
are one construct; no extension defines another per-component digest.
|
||
- **§7**: `replicate` over a `NeuralIntegerExact` capability MUST carry a pin that
|
||
declares `decode-equivalence`, in no form or in `wai.prior.set`. The new
|
||
*Digests* subsection names SHA-256 for parameter-set identity, for component
|
||
identity, and for the decoded output of capabilities that decode against a
|
||
parameter set and of standard codecs in conformance material; FNV-1a and BLAKE3
|
||
stay as diagnostics. The vectors of such capabilities gain `sha256.json`
|
||
sidecars; no existing vector file or value changes.
|
||
- **§8** settles how an optional member is added under the 1.x freeze, which the
|
||
`form` entry below left open: a MINOR revision MAY define an OPTIONAL member,
|
||
envelopes keep `wai` `"1.0"`/`"1.1"`, a reader MUST ignore a member it does not
|
||
understand whatever the MINOR, and an earlier reader may instead reject such an
|
||
envelope (fails closed) or ignore the pin (fails open), which §3.1 states. §8
|
||
states separately the one exception this revision makes to the freeze (next
|
||
entry).
|
||
- **Envelopes made non-conforming:** the list in SPEC §3.1. None is committed.
|
||
One kind stops parsing, the one exception to the `1.x` freeze that §8 states: a
|
||
manifest that carries a member this revision defines (a pin or pin member, or a
|
||
rendition entry's `id`, `sha256` or `companion_of_id`) with another JSON type is
|
||
malformed, because reading a mistyped `form` as absent would turn a set-form pin
|
||
into a form-less one. The envelope's `wai` value does not signal it.
|
||
New vectors: `tests/vectors/int_mlicpp/{model.sha256, stream_set.wai}` (the
|
||
set-form twin, written by `tools/wai_int_mlicpp_set_form.py`) and the
|
||
`pin-conformance/` corpus, with an independent verifier in Python.
|
||
- **Reference-sink behaviour:** a form-less pin over a several-file capability is
|
||
refused (`form_required`), where before it verified one file or failed on a
|
||
directory; `wai.neural.int_synth`'s pin covers `int_synth.json`; a digest that is
|
||
not 64 lowercase hex is refused, where before it was compared case-insensitively;
|
||
a refused pin falls through to a fallback the registry holds; every decoder reads
|
||
parameters through `neural::ParamSource`, so a pinned decode reads only verified
|
||
bytes (ONNX sessions built from them), and an ONNX model that keeps a tensor's
|
||
data in another file, which the runtime would read from outside those bytes, is
|
||
refused with `set_incomplete` naming that file, as §3.1 now states; `bmshj2018`'s CDF cache is keyed by content
|
||
digest and holds a bounded number of tables, least recently used evicted.
|
||
`decode_envelope_strict` applies §7's rule to the capability it decodes through,
|
||
so a sound fallback behind an unsound primary the sink cannot use still decodes;
|
||
it refuses before reading a file only when every capability it could decode
|
||
through (dispatchable and advertised) is refused, so a candidate the registry
|
||
happens to hold does not change its reason. A `WAI2` parse sums the rendition lengths in 64 bits, so a
|
||
table whose lengths wrap a 32-bit sum is refused as truncated on 32-bit targets
|
||
too, where it used to panic. `wai inspect` prints digests through
|
||
`pin::digest_preview`, which shows a malformed one whole instead of cutting it
|
||
inside a character.
|
||
- **Rust API, source-breaking** (the same 0.4.0 release as `form`): the manifest
|
||
types (`Manifest`, `ModelRequirement`, `Conditioning`, `Prior`, `Supersedes`,
|
||
`ManifestV2`, `RenditionMeta`, `Rendition`, `Security`) are now
|
||
`#[non_exhaustive]`, with constructors and `with_*` builders that write the same
|
||
JSON a literal wrote, so the next optional member does not break callers again;
|
||
every manifest object keeps members it does not model (`extra`), and `Manifest`
|
||
gains `security`, so a Rust round trip loses nothing. `Rendition` holds a
|
||
`RenditionMeta`. `Wai::dispatch_capability` is replaced by `dispatch_verified`;
|
||
`WaiMulti::pick`/`primary` take a pin verifier and return `Picked` (index, entry,
|
||
payload, verified set, skipped entries); `companions_of_index` binds by id.
|
||
`DecodeError::PriorMismatch` carries a `PinRefusal`, and `DecodeError` gains
|
||
`NoRendition`; `ModelRegistry` gains `held`/`hold`/`verify`/`resolve_verified`
|
||
(`resolve_checked` is deprecated); new `decode_payload`, `decode_multi`,
|
||
`decode_multi_strict`. `sha2` is now a plain dependency, and the new `pin` module
|
||
needs no feature.
|
||
- **C ABI:** a codec-free `ffi` feature carries the envelope surface (`codecs`
|
||
implies it); `WAI_ERR_PRIOR` (5), `WAI_ERR_UNSOUND_REPLICATE` (6) and
|
||
`WAI_ERR_NO_RENDITION` (7), with the reason first in `wai_last_error`; the unpack
|
||
calls return the original manifest bytes, as `wai.h` already documented; the
|
||
`WAI2` table carries identity, pin and `selectable`; new `wai_envelope_select`
|
||
and `wai_neural_decode_ex` (`WAI1` or `WAI2`, a registry of held sets, a strict
|
||
flag), which writes `*out` on every call, empty on failure, so `out->data` is
|
||
always safe to free. The envelope packers build under `ffi` too, and
|
||
`tests/ffi_envelope.rs` checks in CI that they refuse a candidate and that
|
||
`wai_envelope_select` never picks one.
|
||
- **wai-web** wraps `wai::container` instead of reduced copies, so `role`,
|
||
`companion_of`, identity and pins reach JS, and exposes `pick`, `companionsOf`,
|
||
`verifyPin` and `sha256Hex`; a component whose capability is not dispatchable
|
||
(SPEC §4) is never presented as media, and `rendition(i)` and `companion(i)` refuse an entry whose payload does not
|
||
match its `sha256`. Its JS-facing tests run under wasm32 in CI.
|
||
- **One dispatch predicate:** `dispatch_verified`, `pick`, `primary`,
|
||
`RenditionMeta::is_selectable`, `decode_multi`, `decode_envelope_strict`'s early
|
||
refusal, `wai_envelope_select`, wai-web and `wai inspect` all take a capability only when `codecs::is_dispatchable`
|
||
(above) accepts it, so a candidate, a record or a derivation is never dispatched
|
||
or picked, whatever its pin. An unregistered string stays dispatchable where the
|
||
sink advertises it, and a pin on it is verified as several files, so it fails
|
||
closed. SPEC §7.1 step 2 reads "dispatchable (§4)". `wai.prior.set` has its row in
|
||
the registry table, so `registry/capabilities.json`, `REGISTRY.md` and vocab v2
|
||
list it.
|
||
- **Vocabulary:** `vocab/v1` gains the pin terms additively, and `wai registry
|
||
export` writes the same terms and shapes into v2, with the tier list taken from
|
||
`codecs::TIER_TOKENS`; `vocab/check_shapes.py` validates conforming and
|
||
non-conforming pins against the v2 shapes. `sharedPriorUri` and
|
||
`sharedPriorHash` are annotated as not being the manifest pin. The SHACL pin shape
|
||
targets the objects of `wai:prior` (no class is asserted for them), and
|
||
`supersedes` is checked by its own shape: a required digest and an optional form.
|
||
- **Open item:** `wai.world.model` names its renderer's model by `model_id`, a
|
||
second model-identity construct; it is to fold into the pin once that renderer
|
||
is registered as decoding against a parameter set.
|
||
|
||
### Energy reports, labelled object leaves, and what the signatures cover
|
||
|
||
- **Reports about a figure.** Two claim classes, [`extensions/jwp-receipts.md`](extensions/jwp-receipts.md)
|
||
§2.4.8–§2.4.9, report on one energy figure without changing the receipt that seals
|
||
it. A claim names that receipt by a **figure profile** — the receipt's identity domain
|
||
less its version byte, so the registry is the code's own
|
||
([`extensions/energy-measurement.md`](extensions/energy-measurement.md) §6.1) — and its
|
||
identity, and restates the figure with its class. `measurement` carries the bracket's
|
||
values as integers with each bracket's liveness facts, the build (profile, toolchain,
|
||
target) and the power reader; only the key that signed the figure may sign it, so a
|
||
figure no Ed25519 key signs (an energy binding's) has no `measurement` claim. A
|
||
hardware figure's report must name its power reader (`measurement_no_power_reader`),
|
||
with or without a bracket, and a `null` bracket says the figure was not obtained under
|
||
the bracketed protocol. Because only §3.1 step 6 shows the signer is the figure's, a
|
||
`measurement` claim is accepted only against the receipt that carries its figure:
|
||
checked without it, it is `figure_not_held`, and `ClaimReceipt::attribution` gives a
|
||
verdict only for a claim accepted against that figure.
|
||
`operating-point` records the requested, committed and delivered operating points,
|
||
from a vocabulary named by URI, and a prediction. energy-measurement §6 makes the
|
||
`measurement` claim the carrier of a class-bearing figure's report and lists the
|
||
producers that fall short.
|
||
- **Exact report arithmetic.** energy-measurement §6.2 gives the figure and the least
|
||
declaration a report's integers support as exact integer formulas, at whatever width
|
||
their products need. Producers seal from the same integers; computed from unrounded
|
||
samples, a declaration could come out one ppm below what the published integers
|
||
support. `wai-rs` `energy_report` implements it with no floating-point arithmetic, and
|
||
`claim-conformance/claim_verify.py` recomputes the pinned brackets with Python's
|
||
integers.
|
||
- **Derived, never stated.** The §4 ratio and verdict, whether a commitment was met,
|
||
and a prediction's residual are derived by every reader from what a claim carries. A
|
||
report carrying its ratio is refused (`unknown_member`).
|
||
- **A live power reader.** energy-measurement §3.1: a rail is live in a bracket only if
|
||
it advanced in two consecutive intervals, in at least five and in at least four of
|
||
every five, over at least 300 ms, in both brackets; a figure from a bracket that is not
|
||
live is never sealed under a hardware class. A power sample advances only when it is
|
||
above zero and differs from the sample before it, so a reader that holds its last
|
||
value never advances; a reader of a report refuses, under a hardware class, a bracket
|
||
whose `sd_uw` is 0 whatever liveness facts it carries. A frozen rail, a stale value
|
||
held, one batch refresh, and one refresh published in two steps all used to give a
|
||
flat idle bracket and an infinite ratio that sealed as `OnChipCounter`; `energy_burst`
|
||
and `energy_meter` now refuse them, with fault-injection tests, and a counter meter reads its counter during a span
|
||
rather than only at its ends. On a host whose reader reports a rail that does not
|
||
advance, the reference meters seal unmetered and say why.
|
||
- **The unmetered marker.** energy-measurement §2: an unmetered figure is not a figure
|
||
of zero joules. A `0` that marks unmetered is never presented, totalled or compared as
|
||
0 J; a total over parts that include one covers only the measured parts; new forms
|
||
mark unmetered with class tag `0`. quantum-sim, provenance, interactive-worlds,
|
||
moq-streaming-format and SPEC read `0` this way. `ModelMeter` returns no reading rather
|
||
than a labelled zero or a saturated product, and `wai-quantum-serve`'s `POST /verify`
|
||
renders an unmetered receipt as `"joules_micro": null, "metered": false`.
|
||
- **The labelled JWP leaf.** An object's class rides in its Merkle leaf,
|
||
`BLAKE3("moq-jwp:merkle-leaf\x02" ‖ content_hash ‖ u64_be(joules_micro) ‖ class ‖
|
||
u32_be(len(origin)) ‖ origin)`, so the group signature covers the object's figure, its
|
||
class with its evidence, and its origin (jwp-receipts §2.2, §2.4.2). A label, or the
|
||
origin, can be neither added, stripped nor altered in transit.
|
||
- **The labelled group payload.** `moq-jwp:group-sign\x02` signs the legacy fields and
|
||
then the total's class, its coverage and the parent link (§2.3, §2.4.1); the legacy
|
||
payload never signed the parent. The class is composed from the objects' classes by an
|
||
integer rule — the weakest class, and for a hardware class `⌈Σ F·p / Σ F⌉` ppm over the
|
||
longest window — and a verifier holding every object checks it (§3, *The group
|
||
total*), alongside the sum.
|
||
- **Position.** An object must sit at the position its inclusion proof walks (§3 step
|
||
5, `proof_position_mismatch`), which closes the duplicated last leaf of an odd level
|
||
and an object presented at another object's index — the latter could hide a weaker
|
||
class from the group total. Group signatures are held to the strict Ed25519 rule
|
||
claims use. §3.3 lists the delivery reasons; a group's link hash is defined over its
|
||
signing payload and signature.
|
||
- **`WAI_RECEIPT`** ([`extensions/moq-streaming-format.md`](extensions/moq-streaming-format.md)
|
||
§5): `0` is the unmetered marker, `group_receipt` is the group's link hash, and the
|
||
property's class reads as signed only where it equals the object's leaf label or an
|
||
accepted `energy` claim; otherwise it is discarded (`property_disagrees`) or read as
|
||
unlabelled. A class read as signed names the key that signed it — the group's for a
|
||
leaf, the claim's for a claim — so a class a relay signs into the property under its
|
||
own key is never read as the publisher's.
|
||
- **What stays unverifiable.** energy-measurement §5 and energy-binding §3 now say it is
|
||
the measurement — the joules and every field describing how they were obtained — not
|
||
one number, and that a report's values can be checked for consistency, which is not
|
||
truth. energy-binding §7.4 no longer says green metadata is registered; it is not yet.
|
||
- **Implementation.** `wai-rs` (feature `receipt_claims`): `jwp_delivery` reads and
|
||
verifies both forms of each JWP receipt, the group total and the property;
|
||
`energy_figure` holds the profile registry and views every figure-bearing receipt
|
||
family, the quantum receipts through `MaybeLabelled`; the reference meters write a
|
||
measurement report beside the receipt by default (`--no-report` to opt out;
|
||
`--previous-report` with no file is a usage error), and `wai_prov` presents its total as
|
||
`Partial` over the measured steps, or `UNMETERED`, never as 0 µJ. joule-code's
|
||
`joule-moq` (0.2.0) builds and verifies both labelled forms, checks position, holds
|
||
publisher keys and group signatures to the strict rule (a non-canonical key encoding is
|
||
refused as a key), refuses an unsigned coverage on a legacy group, and runs every
|
||
leaf, group and object vector of the new `jwp-conformance/` corpus.
|
||
- **Conformance and CI.** `claim-conformance/` gains vectors for every new reason, the
|
||
arithmetic at its boundaries, liveness faults (a held value among them) and the
|
||
labelled leaf; the earlier vectors are unchanged and pinned. `jwp-conformance/` holds the JWP corpus
|
||
(`wai_jwp_vectors`) and `jwp_verify.py`, an independent verifier. CI now runs
|
||
`claim_verify.py`, `differential.py` (fixed seed) and `jwp_verify.py` on the linux
|
||
runner, and the JWP corpus on every platform of the conformance matrix. The
|
||
conformance build includes `receipt_claims`, so `wai_meter` and `wai_world_meter`
|
||
are built on every platform of the conformance matrix; `wai-energy-receipts.yml`
|
||
builds those two, `wai_video_meter` and `wai_quantum_meter` on linux, linux-arm64
|
||
and macos; no workflow builds `wai_prov`, which needs `codecs`. joule-code's CI runs
|
||
`joule-moq` with its QUIC transport.
|
||
- **Build.** Every reference meter bin (`wai_meter`, `wai_world_meter`,
|
||
`wai_video_meter`, `wai_quantum_meter`, `wai_prov`) now also requires the
|
||
`receipt_claims` feature, because it writes a measurement report. Cargo skips a bin
|
||
whose required features are off without an error, so a build that names only the
|
||
older features (`--features video_receipt`, say) no longer produces that meter; add
|
||
`receipt_claims`.
|
||
|
||
**Wire.** Every receipt issued in a legacy form keeps its bytes and verifies as before:
|
||
v1 groups, `\x01` leaves, every claim and corpus vector already pinned, `wai-quantum`'s
|
||
receipts (no `wai-quantum` code changes). The new forms are refused by software that
|
||
predates them: a verifier or relay refuses a labelled group at §3 step 2 and a labelled
|
||
object at §3 step 5, and a claim verifier refuses `measurement` and `operating-point` as
|
||
`unknown_class`. A group's `sign_version` other than 2 is `group_label_invalid`.
|
||
**External JWP implementations must adopt the `\x02` leaf and the labelled group payload
|
||
before verifying objects or groups that use them, and hold publisher keys to the
|
||
canonical-encoding rule**; the in-tree `joule-moq` does, and its public API changes are
|
||
listed in joule-code's CHANGELOG. `wai-quantum-serve` (0.4.0) changes the `POST /verify`
|
||
response: an unmetered receipt renders as `"joules_micro": null, "metered": false`
|
||
where it rendered its marker as the integer `0`, and every other receipt adds
|
||
`"metered": true`; a client that reads `joules_micro` as an integer must accept
|
||
`null`. New meter receipts can declare one ppm more than before, because they
|
||
seal from the published integers, and a meter now refuses a rail that is not live where
|
||
it used to seal a figure.
|
||
|
||
### Key discovery, signing authority and module revocation
|
||
|
||
- [`extensions/jwp-receipts.md`](extensions/jwp-receipts.md) §7: a signer can say
|
||
where its keys are published, and a verifier can confirm it. A `signer_id` of the
|
||
form `did:web:<host>[%3A<port>]#<key-id>` names a web origin, which publishes at
|
||
`/.well-known/wai-keys` a **key log**: revisions signed by a root key that
|
||
pre-commits to its successor, each listing the origin's keys, the authorities each
|
||
holds (`delivery`, `energy/<acquisition class>`, `lawful-basis/declaration` or
|
||
`/decision`, `log-inclusion`, `soft-binding`, `binding-recovery`, `measurement`,
|
||
`operating-point`, `receipt`, `module/decoder`, `module/reconstruct`) and whether it
|
||
is active, retired or revoked. The path is published now; its registration in the IANA Well-Known URIs
|
||
registry is to be requested. A `did.json` on the same origin SHOULD list exactly
|
||
the log's active keys, and the log governs.
|
||
- A verifier that has anchored the origin — pinned out of band, or fetched from the
|
||
origin over HTTPS and recorded as such — confirms an accepted receipt, claim or
|
||
module grant against the log, offline, and reports `confirmed`, `refused` or
|
||
`unconfirmed` with a named reason beside the item's own verdict, never folded into
|
||
it. An item needs every authority the item fixes: a group receipt needs `delivery`
|
||
and `energy/<class>` for each class its objects' labelled leaves and its labelled
|
||
total carry, another family's receipt `receipt` and `energy/<class>` for each class
|
||
its figures carry, a claim its class's authority, and an energy authority is read from any
|
||
`energy_provenance` a claim's body carries, whatever its class — so a `measurement`
|
||
or `operating-point` claim needs its class's authority and the energy authority of
|
||
the figure it restates, pinned-only for a hardware class. An Ed25519 `did:key`
|
||
names one key, and one naming a key other than the signer's is refused; a `did:key`
|
||
longer than any Ed25519 key's spelling is a label, refused before it is decoded. The head walk
|
||
ignores revisions that are not held, unattached, or signed by a key their parent
|
||
does not authorise; the owner's rotation supersedes a stolen root's revisions; an
|
||
older log retrieved after a newer one was anchored is reported as rolled back.
|
||
- Pinned-only authorities: the hardware-class energy authorities and the
|
||
lawful-basis authorities, and any authority registered for pre-emption, are
|
||
confirmed only under a pinned anchor that has not moved (`anchor_not_pinned`);
|
||
every confirmation reports whether its anchor was pinned or fetched. A pinned record
|
||
moved forward by a later retrieval stays pinned and keeps the revision that was
|
||
pinned, and a pre-emption authority is read from that revision only. Beside an item
|
||
that needs a hardware-class authority, a verifier reports whether its key also
|
||
holds `delivery` (`also_delivers`); the recommendation that it not is a SHOULD.
|
||
- Revocation refuses every item under a revoked key, whatever time the item carries.
|
||
A retired key's untimed items stay confirmable until the key is revoked, and are
|
||
reported as untimed.
|
||
- §7.9, decoder and reconstruct modules: a module, or a parameter set an envelope
|
||
pins, is identified by the SHA-256 of its bytes and authorised by a signed **module
|
||
grant** from a key holding `module/<form>`, for named capabilities and a bounded
|
||
window. A grant is held only from one of the sink's **module origins** — an origin
|
||
its relying party names, or the origin under which it confirmed the envelope's
|
||
delivery — so anchoring an origin for its receipts does not make it a source of
|
||
modules. The key log revokes modules by digest (`revoked_modules`, append-only), and
|
||
a digest revoked in the head of any origin the sink has anchored is not held,
|
||
whichever origin grants it. Each log declares `grant_max_s` and `status_max_age_s`
|
||
under normative ceilings; a signed **key status** from the head's root, re-issued on
|
||
each heartbeat and held only if it names the head or an ancestor of it, keeps a sink
|
||
current. Eight ordered checks decide whether a sink runs a module; every failure —
|
||
including no network, a stale status and no trusted clock — means the module is not
|
||
held, and dispatch falls back: a sink never reports a file inert because of module
|
||
authorisation while a fallback remains, and an encoder whose capability needs a
|
||
module declares one. The kill latency `grant_max_s + status_max_age_s` is a
|
||
normative parameter. The ceilings are 86400 s for `grant_max_s` and 3600 s for
|
||
`status_max_age_s`.
|
||
- Module revocation through the parameter-set pin. [`SPEC.md`](SPEC.md) §4 folds the
|
||
hook into its steps: a sink that runs a module obtained apart from an envelope holds
|
||
it under §7.9, or the capability is not advertised for that envelope. SPEC §3.1's
|
||
check 5, reserved until now, runs at a sink that holds a key log, keyed on the pin's
|
||
`form` and `sha256`, before check 6. It has two parts. Revocation runs for every
|
||
capability a pin applies to and needs no grant, status or clock: a set whose digest
|
||
the head the sink reaches from any anchor it holds revokes is refused with
|
||
`prior_revoked`, whichever check a grant would fail first. Authorisation runs where
|
||
the relying party holds the capability's pinned sets to §7.9, its choice per
|
||
capability: §7.9 authorises the set as a decoder module, and any other failure,
|
||
offline and no grant included, is `prior_not_authorised`. Dispatch moves to the
|
||
fallback as for any refused pin. An envelope that pins no set does not escape check
|
||
5: the set an unpinned decode reads is checked under each digest a pin over it could
|
||
name. A sink configured with no key log does not run check 5.
|
||
`key_log::module_check` implements §7.9's checks; `key_log::SinkKeyLog` runs check 5
|
||
in the reference dispatcher, given to `pin::verify_with` or to
|
||
`neural::ModelRegistry::with_key_log`, which also runs it before an unpinned decode.
|
||
Library tests in CI dispatch a revoked set to its fallback, report `prior_revoked`
|
||
when none remains, refuse a revoked set whose grant fails an earlier check, whose
|
||
sink holds no grant, or whose capability is not held to §7.9, and fall back when the
|
||
sink is offline; the neural sink gates run the same refusals through
|
||
`neural::decode_envelope` over the committed int_mlicpp set, pinned and unpinned. A
|
||
key log that names the subject's origin holds for one envelope, so a registry given
|
||
one decodes that envelope only. The reference dispatcher runs no module obtained
|
||
apart from an envelope; `wai-web` and the C ABI take no key log.
|
||
`neural::ModelRegistry` gains a private field, so a struct literal no longer builds
|
||
one; build it with `ModelRegistry::new()` and its builder methods.
|
||
- `wai-rs`: `signer_id` (ungated, no dependency) reads a `signer_id`;
|
||
`key_log` (feature `key_discovery`, no new dependency, checked to build for wasm32)
|
||
implements §7. Outside the crate, an item to confirm is read from bytes that
|
||
verified: `Item::from_claim` from an accepted claim, `Item::from_group` from a group
|
||
receipt and the objects included under it, `Item::from_family` from another
|
||
family's receipt through its figure view, given the `signer_id` and signed time the
|
||
same receipt carries. `wai-keys` builds an origin's key log from seeds the operator names,
|
||
appends retirements, revocations, revoked modules and rotations, signs statuses and
|
||
module grants, verifies a log as §7.3 anchors one — afresh, or as a later retrieval
|
||
against a fetched or pinned anchor already held — and emits and checks a `did.json`.
|
||
It creates no key material, never prints a seed, and refuses a published test key
|
||
unless told the log is a test log.
|
||
- `signer-conformance/`: a corpus of anchoring, confirmation and module vectors with
|
||
declared verdicts, generated and verified by `wai_signer_vectors` in CI on every
|
||
platform of the conformance matrix, with a canonical fixture computed by a separate
|
||
implementation (`signer_verify.py`), which also reproduces every vector's verdict. CI
|
||
runs it on the linux runner and requires its verdicts to equal `wai_signer_vectors`'
|
||
line for line (jwp-receipts §5.8).
|
||
- No wire format or committed signed vector changes. Every receipt and claim verifies
|
||
as before, and every vector of the claim corpus, those of the `measurement` and
|
||
`operating-point` classes included, keeps its outcome. The reference meters, whose seeds are published, now write a label as
|
||
`signer_id` rather than a real origin, so their signed output differs from before;
|
||
the world demo's signer is a label too. A CI test holds every `did:web` literal in
|
||
a tracked `.rs`, `.json` or `.js` file under `wai/` (`signer-conformance/` aside)
|
||
that names a domain, and every origin-bound one, to a reserved example name. `wai-quantum-serve`'s `/verify`
|
||
reports the signer's level beside validity.
|
||
- transparency §6 pointed to "jwp-receipts §7" when no §7 existed; it now points at
|
||
this section. SPEC §6.2–§6.3, confidentiality §6–§7 and jwp-receipts §6.6 no longer
|
||
say that a relay signs, or that checking a receipt needs no key: it needs the
|
||
signer's verifying key, which the receipt carries. prior-carriage §6 no longer says
|
||
WAI has no key discovery.
|
||
- Not done here: a key log for `wai-web` and the C ABI; an independent time that could
|
||
reinstate an item signed before its key was compromised; sub-delegation between
|
||
module grants; an authority for pre-emption, relay attestation or reconciliation,
|
||
which have no class; checking a log's evidence under its profile; the keys an OER
|
||
receipt is checked with.
|
||
|
||
### Typed receipt classes, and a second (soft) binding class
|
||
|
||
- [`extensions/jwp-receipts.md`](extensions/jwp-receipts.md) §2.4.3–§2.4.7: every
|
||
receipt now belongs to one **class**. `group`/`object` are the `delivery` class, with
|
||
their wire form unchanged; a new `claim` kind carries the others on one envelope (JCS,
|
||
RFC 8785), one Ed25519 signing rule and a per-signer chain kept separate from the
|
||
group chain. Classes: `energy`, `lawful-basis` (a declaration of purposes each with
|
||
its own basis, or a decision recorded against one) and `log-inclusion` (the log's
|
||
evidence carried verbatim — accepting the claim is not verifying the inclusion). An
|
||
unknown class is rejected, never skipped; `delivery` is reserved to `group`/`object`.
|
||
- **`energy`** gives an object's figure a signed place for its acquisition class, which
|
||
the object receipt has no member for
|
||
([`extensions/energy-measurement.md`](extensions/energy-measurement.md) §1 now says
|
||
so, and that a group total still has none). The claim restates the figure and carries the
|
||
class with its evidence in energy-measurement §2.7's JSON fields — `energy_provenance`,
|
||
`energy_uncertainty`, `energy_counter_filtering`, `energy_calibration_ref`, present and
|
||
`null` where a class has none — admitted through `crate::energy_class`: a refined
|
||
hardware class needs its declared uncertainty above the resolution floor, a counter
|
||
its filtering state and an instrument its calibration reference, and legacy `HwShunt`
|
||
is refused (`energy_legacy_acquisition`, `energy_acquisition_evidence`). Its grade may
|
||
not rank above what the class supports under
|
||
[`extensions/oer-receipts.md`](extensions/oer-receipts.md) §2 — both refined
|
||
hardware classes `measured`, `ModelBased` `derived`, `Estimator` `projected` — so a
|
||
verifier that checks an accepted claim against the OER receipt it names holds one
|
||
graded within the class the claim states. A named OER receipt
|
||
is read, verified and its figure read by the OER module under oer-receipts' own rules
|
||
(a vector document read under §4's profile, a figure read as §4 reads a quantity and
|
||
compared exactly); the claim layer has no reading of OER of its own.
|
||
- §2.6 and §3.2: a **second binding class**. A publisher-signed `soft-binding`
|
||
declaration names a registry algorithm (reverse-DNS form, aligned with the C2PA
|
||
soft-binding algorithm list), its type, the value and an `https` resolution endpoint;
|
||
a `binding-recovery` record states `attests: "ancestor"`. **The ancestor rule**: a
|
||
receipt reached through a soft binding attests the ancestor, never the object in
|
||
hand, and the transformation's energy is unaccounted rather than zero. A declaration
|
||
counts only if signed by the publisher of an already-verified delivery receipt for its
|
||
subject, and only once its own signature is verified — when a recovery is performed,
|
||
and when a recorded one, or a lawful-basis decision, is checked against the
|
||
declaration it names (`context_invalid` otherwise).
|
||
- §3.1 step 3 restates RFC 8032's own decoding rule (a key has one canonical encoding)
|
||
and range rule (`S` < L), and fixes the two choices on which RFC 8032 verifiers
|
||
differ: keys and `R` of small order are refused, and the equation is the cofactorless
|
||
one. Step 1 states what JSON is `malformed` where parsers part — a byte-order mark, a
|
||
repeated member name, an escape that leaves a surrogate unpaired, nesting deeper than
|
||
32 — and that a number never is (`1e400`, `-0` and a 400-digit integer are
|
||
`field_type`); `issued_at` must name a day its month has; `resolution_endpoint`'s host
|
||
must be non-empty and its port digits. Step 2 checks exactly the type each member
|
||
table gives, a member's type and encoding together in the table's order (a
|
||
lawful-basis body's `record` first, a nested object's members where the table reaches
|
||
it); what a class's rules add to a type — a non-zero figure or work count, a non-empty
|
||
basis, an `alg` in registry form, an `https` endpoint — is step 4's, with the rule's
|
||
own reason.
|
||
- `wai-rs --features receipt_claims` (`crate::claim_receipt`, `wai_claim_vectors`) and
|
||
`claim-conformance/`: vectors covering every reason, run by CI in both corpus loops,
|
||
with the feature added to the library tests. A canonical fixture computed by
|
||
a separate implementation (`claim_verify.py`: Python JSON with UTF-16 member order,
|
||
`cryptography` Ed25519 plus step 3's key, `R` and `S` checks written from the curve's
|
||
definition, BLAKE3 from its specification) is pinned in `wai-rs`; that script also
|
||
reproduces every vector's outcome, reading OER receipts with the OER reference
|
||
verifier. `claim-conformance/differential.py` runs both verifiers on generated inputs
|
||
the corpus does not hold and fails on any differing verdict. Both are run by hand,
|
||
not in CI.
|
||
- OER: `py_float_repr` wrote a double half-way between two shortest decimals with the
|
||
upper one (`75.036163330078125` as `75.03616333007813`), where the reference's `repr`
|
||
writes the even one (`…812`). §4's number profile keeps floats out of every receipt
|
||
body, so no content address or verdict depended on it. `str()` of a number no check
|
||
reads did — a vector's name, which the reference and `wai-rs` (`wai_oer_vectors`,
|
||
`wai_oer_diff`) print — so the two printed different names for such a vector. The
|
||
function now takes the length from the shortest round-trip formatter and the digits
|
||
from the exact one, keeping the shortest digits where the nearest decimal of that
|
||
length does not read back (next to a power of two); a table CPython generated pins
|
||
fourteen such doubles.
|
||
- Not done here: key discovery (every claim signature presumes the verifier holds the
|
||
key), an authority class for who may sign which class (a lawful-basis decision is
|
||
checked against its declaration whoever signed it: nothing supplies an operator's key
|
||
the way a verified delivery receipt supplies a publisher's), region- or time-scoped
|
||
soft bindings, and checking a log's receipt under its profile.
|
||
|
||
### `wai.neural.int_mlicpp` — the MLIC++ entropy topology at decode-equivalence
|
||
|
||
- New capability `wai.neural.int_mlicpp` (`wai-rs/src/int_mlicpp.rs`, behind
|
||
`neural_int`): the channel-autoregressive × checkerboard entropy model of
|
||
`wai.neural.mlicpp` — `K` channel slices, each decoded as an anchor pass then a
|
||
non-anchor pass, an entropy network over concat[latent decoded so far, hyper]
|
||
re-run before every pass — with the hyper-decoder, the entropy network and the
|
||
synthesis all integer networks (`int_hyper_synth`), the scale → distribution step
|
||
`int_entropy::det_build_index`, and the integer rANS. Registry: `NeuralIntegerExact`
|
||
(decode-equivalence), `Lossy`, royalty-free (its decoder is this crate's; the
|
||
patent status of the techniques it implements was not examined).
|
||
- Why: the float capability's validation run decoded to garbage while its encoder
|
||
evaluated the entropy network at its runtime's default thread count and the
|
||
decoder single-threaded — one scale fell on the other side of a table edge at
|
||
symbol 416 of 12,288, and every later symbol decoded wrong. Matching the execution
|
||
settings fixes that run; it remains a convention. Here every scale is an integer
|
||
both sides compute exactly, so no thread count, runtime or platform can move a
|
||
bucket.
|
||
- Wire: a `WIM1` payload (36-byte header, then the hyper-latent and latent rANS
|
||
streams) and a one-file `WMB1` model bundle (three `WHM1` networks and the entropy
|
||
tables), so a pinned prior's SHA-256 covers everything the reconstruction depends
|
||
on. The neural dispatch decodes it (`decode_envelope`, `decode_envelope_strict`)
|
||
and checks a pinned prior against the very bytes it decodes with.
|
||
- Totality: every value the stream controls is range-checked. Before the decode
|
||
allocates anything, each network's shapes are derived from its layer geometry (the
|
||
new `int_hyper_synth::net_shape`, which shares its geometry with the checked run):
|
||
a header whose shapes the model does not produce, or on which any network would
|
||
form a buffer — an activation, a transposed convolution's uncropped plane — over
|
||
`Header::MAX_TENSOR` elements, is refused as a model that does not fit the payload.
|
||
`tests/int_mlicpp_preflight.rs` counts allocations: refusing such a header
|
||
allocates a few kilobytes at most, where the decode without the check allocated
|
||
the tensors the header asked for, and on a synthesis wider than the cap aborted
|
||
the process on a 3.4-petabyte allocation. Latents go through
|
||
the new `int_entropy::dequantize` (`sym · 2^frac + base`, `None` where `i64` cannot
|
||
hold it; Kani harness `kani_dequantize_is_exact_or_refused`), and every network
|
||
through the new `int_hyper_synth::run_net_checked`, which runs `run_net` layer by
|
||
layer only while no product, partial sum or requantised value can leave its type
|
||
and refuses otherwise. A test moves one channel's scales across table edges: the
|
||
stream leaves the encoder at the first moved bucket, and the decode is refused
|
||
when the desynchronised latents would overflow the entropy network. With the
|
||
accumulation check removed, a damaged copy of the committed stream panics the
|
||
overflow-checked build.
|
||
- Conformance vector `wai-rs/tests/vectors/int_mlicpp/` (from
|
||
`tools/wai_int_mlicpp_export.py`, deterministic in its seed): **synthetic
|
||
weights**, quantised like a trained model's; no trained MLIC++ model is used, so it
|
||
shows decode-equivalence and nothing about compression quality. A 64×64 image, 8
|
||
latent channels in 4 slices, all 16 scale buckets used, 47 of 512 residuals and 5
|
||
of 16 hyper-latent symbols coded by the rANS escape. The export computes its
|
||
reference in numpy integer arithmetic under explicit int64 range checks and decodes
|
||
its own rANS bytes back, pass by pass, before writing. Pinned: the RGB (FNV-1a and
|
||
SHA-256) and FNV-1a over the bucket and symbol sequences.
|
||
- Evidence: decoded to the pinned bytes on macOS arm64 and on wasm32-wasip2 under
|
||
wasmtime; x86_64 not measured locally (determinism-tiers §6). A golden in
|
||
`byte-exact-conformance` puts it on the four-platform matrix;
|
||
`tests/int_mlicpp.rs` runs on every `wai-conformance` leg in both profiles, and the
|
||
dispatch test in `wai-neural-gates`.
|
||
- Registry tests now pin a recorded basis for every `NeuralIntegerExact` capability
|
||
(`NEURAL_INTEGER_EXACT_EVIDENCE`), as `STANDARD_DEFINED_EVIDENCE` does for
|
||
`StandardDefined`.
|
||
- `int_hyper_synth::IntModel::from_bytes`, the compact network form the integer
|
||
neural decoders load, is total on damaged bytes. It reserved room for the layer
|
||
count it read before checking that the bytes held that many layers — about 480 GB
|
||
for a count of 2^32 − 1, which aborts wherever that allocation fails — and formed
|
||
layer sizes and read offsets in unchecked arithmetic, which overflows a 32-bit
|
||
`usize` (on wasm32 the first case panicked with a capacity overflow). Both are
|
||
checked now, and a valid model parses to the same layers.
|
||
- `int_hyper_synth::run_net` now applies each layer through one function that
|
||
`run_net_checked` shares, `run_net_checked` takes each layer's geometry from the
|
||
function `net_shape` uses, and `decode`'s pixel quantisation is `to_rgb8`; all
|
||
refactors, and every committed vector reproduces its hash.
|
||
|
||
### Energy acquisition — the receipts wai-rs and wai-quantum seal can carry their class; meter windows open from rest
|
||
|
||
- [`extensions/energy-measurement.md`] §2.8: the world-session, film and reel,
|
||
haptic, score, 4D-splat, audio-scene, ternary-reconstruction and
|
||
provenance-step receipts, and every `wai-quantum` receipt, carry the
|
||
acquisition class as an **optional signed label**. Absent, a receipt's signed
|
||
bytes, identity hash and JSON are exactly the legacy ones, and the figure is
|
||
unlabelled: accepted as legacy, never read as any class. Present, the signing
|
||
domain's version byte becomes `0x02` and the class's §2.7 encoding follows
|
||
the figure it labels, inside the signature. A label is refused as legacy
|
||
`HwShunt`, beside a zero figure, or as a refined class without its evidence.
|
||
Provenance labels each step, and a quantum bill of materials each entry.
|
||
- **Wire:** unlabelled receipts are unchanged; tests pin one receipt of every
|
||
family to the bytes it had before the label existed, and every committed
|
||
conformance corpus still verifies. JSON evidence fields are now
|
||
`energy_`-prefixed (`energy_counter_filtering`, `energy_calibration_ref`) in
|
||
every snake_case carrier, including the rendered `wai.video.int_motion`
|
||
receipt: a quantum job receipt already renders `calibration_ref` for the
|
||
calibration receipt it ran under. §2.7 now fixes every field name.
|
||
- §2.9: IOReport is classed `OnChipCounter` — the silicon's own reading, whose
|
||
undisclosed derivation is part of the systematic error an `OnChipCounter`
|
||
declaration already excludes. The section lists the documents elsewhere in
|
||
the repository that class IOReport as `ModelBased` or `Estimator`, which this
|
||
revision does not change.
|
||
- §2.3 and §3: the completion-count error depends on how the burst window
|
||
opens. The reference meters read the count the moment their workers started,
|
||
with no barrier, so the count read low by about `N/2` and every per-unit
|
||
figure read **high** by about `N/(2C)` — the symmetric model did not describe
|
||
it. Workers are now held at a start barrier and released as the window opens:
|
||
the work lies in `[C, C + N)`, the figure divides by `C + N/2`, and the
|
||
declared term is `N / (√3 · (2C + N))`. The window-opening mode is part of
|
||
what a published figure reports.
|
||
- §2.3: a declaration describes the figure as sealed, and `joules_micro` is a
|
||
whole number of microjoules, so the declaration includes the figure's own
|
||
resolution: `0.5/√3` µJ for an estimate rounded to the nearest microjoule,
|
||
`1/√6` µJ for the difference of two whole-microjoule register readings. The
|
||
measurement's own terms are carried across in microjoules and the sum is
|
||
divided by the figure sealed. A 0.563 µJ estimate measured to 1.54 % is
|
||
sealed as `1` and declared 28.9 %. Every verifier refuses a hardware class
|
||
with `joules_micro × relative_ppm < 288 676`, a declaration smaller than the
|
||
integer's own rounding. The receipts built on `energy_class`, every
|
||
optional-label family, the `wai-quantum` labels and the C2PA energy binding
|
||
(bound or parsed) apply that check.
|
||
- §2.8: `quantum_energy::MaybeLabelled` parses and verifies a quantum receipt
|
||
in whichever form it arrives. It takes the form from the JSON and refuses a
|
||
malformed label rather than reading the receipt as unlabelled. The
|
||
`wai-quantum verify` CLI and the HTTP handler's `POST /verify` use it, so the
|
||
labelled receipt `wai_quantum_meter` emits for a measured figure verifies on
|
||
both, and both restate the class. `MaybeLabelled::verify_against_calibration`
|
||
checks a job's link to a calibration in either form, by the identity of the
|
||
form the calibration is in; the `wai-web` job demo accepts a labelled
|
||
calibration the same way.
|
||
- §2.2: the `OCMF` example now names what the regime rests on (a
|
||
conformity-assessed, legally verified measuring system, not a calibration
|
||
certificate); `OnChipCounter` is defined to cover any instrument whose
|
||
uncertainty is not traceable, and the class is stated not to record where
|
||
the sensor sits. §2.1 names the GREEN draft and its revision. §2 places the
|
||
three-name mapping rule on producers. [`extensions/energy-binding.md`] and
|
||
[`extensions/moq-streaming-format.md`] say "refined hardware class" where
|
||
they meant it, so legacy `HwShunt` is not read as missing its evidence.
|
||
- Reference implementation: `energy_class` gains the label rules
|
||
(`labelled_domain`, `write_label`, `check_label`, the JSON helpers) and the
|
||
window-opening model (`CompletionCount`, `WindowOpening`). Each `wai-rs`
|
||
family gains an `energy_provenance` field and `with_energy_class()`, which
|
||
re-signs with the receipt's own key; `ProvStep` gains the same.
|
||
`wai-quantum` adds `quantum_energy` without changing any existing type:
|
||
`Labelled<R>` and `LabelledQbom` re-sign a sealed receipt over the labelled
|
||
payload, and the inner receipt's own `verify()` refuses the result, so a
|
||
reader unaware of labels fails closed. The new `energy_burst` module runs §3
|
||
once for every meter; `wai_meter`, `wai_world_meter`, `wai_quantum_meter`,
|
||
`wai_prov` and `wai_video_meter` now seal `OnChipCounter` with a declared
|
||
uncertainty only when §4 and §2.3 allow, and seal unmetered otherwise. The
|
||
world and quantum meters no longer count in batches, and the reported
|
||
standard deviations are the sample ones the declared uncertainty uses. Each
|
||
meter prints the resolution term and, when it seals unmetered, why.
|
||
- **Rust API (`wai`):** the receipt structs of the families above gain a
|
||
public field, and `per_unit_relative_uncertainty` takes a `CompletionCount`.
|
||
`wai_meter`, `wai_world_meter` and `wai_prov` now emit an unmetered receipt
|
||
instead of exiting when no power reader is present.
|
||
- CI: a new `wai-energy-receipts.yml` runs the video/energy features, the
|
||
label and pin tests of every family, `wai-quantum`'s own tests and the HTTP
|
||
handler's verify test, on x86_64 and arm64. It builds four of the five
|
||
reference meters (`wai_video_meter`, `wai_meter`, `wai_world_meter`,
|
||
`wai_quantum_meter`). It does not build `wai_prov`, which needs the `codecs`
|
||
feature (system libjxl); no workflow enables that feature. `wai-conformance.yml` now
|
||
also triggers on `wai-quantum` changes, which its quantum corpora depend on.
|
||
- §2 and §2.8 now say what is enforced. The verifier rules are: a label is
|
||
checked wherever one is present, and a receipt defined with a class from the
|
||
start is refused when it carries a non-zero figure without one. Labelling a
|
||
new figure in an optional-label family is a **SHOULD**, where it was a
|
||
**MUST** that nothing checked and the reference implementation did not
|
||
meet. An unlabelled figure sealed today has the bytes of an issued one, so
|
||
no verifier can tell them apart. The reference implementation does not hold
|
||
its producers to the rule either: in these families no constructor takes
|
||
the class, and a label is added afterwards, by re-signing, or for a
|
||
provenance step before its receipt is built. §2.8 says how a library could
|
||
hold its callers to it: a labelled constructor, with the unlabelled path
|
||
kept only for recreating issued receipts and deprecated, as `HwShunt` is.
|
||
The reference implementation has not done that.
|
||
The reference meters label every non-zero figure they seal. §2.8 names the
|
||
two producers that do not: `world_model::frame_receipt`, which is not told
|
||
its caller's classes, and the `wai-web` demos, which seal illustrative
|
||
figures. It also says why a corpus generator that recreates a committed
|
||
corpus, such as `wai_quantum_ops_vectors gen`, is not counted: every
|
||
committed corpus predates the label.
|
||
- [`extensions/energy-measurement.md`] §1 states its scope: every receipt
|
||
profile WAI defines that carries an energy figure, a list that is not
|
||
closed. An OER/2 receipt is outside it. Its members are fixed, it has no
|
||
member for the class, and its evidential axis is the grade. No rule of the
|
||
extension binds an OER receipt or its producers and verifiers, so an OER
|
||
receipt with a non-zero figure and no class breaks none, and the §4
|
||
publication gate and §6 reporting contract do not reach it either. Only §2's
|
||
rule for carrying a WAI figure into a three-name vocabulary reaches past the
|
||
governed receipts. §1 also records that two receipts WAI defines fall short
|
||
of §2: the JWP group receipt, which signs a non-zero `joules_total`, and the
|
||
JWP object receipt, which carries `joules_micro`. Neither has a member for
|
||
the class. Where a `WAI_RECEIPT` property accompanies an object over MoQ,
|
||
the object's class travels in it, but no signature covers that property, so
|
||
it falls short too. Nothing carries a class for a group total, and §2.8's
|
||
rule that a total carries no class of its own does not cover it, because a
|
||
group's parts are labelled outside the group receipt, if at all. Giving
|
||
these a signed place for the class remains open.
|
||
[`extensions/jwp-receipts.md`] §2.4.1 points to this from `joules_total`.
|
||
[`extensions/interactive-worlds.md`] §8 now names the session receipt's own
|
||
signing domain, `wai:world-receipt\x01`, which the reference engine already
|
||
used.
|
||
- §2.6 says that the `wai.video.int_motion` and `wai.video.*` OTT constructors
|
||
accept `HwShunt`, so that issued receipts can be recreated, and that nothing
|
||
in them refuses it. The variant's deprecation warning reaches only code that
|
||
names it; a class parsed from its label can be `HwShunt` without one. No
|
||
producer in the reference implementation emits it.
|
||
- The resolution floor is stated as enforced: a declaration must reach the
|
||
`0.5/√3` µJ (≈ 0.289 µJ) that rounding the figure to whole microjoules adds,
|
||
`joules_micro × relative_ppm ≥ 288 676`. `SPEC.md`, the `wai-quantum`
|
||
README, both crates' `BelowResolution` messages and several doc comments
|
||
called it a 1 µJ floor, which is stricter than the rule verified.
|
||
[`extensions/energy-binding.md`] §4 now states the floor, which `bind()`,
|
||
`from_json()` and C2PA `recover()` already applied. The `energy_class` and
|
||
`quantum_energy` docs say what the floor is: the rounding term every
|
||
whole-microjoule figure carries. A figure read as a register delta carries
|
||
the larger `1/√6` µJ, which its producer must declare and no verifier
|
||
checks.
|
||
- [`extensions/oer-receipts.md`] §2 maps the evidential grade onto the refined
|
||
classes: `CalibratedInstrument` and `OnChipCounter` support `measured`, and
|
||
so does legacy `HwShunt`, for a figure sealed before the refinement or a
|
||
refined figure mapped into the three-name vocabulary. Neither an OER/2
|
||
receipt's `provenance.device` nor its `energy.method` names a class. The OER
|
||
wire format, vectors and verifiers are unchanged.
|
||
- Not yet covered: the in-browser demos of `wai-web` that seal into an
|
||
optional-label family seal illustrative figures, fixed in code or passed in
|
||
by the page, without a label, and the OTT manifest demo labels a
|
||
page-supplied figure `ModelBased`. The honest class of an illustrative
|
||
constant is `Estimator`. The `ModelBased` and
|
||
`Estimator` classes still carry no declared uncertainty.
|
||
|
||
### Energy acquisition — on-chip counters and calibrated instruments are separate classes
|
||
|
||
- [`extensions/energy-measurement.md`] §2 refines the hardware class. `HwShunt`
|
||
covered both a counter the measured silicon keeps about itself (RAPL, NVML,
|
||
SMC, IOReport) and an external meter, although only the second can carry an
|
||
uncertainty traceable to a calibration. It is now two classes:
|
||
`OnChipCounter` and `CalibratedInstrument`. Each **must** carry a declared
|
||
uncertainty: a standard (k = 1) relative uncertainty in ppm plus the
|
||
integration window it holds over. The counter records whether its output
|
||
was filtered (`On` / `Off` / `Unknown`; RAPL energy filtering per
|
||
INTEL-SA-00389), and the instrument records a reference to its calibration
|
||
certificate. For an on-chip counter the declaration covers only what the
|
||
implementation could evaluate, never the counter's own systematic error, so
|
||
it is not a total-error statement.
|
||
- §2.1 keeps the declared uncertainty and the GREEN `data-source-accuracy`
|
||
graded bands apart. A band bounds one device reading, while a declared
|
||
uncertainty is a standard uncertainty about a per-object marginal figure. A
|
||
band is never declared as a figure's uncertainty, and a declared uncertainty
|
||
is never used to select a band.
|
||
- §2.3 and §3: a per-unit figure from the bracketed protocol declares the
|
||
bracket's statistical term combined with a completion-count term,
|
||
`N / (√3 · C)` for `N` workers and `C` counted completions. Up to `N`
|
||
reconstructions are part-done at each edge of the window, and the count
|
||
cannot see them. The thread count is now part of what a published figure
|
||
must report.
|
||
- **Wire:** new signing tags `4` (`OnChipCounter`) and `5`
|
||
(`CalibratedInstrument`), each followed by its evidence (§2.7). Tags `0`–`3`
|
||
are unchanged and followed by nothing, so every receipt sealed with `HwShunt`,
|
||
`ModelBased` or `Estimator` keeps its exact signed bytes and verifies as
|
||
before. Tests pin those bytes. `HwShunt` is accepted when read, means
|
||
"hardware, sub-class unspecified, no declared uncertainty", ranks below both
|
||
refinements, and must not be emitted. JSON carriers (the C2PA companion
|
||
assertion, the rendered int_motion receipt) add evidence fields only for the
|
||
refined classes, so legacy bodies are byte-identical.
|
||
- Reference implementation: the new `energy_class` module is the one definition
|
||
shared by `energy_binding`, `c2pa_emit`, `video_receipt` and the
|
||
`wai.video.*` receipts. `verify()` and C2PA `recover()` reject a hardware
|
||
class that lacks its uncertainty. `CounterMeter` emits `OnChipCounter` with
|
||
the window it measured, declaring its configured relative uncertainty
|
||
combined with the register delta's resolution term, `1/√6` µJ, so a short span declares
|
||
more than the configured figure. `wai_video_meter` declares the bracket's statistical
|
||
(Type A) term combined with the completion-count term, and emits an
|
||
unmetered receipt when no decode completed in the window, where it used to
|
||
count one.
|
||
- The receipt families that sealed `joules_micro` before the class existed now
|
||
carry it as an optional label; see the next section.
|
||
|
||
### Integer decoders — fast kernels that keep every output integer
|
||
|
||
- `wai-rs` adds `int_kernels`: register-blocked convolution, transposed
|
||
convolution and IGDN kernels (NEON widening multiply-accumulate on aarch64,
|
||
portable Rust elsewhere), scoped worker threads (none on wasm), and a run-time
|
||
`Policy` that selects them or the retained scalar reference kernels. The
|
||
int_synth, int_hyper, int_audio, int_splat, int_world and JPEG-AI synthesis
|
||
decoders dispatch to them. No bitstream, conformance vector or hash changes.
|
||
- The claim, precisely: for every input the reference decodes, every policy
|
||
returns the same integer for every output element; with overflow checks on, a
|
||
fast policy panics on the inputs where the reference panics, though the panic
|
||
message can differ. Geometry the fast path does not handle, including a
|
||
transposed convolution whose sizes the reference's own arithmetic cannot form,
|
||
goes to the reference. The fast path's own allocations and loops stay in
|
||
proportion to what the reference reads and writes, whatever the stride or
|
||
padding. The two paths still allocate different buffers, so an allocation that
|
||
fails is outside the claim. It is argued by construction and tested, not
|
||
machine-proved. The rules, the tests, their limits and the measured throughput
|
||
(one machine) are in
|
||
[`research/decode-throughput.md`](research/decode-throughput.md).
|
||
- CI: `wai-conformance` runs the kernel-equivalence tests, every committed
|
||
vector that reaches the kernels and edge geometry through the public decoders
|
||
(strides up to `usize::MAX`, padding far beyond the kernel, and strides and
|
||
paddings at which the reference's size arithmetic overflows), under every
|
||
policy, with overflow checks on and off, on each platform leg. Geometry on
|
||
either side of the padded-input bound is pinned too: at the bound the fast
|
||
kernel runs, beyond it the reference does, and a test-build count of
|
||
fast-kernel entries shows which one ran.
|
||
`wai-byte-equality` and `wai-formal-verification` fire on changes to
|
||
`int_kernels.rs`.
|
||
|
||
### Headline decode tests fail on a missing input instead of passing
|
||
|
||
- The `int_hyper` tests over the pretrained mbt2018_mean (`int_hyper_synth`,
|
||
`int_video_sota_keyframe`) and the JPEG-AI whole-decoder tests
|
||
(`jpegai_e2e_full`, `jpegai_reconstruction_binding`, `jpegai_live`) read inputs
|
||
from outside the tree, and used to `return` — reported as a pass — when those
|
||
were absent. With
|
||
`WAI_REQUIRE_SOTA_VECTORS=1` a missing input is now a failure that names the
|
||
tool producing it; without it the skip is written to the process's real stderr,
|
||
so it shows in an ordinary `cargo test` run. Either way, a corrupt input file
|
||
fails, and so does a variable that names inputs which are not all there: a
|
||
path that does not exist (`WAI_JPEGAI_CODESTREAM`/`_WEIGHTS_DIR`/`_NORM`, even
|
||
when another of the three is unset), or a directory that does not exist or
|
||
lacks a file the test reads (`WAI_SOTA_INT_DIR`, `WAI_JPEGAI_ASSETS`, the
|
||
JPEG-AI weights directory). So without the switch a test skips only when no
|
||
variable names what is missing: one of the three path variables is unset, or
|
||
`WAI_SOTA_INT_DIR` or `WAI_JPEGAI_ASSETS` is unset or empty and its default
|
||
directory (`/tmp/sota_int`, `../jpegai-vm-assets`) is absent or incomplete.
|
||
The CI jobs always set the switch, so the new test
|
||
`require_vectors_rule` checks this rule without it: it runs the helpers in
|
||
child processes with the switch and the variables set per case, and
|
||
`wai-sota-vectors.yml` runs it.
|
||
- New CI job `wai-sota-vectors.yml` regenerates the mbt2018_mean integer vectors
|
||
from pinned inputs (`tools/wai_sota_vectors.sh`, the Python stack in
|
||
`tools/requirements-sota-int.txt`, a public-domain photograph pinned by SHA-256,
|
||
the published checkpoint pinned by its full SHA-256) and runs those tests
|
||
with the switch set. It is triggered by changes to the decode path and by a
|
||
weekly schedule. Besides requiring each test by name, it fails if the test log
|
||
contains a skip banner, since a skipped test also reports `ok`. The export tool
|
||
checks the checkpoint's bytes before loading them (as tensors only), because the
|
||
model zoo's own download path checks no hash. The job checks the Rust decode
|
||
against the Python integer reference on one runner; cross-architecture
|
||
byte-equality remains the committed tiny vector's job in
|
||
`wai-byte-equality.yml`. The regenerated vectors are not portable, and part
|
||
of them is not repeatable run to run on every machine. Between an arm64
|
||
macOS machine [name removed 2026-09-30] and the x86_64 runner, the synthesis-only test's input latent,
|
||
which the float model computes, differed, and with it its expected
|
||
`rgb_hash`; each side wrote the same latent in all of its runs. Two runs on
|
||
the one runner wrote different `cond.json` and `int_synth_sota.json`, most
|
||
likely in the float decode's PSNR, which only the quality test reads, against
|
||
thresholds. The PNG bytes of the source also differed between the Mac and the
|
||
runner; the PNG encoder writes them from the pinned pixels before any model
|
||
runs, and the pixels matched. So no expected hash of the regenerated vectors
|
||
is pinned in the repository (the committed tiny vector's is, in
|
||
`tests/vectors/int_hyper/tiny.json`); each run checks the hashes its own export
|
||
wrote, and a hash from one run's vectors must not be pinned for another run,
|
||
on the same machine or another. The pins that hold across machines are on the
|
||
inputs: the photograph's pixels and the checkpoint's bytes.
|
||
- New test `integer_decode_keeps_the_float_codec_quality`: the Rust decoder
|
||
measures its own reconstruction against the source pixels and must stay within
|
||
1 dB of the same model's float decode on the same image. On the pinned
|
||
photograph, measured on an arm64 macOS machine [name removed 2026-09-30]: integer decode 35.985 dB (8-bit
|
||
PSNR of the Rust decode), float decode 36.175 dB (the export tool's PSNR of the
|
||
float reconstruction), a cost of 0.19 dB, from 6328 bytes of rANS payload for
|
||
256×256 pixels. This is a relative bar on one image: nothing here compares
|
||
mbt2018_mean with another codec, and quality is image-dependent — the 42.6 dB
|
||
figure quoted for this codec was measured on a different photograph, which is
|
||
not in the repository.
|
||
- New CI job `wai-jpegai-e2e.yml` runs `jpegai_e2e_full` and
|
||
`jpegai_reconstruction_binding` with the switch set, with the same by-name and
|
||
skip-banner checks. Their inputs — the decoder
|
||
weights, normative tables and test codestream, extracted from the JPEG-AI
|
||
reference-software assets (which are not in this repository) — are fetched from
|
||
WAI's versioned models root (`jpegai/v1/`) by
|
||
`tools/wai_jpegai_fetch_e2e_inputs.sh` and checked against the SHA-256 manifest
|
||
`tools/jpegai-e2e-v1.sha256`. The job also pins the SHA-256 of the decoded
|
||
pixels (`WAI_JPEGAI_EXPECT_SHA256`): measured natively on an arm64 macOS machine [name removed 2026-09-30]
|
||
and reproduced by an x86_64 Linux build under QEMU user-mode emulation, and
|
||
required again on the x86_64 runner. That extends a measurement, not a
|
||
registry guarantee: the registry classes `wai.image.jpegai` as
|
||
`NeuralEntropyExact`, a conservative class that guarantees only the entropy
|
||
decode to be byte-identical on every machine, while the exact decoder's pinned
|
||
output for these inputs was measured identical on four platforms
|
||
(determinism-tiers §6).
|
||
- `jpegai_live` (the ONNX-runtime `wai.image.jpegai` decode) follows the same
|
||
missing-input rule, but no CI job runs it: its model bundle exists only in the
|
||
local JPEG-AI asset directory, and it needs the `neural` and `codecs` features.
|
||
- determinism-tiers §5–§6 record the two jobs. The full-scale `int_hyper` row and
|
||
the JPEG AI whole-decode row (split from the index-derivation row, whose test CI
|
||
still does not run) now name their workflow, state that a missing input fails
|
||
there instead of skipping, and state that neither workflow has run through the
|
||
forge: each passed under `forgejo-runner exec` on the Linux runner's host in an
|
||
earlier form, and its current steps have run on macOS arm64 only. §5 quotes the
|
||
`int_hyper` cost measured on the pinned photograph in place of the 42.6 dB
|
||
figure, and names the bar each job re-checks.
|
||
|
||
### Prior carriage and derived-track operations — registered with their limits
|
||
|
||
- New [`extensions/prior-carriage.md`]: a `Prior` MAY declare the wire form of
|
||
the artifact its digest covers (`prior.form`, additive on the wire; manifests
|
||
without it round-trip byte for byte). When declared, the digest covers the coded
|
||
bitstream, and a sink that cannot read the form refuses the prior rather than
|
||
substitute another packaging. `wai.prior.nnc` (ISO/IEC 15938-17:2024) is the
|
||
registered form. It is optional, not recommended: royalty status is not
|
||
established, and whether conforming decoders reconstruct identical tensors is
|
||
not established, so a `replicate` cannot rest on a prior read out of a form not
|
||
listed as exact (none is). As a payload, NNC takes the replicate class of a
|
||
standard whose decoder conformance the registry has not checked, as MPEG-I
|
||
Haptics does; its own conformance clauses were not read. `wai.tensor.nnc` is
|
||
registered as a candidate for general tensors; its coding is in an unpublished
|
||
amendment, which
|
||
is tracked, not pinned. The extension also fixes the precedence of in-band
|
||
data-unit signatures: WAI's digest covers every byte, and an in-band verdict is
|
||
recorded beside it, never instead of it.
|
||
- New [`extensions/derived-tracks.md`]: the ten derivation operations of
|
||
ISO/IEC 23001-16:2021 are registered as `wai.derive.*` strings, each with a
|
||
per-operation determinism tier. Dissolve and Scaling are untiered. No operation
|
||
is exact unconditionally, Identity included. An executed instance is exact only
|
||
when no sample value is converted into another representation or computed, its
|
||
output does not change over the span the claim covers, and its operation's own
|
||
conditions hold; the sink lists each condition it checked. A derived sample
|
||
holds the tier of its weakest step. A claim over it names the operations
|
||
actually executed, the parameter values each applied, the span it covers and
|
||
its inputs, visual and parameter. These are not payload capabilities, and WAI
|
||
does not composite. Clause 8 of the standard, which defines each operation, was
|
||
not read. Every tier rests on conditions checked on the executed instance; the
|
||
geometric conditions listed per operation come from the operation's title, as
|
||
does whether it reads a visual input, and reading clause 8 may show a list to be
|
||
incomplete. Clause 4 crops or fills an input of another size implicitly for
|
||
every operation, and no list carries a condition on where such an input is
|
||
placed.
|
||
- `wai-rs`: `codecs::derivation` (registry, `Condition`, `derived_sample_tier`),
|
||
`codecs::DeterminismTier` (the two tiers as values, re-exported by
|
||
`codecs::derivation`), `codecs::prior_form`, `Prior::{new, with_form, form,
|
||
check_form, form_preserves_exactness}`, and `container::replicate_prior_refusal`,
|
||
which states why a `replicate` is refused, including over a declared form.
|
||
- **Behaviour change:** `Prior::is_cross_hardware_exact` now also requires the
|
||
declared form, if any, to be established exact (`form_preserves_exactness`), so
|
||
a prior that declares a form is not cross-hardware exact whatever its contract
|
||
says; `determinism_class` still reads the contract alone. A prior without a form,
|
||
which is every prior written before `form` existed, gets the same answer as
|
||
before. The rule sits in that predicate because every `replicate` check reads
|
||
it, `container::replicate_is_sound` and `Wai::replicate_is_sound` included.
|
||
- Each `ExecutedOp` names the visual inputs its instance read (`reads`, as an
|
||
entry of `inputs` or the output of an earlier executed operation), because
|
||
clause 4 of ISO/IEC 23001-16 lets an operation that is not first read the
|
||
output of any previous operation, only new inputs, or both, so its position
|
||
does not say what it read. An instance of an operation that reads a visual
|
||
input and names nothing gives no tier, wherever it sits: it read an input that
|
||
nothing names, whether a track, item, track group or preceding output the file
|
||
lists or, where nothing lists one, the default fill picture (clause 5.1). When
|
||
at least one operation executed, a call that contradicts itself (a reference to
|
||
an unlisted input or to an operation that does not precede the reader, or a
|
||
listed input no instance names) is a `DerivedSampleError`; an empty executed
|
||
list gives no tier before those checks. `neural::ModelRegistry::resolve_checked`
|
||
refuses a declared form the sink cannot read, and the reference sink reads
|
||
none. A new CI workflow, `wai-neural-gates`, is configured to run the two
|
||
model-free tests of those neural-sink refusals, which no workflow ran before,
|
||
in an Ubuntu 24.04 container image; it has not yet run on a runner.
|
||
- **Rust API, source-breaking.** `container::Prior` gained a public field,
|
||
`form`. Its fields are all public and it is not `#[non_exhaustive]`, so Rust
|
||
code outside the crate that builds a `Prior` with a struct literal, or
|
||
destructures one without `..`, stops compiling. The fix is to add
|
||
`form: None`, or to build it with the new `Prior::new(id, sha256,
|
||
determinism)` (and `.with_form(form)`), which names no optional field and so
|
||
is unaffected by the next optional field added. Reading fields, serde, and
|
||
functional update (`Prior { form, ..other }`) are unaffected. SPEC §8 versions
|
||
the wire format, not this API. On the wire, `form` is an optional key, added
|
||
as `prior` itself was, and absent it the manifest's bytes are unchanged. SPEC §8
|
||
also says the manifest schema is frozen for all of 1.x; how an optional key is
|
||
added under that freeze is not settled here, for `form` or for `prior`. For the
|
||
crate, Cargo's semver rules treat a source-breaking change in a 0.x crate as
|
||
needing a new minor version: the release that carries this must be 0.4.0 or
|
||
later (`wai-rs/Cargo.toml` still says 0.3.0). `#[non_exhaustive]` was not
|
||
added. It would break the same literals now and, in addition, rule out the
|
||
`form: None` fix and functional update from outside the crate, which the
|
||
crate's own integration tests use. It would also make `Prior` the only
|
||
manifest struct that code outside the crate cannot build with a literal; the
|
||
others are `Manifest`, `ModelRequirement`, `Conditioning`, `ManifestV2` and
|
||
`RenditionMeta`. Whether they should all become `#[non_exhaustive]`, so that
|
||
adding an optional key never breaks Rust callers, is a decision for all of them
|
||
together.
|
||
- Open: the operations' four-character codes are not recorded (clause 8 of
|
||
ISO/IEC 23001-16 was not read); the `wai` CLI does not list derivation
|
||
operations; there is no NNC reader.
|
||
|
||
### Broadcast and streaming codecs — registered, licence-classified, never defaults
|
||
|
||
- SPEC §5 registers the elementary video and audio bitstreams distribution chains
|
||
carry: `wai.video.{av2,vvc,hevc,avc,vp9,evc,evc.baseline,avs3,lcevc}` and
|
||
`wai.audio.{mpegh,avs3,aac}`. Each names one payload framing, which §8 then
|
||
fixes forever: the Annex B byte stream for AVC, HEVC, VVC and LCEVC (each
|
||
standard also defines a bare NAL unit stream, which is not the capability),
|
||
AV2's Annex B length-delimited bitstream, IVF for VP9, the Annex B raw
|
||
bitstream file storage format for EVC, MHAS for MPEG-H 3D Audio and ADTS for
|
||
AAC. The AVS3 video and audio rows name each standard's elementary
|
||
bitstream; those texts were not read, so whether either defines a second
|
||
form is unchecked. Each also carries a licence class with its recorded
|
||
basis; two are classifications rather than verified statuses (AVS3: no
|
||
published fee schedule; EVC Baseline: royalty-free by design, no formal
|
||
guarantee). All are sink-supplied; none is in the floor or the recommended
|
||
set. No decoder and no C dependency is added.
|
||
- AV2 is registered royalty-free but not recommended: the reference decoder
|
||
exists, deployable decode is not yet confirmed. Promotion is a MINOR revision.
|
||
- LCEVC is an enhancement layer, carried embedded in its base bitstream or as a
|
||
`WAI2` companion whose `companion_of` must name one of its base codecs (AVC,
|
||
HEVC, VVC, EVC, VP9, AV1, under every capability naming those bitstreams;
|
||
`codecs::enhancement_bases`) and resolve to exactly one selectable rendition
|
||
of it; a base takes at most one enhancement layer. The base list is WAI's
|
||
registration, not a restriction of ISO/IEC 23094-2, which leaves the base
|
||
decoder unspecified; a later revision may add a base (a widening, §8).
|
||
- `wai.video.lcevc` and the HDR metadata descriptors (`wai.meta.hdr10plus`,
|
||
`wai.meta.dovi`) are companion-only, now stated normatively: never a v1.0
|
||
`capability` or `fallback`, never a v1.1 selectable rendition. `pick` /
|
||
`primary` never return them; `Wai::check_component_roles` (v1.0) and
|
||
`WaiMulti::check_component_roles` (v1.1) report envelopes that break the
|
||
rules (`RoleError`, `#[non_exhaustive]`). A `WAI1` naming one only as its
|
||
`fallback` does not conform and never takes dispatch step 5, but still decodes
|
||
via its primary capability (§4 step 3); new `Wai::dispatch_capability` applies
|
||
§4 with that rule, and `wai decode` and `wai inspect` both use it, so they
|
||
agree on such a file. `wai wrap` refuses to write a non-conforming envelope
|
||
and, with `--companion <cap> <payload>`, writes the `WAI2` companion form,
|
||
whose intent follows the same rule as the `WAI1` form's (`--intent`, below),
|
||
checked over the base payload's capability and each companion's. HDR
|
||
metadata may bind to a neural video payload, and the base check is what
|
||
refuses `wai wrap wai.neural.dcvc_rt … --companion wai.meta.hdr10plus …`
|
||
(`NeuralFloat`, SPEC §7) unless another intent is given. Every companion-only
|
||
capability is in a standard class, so the companion check refuses nothing
|
||
yet. `wai unwrap --component <i>` extracts a `WAI2` component.
|
||
- SPEC §7.1 states the three rules a `WAI2` companion meets, and
|
||
`WaiMulti::check_component_roles` enforces each: (1) its capability is
|
||
companion-only or unregistered — a registered standalone capability marked
|
||
companion is `RoleError::StandaloneAsCompanion`; (2) `companion_of` names a
|
||
capability it applies to (`codecs::companion_applies_to`: for HDR dynamic
|
||
metadata a registered capability in the `video` media class — new
|
||
`codecs::is_video_class`: every `wai.video.*` capability that is not
|
||
companion-only, the neural video codecs `wai.neural.{video_bmshj2018,dcvc_rt,
|
||
dcvc_fm}` and the films `wai.film.{linear,reel}` — or an unregistered one,
|
||
whose class is unknown; for LCEVC a base codec) —
|
||
`RoleError::IncompatibleBase`; (3) that base is present, and unique for an
|
||
enhancement layer. The check previously let an HDR descriptor bind to an audio
|
||
rendition, LCEVC bind to any `wai.video.*` rendition (JPEG 2000 included), and
|
||
any standalone codec ride as a companion; the reference test that carried an
|
||
IAMF stream as a companion now carries ST 2094-10 dynamic metadata [name removed 2026-09-30] instead.
|
||
- SPEC §8 now says what these rules are, so they do not read as a redefinition
|
||
inside `1.x`: a registration fixes a capability's payload format **and its
|
||
role** (standalone media, or companion-only); changing either, or narrowing
|
||
what a companion-only capability applies to, needs a new MAJOR. A MINOR
|
||
revision may add a rule that enforces a registration, or a manifest field's
|
||
definition, as it was made, if it lists every kind of envelope it makes
|
||
non-conforming. The companion rules are such rules: the HDR descriptors were
|
||
registered as companion descriptors to a video payload, not standalone media;
|
||
`wai.video.lcevc` was registered companion-only with these rules; and
|
||
`role = "companion"` was defined as naming, in `companion_of`, the component
|
||
it binds to. SPEC §7.1 lists the envelopes they make non-conforming: an HDR
|
||
descriptor as a `WAI1` `capability` or `fallback`, an HDR descriptor as a
|
||
`WAI2` selectable rendition (which `pick` / `primary` no longer select), an
|
||
HDR descriptor bound to a registered capability outside the `video` media
|
||
class, a registered standalone capability marked companion, and a companion
|
||
of any capability with no `companion_of` or none that names a selectable
|
||
rendition in the envelope. Such envelopes still parse. The list also records
|
||
that the earlier §7.1 named immersive-audio metadata as a companion use and a
|
||
reference test carried `wai.audio.iamf` that way, which rule 1 now rejects,
|
||
since `wai.audio.iamf` was registered as standalone media.
|
||
- SPEC §8 also says what registering a capability does to an envelope that used
|
||
its string while no revision registered it. §7.1 leaves such a use unchecked
|
||
(an HDR descriptor's unregistered base is not constrained; an unregistered
|
||
companion needs only a present base), so the registering revision may make the envelope
|
||
non-conforming where it uses the string against the new registration: a
|
||
companion whose capability is registered standalone, an HDR descriptor bound
|
||
to a capability registered outside the `video` class, a companion-only
|
||
capability carried as a payload or bound to a base it does not admit. §8
|
||
lists these kinds once for every registration; they still parse. This covers
|
||
`wai.video.lcevc`, which no earlier revision registered.
|
||
- `WaiMulti::companions_of` no longer says every companion it returns is meant
|
||
to be applied: the role check accepts an unregistered companion with a
|
||
present base, and SPEC §7.1 forbids a sink to apply a companion whose
|
||
capability it does not recognise.
|
||
- The broadcast-codec dispatch guidance asks for a recommended-set `WAI2`
|
||
rendition, not a bare v1.0 `fallback`: step 5 decodes the same payload with
|
||
the fallback's codec, which cannot read an HEVC, VVC or AV2 bitstream.
|
||
- `codecs::capability_license` is now exhaustive with no default class, so a
|
||
registered capability can no longer become royalty-free by omission. New
|
||
`codecs::mandatory_floor()` / `recommended_set()`; tests hold that every
|
||
default is royalty-free. `wai capabilities` gains a `role` column.
|
||
- **Replicate classes: exhaustive, and the exact class needs recorded evidence.**
|
||
`codecs::capability_replicate_class` no longer has a default arm (the
|
||
determinism-tiers change below still ended it in a `StandardDefined`
|
||
fall-through). Every registered capability is listed under one class; one
|
||
listed nowhere returns `None`, which the tests reject. `StandardDefined`, which
|
||
promises the same bytes on every conforming sink, is admitted only on a recorded
|
||
basis — a conformance clause that requires identical output, or the WAI
|
||
extension whose equivalence pins the reconstruction — pinned one row per
|
||
capability in the tests (`standard_defined_rests_on_recorded_evidence`). The
|
||
broadcast codecs enter with:
|
||
- `StandardDefined`: AVC, HEVC and VVC (ITU-T H.264 §C.4, H.265 and H.266
|
||
§C.5.1: every sample a conforming decoder outputs equals the specified
|
||
decoding process's) and VP9 (VP9 specification v0.6 §8.1);
|
||
- `StandardTolerance`: AV2 (AV2 v1.0.0 §7.1: film grain, as AV1);
|
||
- `StandardUnclassified`: EVC (both entries), AVS3 video and audio, LCEVC,
|
||
MPEG-H 3D Audio and AAC, whose conformance documents were not read.
|
||
|
||
`wai.audio.ivas` moves from `StandardUnclassified` to `StandardTolerance` on
|
||
3GPP TS 26.252 V19.2.0 §7.2.1: implementations based on the floating-point
|
||
reference are tested by non-bit-exact procedures. New
|
||
`codecs::is_cross_decoder_exact(cap)` is true where the class's
|
||
`replicate_promise` is `ByteExact`, except for a capability whose output is
|
||
computed from other envelopes' decoded output (new
|
||
`codecs::composes_decoded_sources`: `wai.audio.scene`). A scene's mixdown is
|
||
exact given its sources' PCM, and a source decoded through `wai.audio.opus` may
|
||
decode differently on two conforming sinks, so the capability alone does not
|
||
make it portable; new `codecs::is_cross_decoder_exact_with_sources(cap,
|
||
sources)` is true once every source's capability is known and exact, and
|
||
false when it cannot tell (no source given, or a source outside
|
||
`wai.audio.*`). The class's `ByteExact` promise for a scene holds given its
|
||
sources' PCM, so SPEC §7 names `is_cross_decoder_exact`, not the promise, as
|
||
the audit for the same bytes on every sink. The
|
||
promise covers the output the standard or extension specifies, not a
|
||
conversion a sink applies afterwards. New
|
||
`tools/wai_replicate_probe.sh` decodes each format with every independent
|
||
decoder on the host and fails if a `StandardDefined` capability's decoders
|
||
disagree — a falsification check, not the basis of any class.
|
||
|
||
### JPEG AI exact decoder: no platform math library on the path, and a cross-platform check
|
||
|
||
- The exact decoder (`jpegai_decode::decode`) called the platform math library's
|
||
`exp` in the per-channel gain scaler, on every target. Its roundings to an integer
|
||
compiled, on x86_64 and wasm32 (as rustc 1.98 compiles them), to calls to the
|
||
symbols `round`, `roundf` and `rint`, which the link resolves; an x86_64 Linux
|
||
test executable inspected took them from Rust's compiler builtins, not from the C
|
||
library. The decoder now takes `exp` and rounding from the new `float_repro`
|
||
module, which builds them from IEEE-754 basic operations, integer conversions and
|
||
bit manipulation, so their bits are fixed by the source. `float_repro::exp` was
|
||
within 1 ULP of the host math library on every host it was compared with
|
||
(determinism-tiers §6 lists them). Its test asserts ≤ 2 ULPs. The test is ignored
|
||
on targets whose C library was not measured, Windows among them. It selects by
|
||
target, not by host, so it also runs on glibc versions and on x86_64 hardware that
|
||
were not measured. The roundings equal the standard library's. The decoded output
|
||
is unchanged (RGB8 SHA-256 `dbc494a5…` for the `jpegai/v1` inputs).
|
||
- The exact decoder now also refuses a target that evaluates float arithmetic in x87
|
||
extended precision (32-bit x86 without SSE2), where it would not reproduce.
|
||
- New `jpegai_decode::decode_with_stages`: the same decode, also reporting the float
|
||
values after each stage (latents, synthesis, each post-filter), so two platforms
|
||
can be compared stage by stage rather than only by their final bytes.
|
||
- New `tests/jpegai_cross_platform.rs` pins SHA-256 hashes of the parsed float
|
||
inputs, of every reported stage and of the RGB8 output for the `jpegai/v1` inputs.
|
||
All nine matched on macOS arm64 (native), wasm32-wasip2 (a WASI runtime), Linux
|
||
arm64 (glibc) and Linux x86_64 (glibc, under user-mode emulation), each running
|
||
this change's final source, and on all four also before the math-library calls
|
||
were replaced. determinism-tiers §6 records the toolchain and the method for each.
|
||
Not measured: macOS x86_64, Windows, x86_64 hardware. The test skips without its
|
||
inputs; `wai-jpegai-e2e.yml` supplies them and requires it to pass; `float_repro`'s own pinned-output test
|
||
needs no inputs and runs in the library tests.
|
||
- Two checks keep math-library calls off the decode path. Clippy's
|
||
`disallowed_methods` (`wai-rs/clippy.toml`), denied in each module on the path,
|
||
rejects a listed float method however it is spelled.
|
||
`tests/jpegai_decode_symbols.rs` lists the symbols of an executable that links
|
||
only the decoder and its weight loaders, and fails on any C math-library function
|
||
the executable imports, whatever made the call, and on any it defines other than
|
||
the round-to-integral functions, whose results IEEE 754 fixes (unix targets). A
|
||
definition alone shows what the linker kept, not what the decode calls: GNU ld
|
||
kept an unused `floor` in an x86_64 Linux debug build. The conformance workflow
|
||
runs both on its unix legs. They replace a scan of the source text, which missed a
|
||
call spelled `f64::exp(x)`. Details and evidence: determinism-tiers §5–§6. The
|
||
registry class of `wai.image.jpegai` is unchanged here.
|
||
- The gain unit's `log_k` constant was `0.2003654931615394`, 9.2 × 10⁻⁹ from
|
||
`(ln 54.82 − ln 0.11) / 31`, which the reference computes as
|
||
`0.20036548400207613`. It is now that value. At β = 0, the only β the exact decoder
|
||
accepts, both constants give the same scaler for every gain-table entry, so the
|
||
decoded output is unchanged.
|
||
- `tools/wai_jpegai_gain_exp_margin.py` now models the reference's gain scaler as the
|
||
reference computes it, in float32, rounding half to even. It fails if the decoder's
|
||
`log_k` differs from the reference's. At β = 0 every table entry is at least 77
|
||
float32 ULPs from a rounding boundary, so the two scalers agree for any float32
|
||
`exp` within that. The margin does not hold over every 12-bit β: at four inputs
|
||
the reference's own scaler depends on the last bit of its `exp`
|
||
(determinism-tiers §6).
|
||
|
||
### OER receipts — graded, chained energy receipts with three implementations
|
||
|
||
- New [`extensions/oer-receipts.md`]: ingests OER/2 draft 0.1. A receipt records one
|
||
unit of work, its joule cost, the cost's **evidential grade** (`projected`,
|
||
`simulated`, `derived`, `measured` — orthogonal to the acquisition class, mapping
|
||
stated), and what it rests on; canonical, content-addressed, hash-chained and
|
||
Ed25519-signed. Conformance is reason-set-equivalence plus a canonical-form fixture
|
||
the vectors cannot check themselves.
|
||
- New `oer-conformance/`: the upstream 9 vectors, 1 from the portable C kernel
|
||
(`settle/`, Apache-2.0), 99 hand-declared edge vectors — every reason the upstream
|
||
nine never exercise, each number shape and decimal-string defect and each limit
|
||
from the accepting side, each bound, each of the four tolerances at and past its
|
||
edge on both sides and past 34 digits, each step of the order of the reasons
|
||
reported alone, each member judged on its value, each claim an abstention cannot
|
||
make, each key a signature cannot rest on, and the extremes a document may hold —
|
||
and `unreadable/`, the documents every verifier must refuse whole.
|
||
They found five defects in the reference verifier (a mis-quoted
|
||
`missing_capability`, three crashes on missing members, a repeated receipt id
|
||
hiding reasons), and a class of inputs on which the reference and `wai-rs`
|
||
reported different reason sets or crashed (parser-dependent numbers and versions,
|
||
lenient decimals, members of the wrong type, rounding past the 28th digit,
|
||
documents at the edge of what a parser can read); all fixed in both.
|
||
- `wai-rs --features oer` (`crate::oer`, `wai_oer_vectors`): a third, independent
|
||
verifier; CI runs it on the four-platform matrix and rebuilds the C kernel,
|
||
requiring its output byte-identical to the pinned corpus.
|
||
- `oer-conformance/fuzz_differential.py` with `wai_oer_diff`: seeded differential
|
||
fuzzing of the reference verifier against `wai-rs` over mutated corpus documents;
|
||
any divergence in reasons or refusal, or any crash, fails it.
|
||
- Departures from draft 0.1, each in the extension's §9: decisions are named
|
||
`oer.decision.*`, not `wai.settle.*` (WAI's namespace is for payload formats); the
|
||
stage list behind `bundle.provenance_root` is serialised exactly as the body is,
|
||
pinned by a non-ASCII fixture; the number profile is a MUST (I-JSON integers,
|
||
decimal strings in JSON's number grammar, anything else `malformed`); every
|
||
composition check is exact, with `ln 2` pinned as a decimal, and every tolerance
|
||
scales by the figure's magnitude; a document outside the document profile is
|
||
refused whole, a repeated member name included; every mistyped member has one
|
||
named reason; published keys are decoded strictly, so a small-order key cannot
|
||
verify every message; `oer` is a string;
|
||
and a decision that was not earned is receipted as `abstain` — no answer, no
|
||
record — which a new reason, `abstention_settled`, holds to that. `escalate` is a
|
||
verdict returned from upstream, as the corpus always had it.
|
||
|
||
### Determinism tiers — naming the reproducibility spectrum for neural decode
|
||
|
||
- New [`extensions/determinism-tiers.md`]: names the two guarantees a neural
|
||
capability can make, so a manifest can declare which it satisfies and a verifier
|
||
knows what it may check. `entropy-consistency` — the entropy decode reproduces
|
||
across devices (integer entropy arithmetic or explicit parameter transmission);
|
||
JPEG AI sets its bit-exact conformance point at this tier (the entropy-decoder
|
||
output). `decode-equivalence` — the whole decode to the final medium reproduces
|
||
on every conforming sink, so the rendered result has a portable hash; WAI's
|
||
integer capabilities are registered at this tier, and it is the precondition for
|
||
a hard reconstruction binding. Referenced from SPEC §7 (Replicate-soundness);
|
||
framed self-referentially.
|
||
- Normative: over a neural capability, `intent = replicate` promises
|
||
`decode-equivalence` (SPEC §3, §7; standard codecs, below). The
|
||
first version of the extension called `entropy-consistency` the floor for a
|
||
sound neural `replicate`, and SPEC §7 said the same in its closing paragraph
|
||
while requiring an integer decode path in its rule. Resolved: a neural
|
||
capability backs `replicate` only if it is registered at `decode-equivalence`,
|
||
which §7 admits only by the integer construction; `entropy-consistency` MUST NOT
|
||
back it, and neither may a reconstruction that one implementation pins through
|
||
fixed-order float stages. A tier is a property of the capability as registered,
|
||
not of one implementation.
|
||
- **Downgrade: `wai.image.jpegai` is registered at `entropy-consistency`, not
|
||
`decode-equivalence`, and no longer backs `replicate`.** Its registry class
|
||
moves from `StandardDefined` to the new `NeuralEntropyExact` (integer entropy
|
||
decode, float later stages). Its decode has float stages and a float ONNX
|
||
fallback, so decode-equivalence never held as a property of the capability, and
|
||
`StandardDefined` ("the neural float hazard does not apply") did not describe it.
|
||
What the exact decoder does is unchanged — one pinned reconstruction, reproducible
|
||
across machines under the conditions in the extension's §5 — and is now stated as
|
||
a property of that decoder. A reconstruction binding over `wai.image.jpegai` is
|
||
checkable only with that decoder (reconstruction-binding §2). Earlier entries
|
||
below that call the JPEG AI decode decode-equivalent, or byte-identical on every
|
||
machine, are superseded by this one.
|
||
- **`replicate` over a standard codec promises the named standard's decoder
|
||
conformance, not byte identity across sinks.** The first form of this change
|
||
stated in SPEC §3 that `replicate` promises the same bytes on every conforming
|
||
sink, while every standard codec stayed `StandardDefined` and passed the rule —
|
||
including codecs whose standard conforms decoders to a bound around a reference
|
||
(RFC 6716 §6 holds an Opus decoder to `opus_compare` thresholds, not to
|
||
identical output). SPEC §3 and §7 now state the promise per registry class, and
|
||
two classes are added:
|
||
- `StandardTolerance` — the named standard holds decoders to a criterion around
|
||
a reference: `wai.audio.opus`, `wai.image.jpeg`, `wai.image.jxl`,
|
||
`wai.image.jp2`, `wai.video.j2k`, `wai.video.av1`, `wai.video.av1.lossless`,
|
||
`wai.image.avif` (AV1 §7.2: identical output unless film grain is signalled,
|
||
and then no "perceptually significant differences", a criterion the AV1
|
||
specification leaves undefined), `wai.audio.iamf`.
|
||
- `StandardUnclassified` — the named standard's conformance clauses have not
|
||
been checked: `wai.audio.ac4`, `wai.audio.ivas`, `wai.mesh.draco`,
|
||
`wai.pc.gpcc`, `wai.pc.vpcc`, `wai.splat.sog`, `wai.haptic.mihs`,
|
||
`wai.meta.hdr10plus`, `wai.meta.dovi`, and the pass-through asset formats
|
||
`wai.mesh.glb` and `wai.splat.spz`.
|
||
|
||
`StandardDefined` keeps the capabilities whose reconstruction is specified
|
||
exactly (PNG, FLAC, zstd, XZ and the WAI-native capabilities). `replicate` over
|
||
the reclassified capabilities is still honoured; what it promises is narrower.
|
||
SPEC §7 also states that a conversion a sink applies after the standard's decode
|
||
(PNG depth reduction, YUV→RGB, JPEG colour space, rendering an IAMF mix) is not
|
||
part of the promise, and that an extension composing other capabilities' output
|
||
is only as exact as their classes (spatial-audio §4 now says so for sources
|
||
decoded through Opus). The clauses read for each classification, and what was not
|
||
read, are in `research/media-codec-registry.md`. API: new
|
||
`ReplicateClass::{StandardTolerance, StandardUnclassified}` variants,
|
||
`ReplicatePromise` and `ReplicateClass::replicate_promise`; `wai capabilities`
|
||
prints `standard-tolerance` / `standard-unclassified` for them.
|
||
- `wai.image.jpegai` stays `NeuralEntropyExact` as a conservative choice pending a
|
||
reading of its conformance part, ISO/IEC 6048-4. An earlier form of this change
|
||
said in SPEC §7 and the extension that its standard does not bound the
|
||
reconstruction, so no weaker promise than `decode-equivalence` was available.
|
||
Part 4 had not been read, and the overview of the standard that was read (arXiv
|
||
2510.13867) says the conformance part "specifies the requirements for generating
|
||
a JPEG AI-compliant reconstruction" and "considers the possibility that a decoder
|
||
is conformant to the standard without having bit-exact reconstruction", with "a
|
||
certain leeway" — which points to a criterion around a reference, though the
|
||
overview does not state one. SPEC §7 (*`wai.image.jpegai` and the standard
|
||
classes*) now states what was and was not read, that the capability is a
|
||
candidate for `StandardTolerance` if Part 4 sets such a criterion, and that its
|
||
treatment differs from the `StandardUnclassified` codecs' and film-grain AV1's
|
||
by this revision's choice, not by anything found in the standards. No class or
|
||
runtime behaviour changes.
|
||
- `decode_envelope_strict` and `Wai::replicate_is_sound` now apply SPEC §7 in two
|
||
steps (`codecs::replicate_refusal`): the capability's registry class first, then
|
||
the pinned prior. Before, they read only the prior's *declared* contract string,
|
||
so a `replicate` envelope over `wai.image.jpegai` or a float codec passed when its
|
||
prior declared an integer contract; it is now refused. API: new
|
||
`ReplicateClass::NeuralEntropyExact` variant (an exhaustive `match` must add it),
|
||
`DeterminismTier`, `ReplicateClass::{neural_tier, label}`,
|
||
`codecs::{replicate_refusal, ReplicateRefusal}` and `Wai::replicate_refusal`;
|
||
`Wai::replicate_is_sound` now consults the registry (a floor capability with no
|
||
prior now passes it; the free function `container::replicate_is_sound` is
|
||
unchanged and checks only the prior).
|
||
- Fix: `wai wrap` wrote `intent = replicate` over every capability, including
|
||
`wai.image.jpegai` and the float neural codecs, which SPEC §7 says an encoder MUST
|
||
NOT do, and it pins no prior, so its envelopes over an integer-exact neural codec
|
||
were refused by the strict decode as well. It now takes `--intent <intent>`
|
||
(default `replicate`) and writes `replicate` only where a sink accepts it with no
|
||
prior — over the three standard classes; otherwise it refuses, says why, and
|
||
writes nothing. The same holds for a `--fallback` capability, since a sink that
|
||
lacks the primary decodes through it (SPEC §4 step 5): `wai wrap wai.image.jp2 …
|
||
--fallback wai.image.jpegai` is refused unless given another intent. SPEC §7 now
|
||
says the class a sink checks is that of the capability it decodes through. API:
|
||
`codecs::unpinned_envelope_intent`. The library test checks every registered
|
||
capability against the sink's own check; `tests/cli_wrap_intent.rs` runs the
|
||
binary, which needs the `codecs` feature, which no CI job builds.
|
||
- CI: new `wai-neural-gates.yml` runs the two model-free neural-sink tests,
|
||
`strict_decode_gates_unsound_replicate` (the `decode_envelope_strict` gate) and
|
||
`resolve_checked_verifies_prior_hash`, on the Linux runner, and fails unless both
|
||
ran. No workflow ran them before: they need the `neural` feature, which
|
||
`wai-conformance.yml` does not build, and `wai-neural-e2e.yml` selects only the
|
||
reference-match tests. The job runs in an Ubuntu 24.04 image: the ONNX Runtime
|
||
binary the `ort` crate downloads needs glibc 2.38 and a GCC 13 libstdc++, and
|
||
in the runner's default 22.04 image the test binary fails to link.
|
||
- Wording corrections, each checked against its source:
|
||
- The claim that byte identity holds between sinks running the same decoder
|
||
build (the `StandardTolerance` and `ReplicatePromise` docs, SPEC §7) had
|
||
nothing behind it and is removed; `replicate` over those classes does not
|
||
promise byte identity across sinks.
|
||
- JPEG and JPEG 2000 were listed with Opus and JPEG XL as having a stated bound.
|
||
The clauses that would state them were not read (T.81 leaves the IDCT's
|
||
accuracy to its Part 2; only the table of contents and terms of ISO/IEC
|
||
15444-4 were read), so SPEC §7, the extension and the registry docs now say so
|
||
and give no bound.
|
||
- The JPEG AI overview is quoted instead of paraphrased: it says Part 4
|
||
"considers the possibility" of conformance without bit-exact reconstruction,
|
||
and does not itself state a criterion on the reconstruction.
|
||
- "Before v0.4.0" in the registry comments and "until v0.4.0" in the extension
|
||
were wrong: the replicate classes and `wai.image.jpegai` were both added within
|
||
v0.4.0. They now say "earlier in v0.4.0".
|
||
- determinism-tiers §2 described ISO/IEC 23001-11's decoding-operation reduction
|
||
as in-band metadata with which a decoder degrades its output after a conformant
|
||
decode. The 4th-edition CD text (WG 3 N1864) says otherwise: a decoding
|
||
operation reduction request goes from the receiving device to the remote
|
||
encoder, which sends a less complex bitstream, and complexity metrics let a
|
||
decoder vary its operating frequency; neither changes the output of a decode.
|
||
§2 now sorts every metadata set that text's Introduction names by what it
|
||
changes. Attenuation-map post-processing, display adaptation (the RGB
|
||
components of reconstructed frames rescaled, saturated or, for contrast
|
||
enhancement, set to zero or the peak signal before display) and cross-segment
|
||
decoding (an enhanced picture used as a reference) change the decoded medium,
|
||
and the post-decode rule applies to them. Decoding-operation-reduction and
|
||
attenuated-video requests, and low-power encoding, change the payload before
|
||
it arrives; the rule about "honouring a hint" is replaced by one about such a
|
||
payload: a tier, hash or receipt over it covers that payload, not the one it
|
||
stands in for. Media selection and complexity metrics change neither. §2 also
|
||
says what the metadata is: power-saving metadata stated in advance, whose
|
||
energy quantities are expected or potential savings (clauses 7.4 and 8), not
|
||
energy that was spent. energy-binding §7.1–§7.2 and task-fidelity §4 follow;
|
||
§7.2 now names the parties to `op_requested` and `op_committed` as the
|
||
requesting and the acting party, where "sender" and "receiver" had them
|
||
reversed for a DOR request. The transport landscape research note marks its
|
||
earlier reading as superseded in a note under its provenance header that
|
||
names every passage repeating it.
|
||
- `int_entropy.rs` cited SPEC §6 for replicate-soundness; it is §7.
|
||
- Correction to the JPEG AI entries (SPEC §5 row and the decoder entry below): the
|
||
dequantisation, latent refinement, post-filters (including the eICCI network) and
|
||
colour conversion run in IEEE-754 float with a fixed evaluation order, not in
|
||
integer; and the end-to-end test bar is max|Δ| ≤ 2 (recorded max|Δ| = 1), not
|
||
1 LSB.
|
||
- Fix: `wai.image.jpegai` decoded codestreams it does not implement. The exact
|
||
decoder checked only the picture size and latent channel counts, so a 256×256
|
||
codestream with β ≠ 0, another `model_id`, another synthesis transform or another
|
||
eICCI model selection passed that check and was decoded as if it had the
|
||
implemented configuration; the float ONNX path was no better (it applied β to the
|
||
entropy parameters but not to dequantisation). The earlier statement that every
|
||
per-image quantity is derived from the bitstream did not hold while β is not
|
||
implemented; β ≠ 0 is now refused. `jpegai_decode::check_codestream` now
|
||
checks every header field either path hard-codes, and both paths refuse a
|
||
codestream that fails it. The exact decoder also refuses to run in a non-default
|
||
floating-point environment (`fp_env_is_default`). The committed reference
|
||
codestream (`wai-rs/tests/vectors/jpegai/`) lets the library tests exercise the
|
||
check without fixtures.
|
||
- Fix: the JPEG AI picture-header parser mislabelled a flag. The bit after each
|
||
component's GRFS channel flags is `synthesis_tile_enable`; the LSBS flags are the
|
||
first two bits of the tool header (`parse_lsbs_flags`). It also read `c_ver` /
|
||
`c_hor` when `s_ver` / `s_hor` is 2, where the reference codes neither; the float
|
||
path's parser shared that defect and dropped the separate chroma β.
|
||
- Float reproducibility stated precisely (SPEC §7, the extension, `DeterminismClass`):
|
||
IEEE-754 basic operations are correctly rounded, and float decodes diverge across
|
||
hardware through what a runtime chooses per device and through the floating-point
|
||
environment. `replicate` still requires integer arithmetic, because those
|
||
conditions belong to one build and one process.
|
||
- The extension now also states what a receipt can hash under each tier, why
|
||
transmitting entropy parameters stops at `entropy-consistency`, which capabilities
|
||
are offered at `decode-equivalence` and at what cost, and the evidence behind each
|
||
claim — which tests skip, which CI does not run, and which matrix legs actually
|
||
ran (the Windows x86_64 leg has not run since 2026-08-27).
|
||
|
||
### Reproducible-reconstruction binding — a C2PA/JPEG-Trust hard binding over pixels
|
||
|
||
- New `wai.*` provenance capability: `reconstruction_binding` (feature
|
||
`provenance`) + [`extensions/reconstruction-binding.md`]. A cryptographic hard
|
||
binding over the *deterministically decoded* pixels of a neural-coded asset —
|
||
which such media normally can't carry, because a neural decode isn't
|
||
reproducible (JPEG AI's normative conformance point is the entropy-decoder
|
||
output, not pixels; its multi-branch design reconstructs one codestream several
|
||
ways). WAI's byte-exact decode restores a portable decoded-pixel hash. SHA-256
|
||
(the C2PA default) over the canonical RGB8 buffer plus a descriptor pinning the
|
||
reproducible decode; carried as a custom C2PA/JPEG-Trust assertion
|
||
(`science.transaction.wai.hash.reconstruction`) beside the standard byte-binding.
|
||
A substrate for machine-readable AI-content marking (e.g. EU AI Act Art. 50).
|
||
|
||
### `wai.image.jpegai` now dispatches to the byte-exact decoder
|
||
|
||
- `decode_envelope` prefers the deterministic `jpegai_decode::decode` when the
|
||
model directory ships the integer weight bundle (`weights/*.json` + `norm.json`),
|
||
falling back to the ONNX-runtime decode otherwise — existing bundles keep
|
||
working. `wai.image.jpegai` is now registered in SPEC §5 (it was a live
|
||
capability missing from the normative menu).
|
||
- The cross-arch byte-equality matrix now covers the merged decoder's entropy
|
||
stage (`byte-exact-conformance`), and that crate's build — broken on a moved
|
||
`rans.rs` include — is restored.
|
||
|
||
### JPEG AI — a byte-exact codestream→RGB decoder (`jpegai_decode::decode`)
|
||
|
||
- `wai::jpegai_decode::decode(codestream, &DecoderWeights, &NormTables) -> Rgb`
|
||
reconstructs an ISO/IEC 6048 JPEG-AI codestream to RGB through a fully
|
||
integer entropy front end and a deterministic value/synthesis/post-filter
|
||
path — one pinned reconstruction, reproducible byte-for-byte on every machine,
|
||
so the decoded pixels have a portable hash. No per-image fixtures: every
|
||
per-image quantity is derived from the bitstream; only checkpoint constants
|
||
come from a sink-supplied model bundle.
|
||
- Determinism profile: the entropy decode (mANS), the dequant, and the derived
|
||
entropy inputs (skip mask, σ index, RVS band, dequant gain) are integer-exact
|
||
against the reference; the PTQ value/synthesis stages are deterministic and
|
||
land within 1 LSB of the ISO reference decoder. Distinct from the in-house
|
||
`wai.neural.int_*` codecs (all-i64, hash-portable): this decodes a published
|
||
external standard's codestream deterministically.
|
||
- Modules: `jpegai_decode` (the composed entry point) over `jpegai_bitstream`,
|
||
`jpegai_mans`, `jpegai_pipeline`, `jpegai_synthesis`, `jpegai_icci`
|
||
(feature `neural_int`). Inputs are regenerable via
|
||
`tools/wai_jpegai_build_e2e_inputs.py` from the JPEG-AI reference-software
|
||
assets, which are kept locally and are not in this repository; the extraction is
|
||
published under WAI's versioned models root (`jpegai/v1/`). The whole chain —
|
||
codestream → RGB — is exercised end to end against the reference PNG by
|
||
`wai-jpegai-e2e.yml`, which fetches those inputs pinned by SHA-256.
|
||
|
||
### JPEG AI — a live, self-sufficient `wai.image.jpegai` decode (2026-06-22)
|
||
|
||
- `neural::jpegai::decode(codestream, &JpegAiModel)` decodes a real ISO/IEC 6048
|
||
codestream to RGB with **no per-image schedule fixtures**: every per-image
|
||
quantity is derived from the bitstream, and only checkpoint constants come
|
||
from a sink-supplied model bundle. Wired into `decode_envelope` — JPEG AI is
|
||
now a dispatchable capability, not just reference/conformance support.
|
||
- The two derivations that closed it, traced bit-exact from the reference: the
|
||
residual coding mask is `sigma_skip > 382` (the σ surface *before* the rvs
|
||
addition — on the bucketed index it looks ambiguous, on `sigma_skip` it is
|
||
exact, 0 violations / 65536 positions); the dequant is
|
||
`resi × rvs_dq[grfs[ch]][lk] / 2^16 / scaler_vec[ch]`, with `grfs` determined
|
||
the same way the σ rvs does so the per-channel `scaler_vec` is consistent.
|
||
Matches the reference decoder's PNG to within rounding (max diff 1), checked by
|
||
`tests/jpegai_live.rs` on a machine holding the local JPEG-AI assets; no CI job
|
||
runs it (its model bundle is not in the repository).
|
||
|
||
### Film reel — `wai.film.reel`, cuts over one world (2026-06-17)
|
||
|
||
- The editor's layer over `wai.film.linear`: a `WREL` embeds one sealed world
|
||
+ a sequence of **shots** (each its own camera track + frame schedule) played
|
||
back to back. Cutting wide→close, or one beat from two angles, is film
|
||
grammar; a reel carries it as a few kilobytes of camera keys, the world
|
||
shared across every shot. Conformance is **reel-equivalence** —
|
||
film-equivalence extended over cuts.
|
||
- Shots MAY revisit earlier world ticks (a cut back in time from a new angle),
|
||
so the world is sampled at the sorted/de-duplicated union of all frame ticks
|
||
— convergence still holds because replay-equivalence is order-independent.
|
||
- Reuses the film machinery (shared camera interpolation + pose bytes) + the
|
||
`FilmReceipt` profile (identity = `reel_hash`); `wai_meter` grows a `.wrel`
|
||
branch. Registered + tiered Lossless. Corpus vector `coverage` (the character
|
||
beat cut three ways, incl. a cut back in time) joins `film-conformance/`,
|
||
verified by `wai_film_vectors` on the four-platform matrix. SPEC §5 row +
|
||
`extensions/linear-film.md §7`.
|
||
|
||
### Linear film — `wai.film.linear` (2026-06-16)
|
||
|
||
- "A film is a sealed, camera-on-rails world." A generated film ships not
|
||
as pixels but as the **world** that produces it (an embedded sealed
|
||
`wai.world.replay`) + a **camera track** (keyframed pose over the world's
|
||
ticks) + a **frame schedule** (which tick each frame samples). The sink
|
||
renders the frames — lever 3 (instructions-at-the-sink) for the
|
||
synthesizable slice of "video" (animation / CGI / motion graphics). The
|
||
conformance corpus carries whole films in ~1 KB.
|
||
- `wai-rs` `film` feature (reuses the world engine + `Fx` floor + blake3):
|
||
the `WFLM` container (contract + embedded `WWLD` + camera track) and the
|
||
deterministic reconstruct contract. Conformance is **film-equivalence** —
|
||
per-frame `BLAKE3(world_state_hash(frame_tick) || interpolated camera
|
||
pose)` is bit-identical on every machine. It **composes** the embedded
|
||
world's replay-equivalence (inherited unchanged) with the camera track in
|
||
the `wai.det.fixed64` floor; the render (pixels) stays the sink's.
|
||
- The world engine gained one general primitive: `collect_checkpoints_
|
||
sampled` / `run_sampled_dispatch` — read the state hash at an arbitrary
|
||
frame cadence, independent of the contract's checkpoint cadence (a
|
||
renderer samples the timeline at its own frame rate).
|
||
- Corpus `film-conformance/` + `wai_film_vectors gen|verify` (dolly,
|
||
two-shot, crane-up) — all pass, on the four-platform CI matrix. Registered
|
||
+ tiered Lossless (film-equivalence is exact). `FilmReceipt` (per-frame
|
||
Merkle leaves) + a `wai_meter` `.wflm` branch emitting **measured** joules.
|
||
- Spec PROMOTED to `extensions/linear-film.md`, SPEC §5 temporal-content
|
||
row, content-taxonomy updated (animation-film → ✅). The biggest
|
||
commercial wedge in the taxonomy, now shipped.
|
||
|
||
### Music as instructions — `wai.audio.score`, LIVE at /score (2026-06-14)
|
||
|
||
- The deepest audio lever: a score carries **no recording**, only note
|
||
events — ship the composition, not the recording. `wai-rs` `score`
|
||
feature (reuses the world `Fx` floor + blake3): the `WSCR` container
|
||
(contract + note events) and a **trig-free** deterministic reference
|
||
synth — a phase accumulator drives a piecewise-linear oscillator
|
||
(saw/square/triangle/pulse) shaped by a linear ADSR, all `+ − × ÷`.
|
||
Pitch is a per-sample phase increment authored off-line (no exponential
|
||
in the floor). `mixdown_hash` is the **mixdown-equivalence** quantity,
|
||
the music counterpart of spatial audio; a soundfont/neural instrument
|
||
is the sink's presentation.
|
||
- Corpus `score-conformance/` + `wai_score_vectors gen|verify` (scale,
|
||
chord/polyphony, timbre-changing arpeggio) — all pass. Registered +
|
||
tiered Lossless (exact reference mix).
|
||
- Browser sink `ScoreSink` (`wai-web`) + `/score` page, LIVE: synthesizes
|
||
the score in-tab, verifies the mixdown hash vs the corpus, draws a
|
||
piano roll, and plays the reconstructed PCM via Web Audio. wasm pkg
|
||
uniquely named `wai_score`.
|
||
- Spec PROMOTED to `extensions/score.md` (site mirror), SPEC §5 row,
|
||
content-taxonomy updated (music → ✅). Completes the audio surface:
|
||
recorded objects (audio.scene) and synthesized notes (audio.score).
|
||
|
||
### Neural subsystem reconciled onto main (2026-06-14)
|
||
|
||
- Wires the full learned-image/audio decoder family into `neural/mod.rs`
|
||
and brings the categorical `rANS` (`decode_categorical`) it needs:
|
||
hyperprior (`mbt2018_mean`), checkerboard context (`elic`),
|
||
channel-autoregressive (`mlicpp`/`mlicv2`/`cmic`), integer-exact
|
||
(`int_synth`), `glc` (VQ), inter-frame video (`dcvc_rt`/`dcvc_fm`),
|
||
`snac` audio — plus the ISO/IEC 6048-1 JPEG-AI entropy + codestream
|
||
+ σ/value/filter port (`jpegai_*`).
|
||
- Restores the **semantic / generative path** (`semantic.rs`,
|
||
CLIP-similarity conformance, `ModelRequirement.min_sim`/`sim_metric`)
|
||
and the shared deterministic ONNX loader (`ort_session.rs`), behind
|
||
the `semantic` feature.
|
||
- Re-adds `byte-exact-conformance` (cross-arch rANS + int_synth + JPEG-AI
|
||
mANS goldens) and the model export/encode tooling under `tools/`.
|
||
- All features build + test green together (`neural` + `semantic` +
|
||
the interactive/temporal cargo classes); clippy clean.
|
||
|
||
### Asset provenance — `wai.asset.provenance`, LIVE at /provenance (2026-06-13)
|
||
|
||
- **Lever 4 — the "verifies whatever it carries" half.** For content that
|
||
cannot be reduced to instructions (a photo, a recording), WAI does not
|
||
reconstruct — it carries the opaque bytes content-addressed and attaches
|
||
a signed, energy-accounted **lineage**. `AssetReceipt` (`provenance.rs`,
|
||
`provenance` feature): a JWP group whose objects are processing
|
||
**steps** (op + input/output content hash + work + measured joules),
|
||
Merkle-bound, Ed25519-signed, with chain-linkage verification (each
|
||
step's input = the previous output, final output = the asset hash) and
|
||
`parent_receipt_hash` chaining. `from_json` so a verifying sink can
|
||
re-parse it.
|
||
- **`wai_prov` tool**: synthesizes a real image, builds a 3-step lineage
|
||
(origin → PNG master → published JPEG) with the **real measured CPU
|
||
energy** of each encode (IOReport via macmon, no sudo), wraps the JPEG
|
||
as a content-addressed `wai.image.jpeg` envelope, and emits the signed
|
||
receipt (origin 0 + PNG 0.43 J + JPEG 0.14 J = 0.57 J measured).
|
||
- **Browser sink — `AssetProvenance` (`wai-web`)** + `/provenance` demo,
|
||
LIVE: recomputes the asset's content hash in-tab and compares to the
|
||
receipt (tamper-evidence — a "corrupt one byte" button flips it red),
|
||
verifies the Ed25519 signature + Merkle chain, shows the energy-
|
||
accounted lineage, and hands the carried JPEG to a native `<img>` (the
|
||
browser decodes it; WAI does not). wasm pkg uniquely named `wai_prov`.
|
||
- **Spec PROMOTED to `extensions/provenance.md`** (site mirror). It is a
|
||
receipt profile *about* a carried asset (like `jwp-receipts`), not a §5
|
||
payload codec — the asset keeps its own capability (`wai.image.jpeg`).
|
||
Completes the thesis: carries whatever it can reconstruct, verifies
|
||
whatever it carries.
|
||
|
||
### Cargo-class polish wave — real cloud, EWA render, receipts, telepresence (2026-06-13)
|
||
|
||
Four follow-on builds across the new cargo classes:
|
||
|
||
- **Real-SPZ-derived 4D clip** (`wai-spz-gen 4d`): the `bloom` clip animates
|
||
1200 genuine SPZ [name removed 2026-09-30] Gaussians (the same cloud the static
|
||
`wai.splat.spz` asset ships, round-tripped through the SPZ library) —
|
||
real cloud, authored motion. LIVE on `/volumetric`. Honest framing: the
|
||
win is a verifiable per-frame state, not capture fidelity.
|
||
- **WebGPU EWA render on `/volumetric`**: the page now renders the
|
||
reconstructed cloud with a real anisotropic EWA splat pipeline (the
|
||
same one the worlds demo uses) — per-Gaussian covariance projection,
|
||
conic, 3σ bounding quad, premultiplied blend, back-to-front sort — with
|
||
a 2D alpha-disc fallback where WebGPU is unavailable. The hash-verify
|
||
badge is render-independent. Confirmed on WebGPU (a browser [name removed 2026-09-30]) + 2D fallback.
|
||
- **Per-class receipts with measured joules**: `HapticReceipt`
|
||
(`haptic/receipt.rs`) + `Splat4dReceipt` (`splat4d/receipt.rs`) — the
|
||
worlds/audio JWP receipt profile transposed to touch (per-channel
|
||
blocks) and volumetric (per-frame blocks), reusing `world::receipt::
|
||
merkle_root`. New `wai_meter` bin measures **real CPU energy** (IOReport
|
||
via macmon, no sudo) for haptic/splat4d reconstruction and emits a
|
||
signed, self-verified receipt carrying the measured `joules_micro`;
|
||
`wai_world_meter` now also dispatches `feed0`/`skeletal0`, so feed and
|
||
avatar worlds get the same measured `SessionReceipt`. Every cargo class
|
||
now has a measured-energy receipt path (haptic 57 µJ, splat4d 0.21 J for
|
||
1200 Gaussians × 17 frames, feed/avatar mJ-scale).
|
||
- **Avatar telepresence — LIVE at `/telepresence`**: a live skeletal world
|
||
of N avatars over the existing worlds relay. New `SkeletalActor`
|
||
(wasm) signs `CREATE` + `SET_ROTATION` `CausalOp`s; the worlds transport
|
||
(relay-first + BroadcastChannel fallback, hello/sync/op/hash,
|
||
boundary-hash convergence) is reused verbatim. Each tab creates and
|
||
animates its own arm rig; sinks converge on the pose hash ("poses agree
|
||
@ tick N"). Verified on prod through the real relay with two isolated
|
||
contexts — identical pose hashes, zero errors.
|
||
|
||
### Volumetric / 4D splats — `wai.splat.4d`, LIVE at /volumetric (2026-06-13)
|
||
|
||
- **Phase 5 complete — the roadmap frontier.** A `wai.splat.4d` is a
|
||
**temporal Gaussian splat**: the static splat extended to time. Each
|
||
Gaussian carries a keyframed track (position / scale / rotation /
|
||
color / opacity); the sink interpolates the cloud at frame N in the
|
||
`wai.det.fixed64` floor. Conformance is **frame-state-equivalence**:
|
||
the reconstructed Gaussian set at frame N hashes identically on every
|
||
sink, no tolerance — the temporal counterpart of the static splat.
|
||
- **`wai-rs` `splat4d` feature** (reuses the world `Fx` floor + blake3,
|
||
no codec/ML deps): the `W4DS` container (contract + per-Gaussian
|
||
keyframe tracks, 14 `Fx` per key) + a deterministic reconstruct (Fx
|
||
linear interpolation, hold outside the track). Trig-free (quaternions
|
||
interpolate component-wise; the sink normalizes for rendering).
|
||
`frame_hash` = the frame-state-equivalence quantity; `clip_hash` =
|
||
frame-independent identity. Registered + tiered **Lossy** (consistent
|
||
with `wai.splat.spz` — the splat representation approximates the
|
||
captured scene, even though the reconstruction is frame-state-exact).
|
||
- **`volumetric-conformance/` corpus** + `wai_splat4d_vectors gen|verify`:
|
||
orbit (two Gaussians swap places), pulse (scale + fade), morph (color
|
||
shift) — every frame's hash checked, all pass.
|
||
- **Browser sink — `Splat4dSink` (`wai-web`)** + `/volumetric` demo page,
|
||
LIVE: reconstructs the cloud in-tab and **verifies every frame's hash**
|
||
against the corpus; a frame scrubber re-derives the cloud, drawn as
|
||
painter-sorted alpha Gaussians (a sink with WebGPU renders the same set
|
||
with the full EWA pipeline — presentation). wasm pkg uniquely named
|
||
`wai_splat4d`.
|
||
- **Spec PROMOTED to `extensions/volumetric.md`** (site mirror),
|
||
registered in SPEC.md §5 (`wai.splat.4d`). **Honest about size**: the
|
||
win is content-addressing + dedup + a verifiable per-frame state, NOT
|
||
payload reduction (a real cloud is MB-scale) — stated plainly.
|
||
- **The cargo set is complete across the medium**: sight (worlds/replay),
|
||
hearing (audio/mixdown), touch (haptics/sample), live state
|
||
(feed/replay), a moving body (avatar/pose), and now a moving volume
|
||
(splat.4d/frame-state) — one `wai.det.fixed64` floor, one verification.
|
||
|
||
### Avatars / digital humans — `wai.avatar.pose`, LIVE at /avatar (2026-06-13)
|
||
|
||
- **Phase 4 complete — digital humans**: motion as instructions. An
|
||
avatar is a **skeletal world** — the worlds container/op-log/ordering/
|
||
sealing/state-hash/receipt-chain, with the `step` reduced to the
|
||
identity. Conformance is **pose-equivalence** (the per-bone local
|
||
transforms hash identically), a special case of replay-equivalence.
|
||
- **`wai.world.sim.skeletal0`** (in `wai-rs` `world` feature): a bone is
|
||
a `parent[16] + translation[3] + rotation-quaternion[4] + scale[3]`
|
||
(all `wai.det.fixed64`). Mutations: `CREATE` / `DELETE` /
|
||
`SET_ROTATION` (the animation driver) / `SET_TRANSLATION` / `SET_SCALE`,
|
||
all LWW. **Trig-free floor**: rotations are quaternions and the FK
|
||
composition is `+ − ×` only; axis-angle authoring happens off-line.
|
||
The forward-kinematics → world space and the skinning of a
|
||
`wai.mesh.glb` are presentation (a pure function of the hashed pose),
|
||
never in the hash. Registered + tiered `Lossless`; dispatched in
|
||
`run_sealed_dispatch`.
|
||
- **`avatar-conformance/` corpus** + `wai_avatar_vectors gen|verify`: a
|
||
sealed 3-bone arm wave, a sealed 5-bone humanoid pose, and three seeded
|
||
adversarial-arrival shuffles of the arm wave — all converge, all pass.
|
||
- **Browser sink — `SkeletalSink` (`wai-web`)** + `/avatar` demo page,
|
||
LIVE: replays a sealed animation in-tab, **re-derives the sealed pose
|
||
hash** (checked against the native corpus and the file's own seal), and
|
||
exposes FK world-space joints + bone segments to draw the skeleton on a
|
||
rotating orthographic canvas with a tick scrubber. wasm pkg uniquely
|
||
named `wai_avatar`.
|
||
- **Spec PROMOTED to `extensions/avatars.md`** (site mirror), registered
|
||
in SPEC.md §5 (`wai.avatar.pose`). A moving body joins sight (worlds),
|
||
sound (audio), touch (haptics), and live state (feeds) under one floor.
|
||
|
||
### State-feeds — `wai.feed.state`, LIVE at /feed (2026-06-13)
|
||
|
||
- **Phase 3 complete — live state-feeds**: state as instructions. A
|
||
feed is a **non-physics world** — the same `WWLD` container, op log,
|
||
canonical ordering, sealing, state hash, and receipt chain as
|
||
interactive-worlds, with the simulation `step` reduced to the
|
||
identity. Conformance is therefore **replay-equivalence**, reused
|
||
wholesale: no new criterion.
|
||
- **`wai.world.sim.feed0`** (in `wai-rs` `world` feature): nodes are
|
||
records of self-describing typed cells (`Int` i64, `Fixed` Q32.32,
|
||
`Text` UTF-8 — hashed without a schema). Mutations: `CREATE` /
|
||
`DELETE` / `SET_CELL` (LWW) and `ADD_INT` / `ADD_FIXED` (SUM —
|
||
commutative accumulation). Wrong-typed / out-of-range / absent-node
|
||
updates are deterministic no-ops. Registered + tiered `Lossless` in
|
||
the central `codecs` registry; dispatched in `run_sealed_dispatch`.
|
||
- **`feed-conformance/` corpus** + `wai_feed_vectors gen|verify`:
|
||
sealed scoreboard (SUM scoring + an LWW correction), telemetry
|
||
(LWW gauge + SUM packet count, incl. a negative `ADD_FIXED`), market
|
||
tape (LWW last + SUM volume), and three seeded adversarial-arrival
|
||
shuffles of the scoreboard — all converge, all pass (hash equality).
|
||
- **Browser sink — `FeedSink` (`wai-web`)** + `/feed` demo page, LIVE:
|
||
loads a sealed feed, replays it in-tab with the same engine that made
|
||
the corpus, and **re-derives the publisher's sealed BLAKE3 state hash**
|
||
(the file's seal also equals the native corpus hash — both checked).
|
||
A tick scrubber re-derives the feed at any tick; the record table
|
||
renders the typed cells generically. wasm pkg uniquely named
|
||
`wai_feed` (avoids the deploy fingerprinter's basename collision).
|
||
- **Spec PROMOTED to `extensions/state-feeds.md`** (site mirror),
|
||
registered in SPEC.md §5 (`wai.feed.state`). The **broadcast profile**
|
||
(one authoritative publisher, many read-only sinks) is documented as a
|
||
deployment shape of `wai.world.live`, not a new format. State joins
|
||
sight (worlds/replay), hearing (audio/mixdown), and touch
|
||
(haptics/sample) under one floor — verified the same way.
|
||
|
||
### Interactive worlds — `world` feature + conformance corpus (2026-06-11)
|
||
|
||
- **New cargo class implemented**: the interactive-worlds extension
|
||
(spec draft staged in `wai-transaction-science-web/research/`,
|
||
destined for `extensions/interactive-worlds.md`). Media that consumes
|
||
input: a world is a content-addressed artifact, every event is a
|
||
signed `CausalOp`, and conformance is **replay-equivalence** —
|
||
identical BLAKE3 state hash at every checkpoint, no tolerance
|
||
parameter. One container (`WWLD`) serves `wai.world.scene` /
|
||
`wai.world.replay` / `wai.world.live`, differing only in op-log seal
|
||
state; an input log and a multiplayer op log are the same object.
|
||
- **`wai-rs` `world` feature** (pure Rust, no ML dep chain; deps:
|
||
blake3 + ed25519-dalek): `Fx` Q32.32 fixed-point (`wai.det.fixed64`,
|
||
the floor profile — bit-exact unconditionally), xoshiro256++ contract
|
||
rng, signed/dep-gated ops with canonical `(effect_tick, lamport,
|
||
actor)` ordering, coordination-free BLAKE3 node minting, WWLD
|
||
section container with content-binding seal, `Sim` trait (the
|
||
extension standardizes the contract, not a physics engine) +
|
||
`kinematic0` reference registrant, sealed `Replayer` + `LiveWorld`
|
||
engines (buffering, past-tick re-derivation, equivocation rejection,
|
||
receipted-checkpoint history sealing).
|
||
- **`world-conformance/` corpus** + `wai_world_vectors gen|verify`:
|
||
sealed replays, LWW/SUM conflict batteries, dep-gap / past-tick /
|
||
shuffled-arrival live streams, expected hashes. All vectors pass;
|
||
clippy clean on the new code.
|
||
- **Spec PROMOTED to `extensions/interactive-worlds.md`** (site mirror
|
||
`src/pages/extensions/`), registered in SPEC.md §5: `wai.world.scene`
|
||
/ `wai.world.replay` / `wai.world.live` + asset media `wai.mesh.glb`
|
||
(canonical glTF 2.0 Binary, Khronos — decoder sink/library-supplied);
|
||
`media: "world"` noted in §3. Capability strings live in the central
|
||
`codecs` registry (`known_capabilities()`), tiered `Lossless` —
|
||
replay-equivalence is exact state reproduction, the simulation analog
|
||
of lossless.
|
||
- **Second sim registrant `wai.world.sim.ballistic0`** (gravity +
|
||
ground-plane bounce, per-node radius/restitution, `G = −10`, all
|
||
Q32.32 — bit-exact through every bounce) proving the registration
|
||
story: entered registry, dispatch, and corpus (`ballistic_bounce`
|
||
vector) without one line of the extension changing. New
|
||
`run_sealed_dispatch` routes on `contract.sim`; a missing sim
|
||
reports the world inert (`UnknownSimCapability`), mirroring SPEC.md
|
||
§4 step-6 semantics so callers can fall back per the envelope.
|
||
- **Native energy meter — measured joules, no sudo (2026-06-13)**:
|
||
corrected a wrong assumption that real energy needed RAPL/sudo
|
||
(unavailable on the arm64 prod box). On arm64 macOS [name removed 2026-09-30] the SoC energy
|
||
counters are readable through IOReport with **no privileges** (the
|
||
source `macmon`/`asitop` use; verified euid≠0 reads CPU/GPU/ANE power
|
||
on the arm64 machine [name removed 2026-09-30]), and `proc_pid_rusage` gives exact per-process
|
||
retired cycles/instructions. New `wai_world_meter` bin (`world`
|
||
feature): brackets a tight replay burst of a sealed `.wwld` against an
|
||
idle baseline, samples CPU power via macmon, and reports the marginal
|
||
**measured** CPU energy — e.g. the truss replay metered at ~512 µJ /
|
||
replay (~4.3 µJ/tick) at 3.2 W marginal, a real number that fills a
|
||
receipt's `joules_micro` where a native sink runs. The burst runs on
|
||
N threads so the marginal draw clears a shared box's idle fluctuation
|
||
(single-thread 512 µJ vs 10-thread 524 µJ/replay cross-validates the
|
||
method — per-replay energy is invariant to parallelism). With a second
|
||
arg the meter emits a full §8 `SessionReceipt` carrying the MEASURED
|
||
joules + exact `work_steps`, signed and self-verified — real measured
|
||
energy end to end. Spec §8 + the `/worlds` page corrected: the
|
||
sink-metering path needs no sudo (IOReport on arm64 macOS [name removed 2026-09-30], RAPL on
|
||
x86); the browser estimate is the fallback, not the ceiling. clippy
|
||
clean, 94 tests green.
|
||
- **Phase 2 begun — haptics (`wai.haptic.signal`) (2026-06-13)**: touch
|
||
as instructions. Spec drafted (`research/haptics-draft.md`), capability
|
||
registered + tiered. `wai-rs` `haptic` feature (reuses the world `Fx`
|
||
floor, no codec/ML deps): the `WHAP` container + a deterministic
|
||
reconstruct — per channel, a keyframed **intensity/sharpness** envelope
|
||
(the Core-Haptics pair every actuator API maps to) linear-interpolated
|
||
in `Fx` and quantized to `u8`. Trig-free by construction (amplitude +
|
||
sharpness, not a modulated carrier), so the floor reconstructs it
|
||
exactly with `+,−,×,÷` alone. `signal_hash` is the **sample-equivalence**
|
||
quantity. `wai_haptic_vectors gen|verify` + `haptic-conformance/` (ramp,
|
||
thud, dual-rumble pulse) — all pass, hash equality. 104 tests, clippy
|
||
clean. Next: Gamepad/Vibration browser sink + `/haptics` demo, then
|
||
promote. This completes the sight → hearing → touch trio's engines.
|
||
- **Spatial-audio: receipt profile + spec PROMOTED (2026-06-13)** —
|
||
Phase 1 complete. `audio_scene::SceneReceipt` (the worlds receipt
|
||
transposed to audio): identity `scene_hash`, the JWP group's objects
|
||
are the mix's **PCM blocks** (`BLAKE3("wai:audio-block")`), Merkle-bound
|
||
with JWP's exact domains, one Ed25519 sig over scene_hash + root +
|
||
blocks + `work_mac` (samples × objects, exact signed) + carried
|
||
`joules_micro`; `parent_receipt_hash` chains renders. Reuses
|
||
`world::receipt::merkle_root` — minimal new code. 2 receipt tests (101
|
||
total). Draft promoted to `extensions/spatial-audio.md` (+ site mirror,
|
||
SPEC §5 row + extension pointer) — and the canonical spec corrected to
|
||
the **as-built** `sqrt_constpower` pan (sqrt-only, no transcendental
|
||
tables) and the real receipt shape, since registration froze the
|
||
format. clippy clean; spec page live. Phase 1 (spatial audio) DONE:
|
||
spec · register · mixer · corpus · live /audio sink · receipt ·
|
||
promoted. Next: Phase 2 — haptics.
|
||
- **Spatial-audio browser sink — LIVE at /audio (2026-06-13)**: `wai-web`
|
||
`AudioSink` (wasm-bindgen): parse `WSCN`, load the corpus sources, run
|
||
the exact deterministic mixdown, expose `mixdown_hash_hex` +
|
||
`scene_hash_hex` + `mix` (i16 PCM) + `object_xz(t)`. The `/audio` page
|
||
(site-styled, in nav) loads a scene, **verifies the mixdown hash
|
||
in-browser against the corpus** (mixdown-equivalence, the same proof
|
||
worlds does for state), plays the bit-exact stereo mix via an
|
||
AudioBuffer, and shows a top-down radar of the objects panning. Proven
|
||
on prod: all three scenes verify, zero errors — wasm in a browser [name removed 2026-09-30] mixes
|
||
byte-identically to native. Honest split kept: plays the exact stereo
|
||
floor; binaural/HRTF noted as the richer optional presentation.
|
||
- **Spatial-audio conformance corpus (2026-06-13)**: `wai_audio_vectors
|
||
gen|verify` + `audio-conformance/` — the mixdown-equivalence analog of
|
||
the worlds corpus. Three vectors (static stereo pair, distance
|
||
near/far, keyframed left→right sweep) ship a `.wscn` scene + a
|
||
`.sources.bin` (decoded mono tones) + expected `scene_hash` and
|
||
`mixdown_hash`; `verify` mixes each and checks hash equality, no
|
||
tolerance. All pass; clippy clean.
|
||
- **Spatial-audio reference mixer — mixdown-equivalence (2026-06-13)**:
|
||
`wai-rs` `audio_scene` feature (reuses the world `Fx` floor + blake3,
|
||
no codec/ML deps): the `WSCN` container (scene contract + object table
|
||
+ asset table + optional seal, mirrors WWLD) and a fully deterministic
|
||
mixdown — `Fx` position interpolation, `inverse_clamped` distance
|
||
attenuation, `sqrt_constpower` pan (sqrt only, no transcendental
|
||
tables), `Fx` accumulate, round-half-away i16 quantization in integer
|
||
space. `mixdown_hash` (BLAKE3 over canonical i16LE PCM for a declared
|
||
layout) is the **mixdown-equivalence** quantity; `scene_hash` is the
|
||
identity. 5 tests prove it: centered = equal L/R, hard-left = silent
|
||
right, deterministic + layout-bound hash, distance attenuates, a
|
||
moving source sweeps L→R + container round-trips. Sources arrive as
|
||
decoded mono PCM keyed by content hash — decoding the `wai.audio.*`
|
||
assets is the sink's job. clippy clean. Next: conformance corpus
|
||
bin + Web Audio browser sink + demo.
|
||
- **Content taxonomy + cargo roadmap; spatial-audio Phase 1 begun (2026-06-13)**:
|
||
mapped the full space of web-transported content WAI's approach covers
|
||
(`research/content-taxonomy.md`) — a capture→synthesizable→executable→
|
||
symbolic spectrum × four levers (codec dispatch, neural prior,
|
||
instructions-at-sink, determinism+receipts), with a per-row
|
||
mechanism/extension/status table and the white space called out. A
|
||
phased build plan (`research/cargo-roadmap.md`) takes each white-space
|
||
class through the proven interactive-worlds recipe. **Phase 1 kicked
|
||
off — object-based spatial audio** (`wai.audio.scene`): the audio
|
||
analog of interactive-worlds (ship sound objects + a scene, render to
|
||
the sink's layout; **mixdown-equivalence** = replay-equivalence with
|
||
the tick replaced by the audio sample). Spec drafted
|
||
(`research/spatial-audio-draft.md`, `WSCN` container, `Fx` mixdown,
|
||
content-addressed `wai.audio.*` sources, stereo bit-exact floor +
|
||
binaural perceptual tier, WAI2 recorded-mix fallback, JWP receipt
|
||
profile); `wai.audio.scene` registered + tiered Lossless. 94 tests
|
||
green. Remaining phases: haptics, live state-feeds, digital humans,
|
||
volumetric/4D splats.
|
||
- **Real SPZ decode — `wai.splat.spz` end to end (2026-06-13)**: the
|
||
splat gallery now renders a genuine SPZ [name removed 2026-09-30] file, not a
|
||
mesh-derived stand-in. New `wai-spz-gen` crate depends on the field's
|
||
`spz` reference library (the sink-supplied codec WAI dispatches to),
|
||
encodes a 2,400-Gaussian cube cloud to a real `.spz` (validated by
|
||
re-loading through the library), and wraps it as a content-addressed
|
||
`wai.splat.spz` WAI object (`nebula.spz.wai`). `gen-gallery` adds it
|
||
to the scene's asset table (role 1) beside the glb. Browser:
|
||
`WorldSink.asset_payload(name, env, cap)` generalizes the §4.1 verify
|
||
to any capability; the page gunzips (DecompressionStream) and decodes
|
||
the SPZ bitstream in-tab — 16-byte header, non-interleaved arrays,
|
||
24-bit fixed-point positions, log scales, smallest-three quaternions,
|
||
byte/255 opacity+color — into per-Gaussian world covariance for the
|
||
EWA pipeline. Decoded count matches the encoded 2,400 (validates the
|
||
layout); the cube silhouette + surface-aligned discs validate the
|
||
quaternion math. Page copy corrected: the splat view decodes a real
|
||
SPZ asset (no longer "derived from mesh"). Verified locally (2,400
|
||
Gaussians, zero errors); prod serves the identical artifacts.
|
||
- **Anisotropic EWA splatting (2026-06-13)**: upgraded the splat
|
||
renderer from isotropic billboards to real 3D Gaussian splatting. Each
|
||
splat carries an anisotropic 3×3 covariance (surface-aligned flat
|
||
discs: wide in the triangle tangent plane, thin along the normal); the
|
||
vertex shader projects it to a screen-space conic
|
||
(Σ' = J·W·Σ·Wᵀ·Jᵀ with the perspective Jacobian), emits a tight
|
||
3σ bounding quad, and the fragment evaluates the conic — the textbook
|
||
3DGS pipeline, still CPU-sorted back-to-front and alpha-blended.
|
||
Cam uniform carries view + proj separately (+ viewport/focal) for the
|
||
Jacobian. Visibly sharper, surface-coherent splats; proven on prod,
|
||
zero errors.
|
||
- **3D render layer — PBR mesh + Gaussian splats (2026-06-13)**: the
|
||
`/worlds` gallery now renders the content-addressed, hash-verified
|
||
`wai.mesh.glb` two ways in a real 3D path (perspective camera + depth
|
||
buffer, all in `wai-web/demo/world.js`, no wasm change): a **solid
|
||
PBR-shaded cube** (flat normals from screen-space derivatives, Lambert
|
||
+ spec + sky-ambient, gamma) and a **Gaussian-splat cloud** —
|
||
instanced camera-facing quads with a Gaussian falloff, back-to-front
|
||
depth-sorted each frame (painter's order) and alpha-blended. The splat
|
||
cloud is *derived from the same verified mesh*, making the thesis
|
||
visible: one content-addressed asset, two sink-side renderings,
|
||
presentation never touches the state hash. SPZ binary decode stays
|
||
sink-supplied (stated honestly on the page); the renderer is the real
|
||
deliverable. Two gallery vectors (`gallery` / `gallery_splat`); the 2D
|
||
sim path is untouched. Proven on prod: both render, 768 splats from
|
||
the cube, zero errors.
|
||
- **Receipt chaining + work-meter + `wai.splat.spz` (2026-06-12)**:
|
||
three follow-ons. (1) `SessionReceipt` gains `parent_receipt_hash`
|
||
(BLAKE3 of the prior receipt's canonical bytes — the JWP linkage) +
|
||
`receipt_hash()` + `verify_chain`: successive sealed sessions of one
|
||
world (shared `world_hash`, `group_id`+1) form an append-only signed
|
||
timeline. (2) An **honest energy meter**: receipts carry exact,
|
||
signed, deterministic `work_steps` (Σ node-steps, from
|
||
`collect_checkpoints_and_work`) AND a separately *carried*
|
||
`joules_micro` — never derived in the receipt; the browser supplies a
|
||
wall-clock estimate at a **declared** coefficient (labeled "est@4W"),
|
||
native sinks supply real RAPL, an unmetered sink signs `0`. (3)
|
||
Registered **`wai.splat.spz`** — Gaussian-splat scenes as an asset
|
||
capability parallel to `wai.mesh.glb` (presentation only, never in the
|
||
state hash; the win is content-addressing + dedup, not payload size;
|
||
renderer sink-supplied). Browser `seal_session(seed, id, joules,
|
||
group, parent)` chains via localStorage; the `/worlds` HUD shows
|
||
`chain #N ↳linked · N steps · ≈X mJ est@4W · ✓ re-verified`. Proven on
|
||
prod: two seals chain (g0 parent null → g1 parent = g0.receipt_hash),
|
||
exact work 248 steps, est joules carried, zero errors. Spec §8 updated
|
||
(chaining + the two-quantity energy model). 94 native tests, 10/10
|
||
vectors, clippy clean.
|
||
- **Asset path SHIPPED — `wai.mesh.glb`, content-addressed (2026-06-12,
|
||
extension §4.1)**: worlds are made of WAI objects. New
|
||
`world::asset_content_hash` (BLAKE3 `wai:asset\x01` over a complete
|
||
WAI envelope) + `DOMAIN_ASSET`; native tests PROVE the load-bearing
|
||
claim — a scene with an asset table has the byte-identical state hash
|
||
AND world hash as the same scene without it (assets are
|
||
presentation-only, never in the state hash), and the asset hash binds
|
||
the exact envelope. Generator `gen-gallery` writes a `gallery.wwld`
|
||
scene + `assets/cube.glb.wai`: a real glTF 2.0 Binary cube
|
||
(hand-written GLB writer) wrapped as a `wai.mesh.glb` WAI envelope and
|
||
content-addressed in the table. Browser: `WorldSink.assets()` +
|
||
`asset_glb(name, envelope)` (verifies content hash in WASM, rejects a
|
||
mismatch, unwraps the WAI envelope to the `.glb` payload); the page
|
||
decodes the GLB (minimal JS decoder) and renders the verified mesh as
|
||
a spinning wireframe. Proven on prod: `1/1 verified ✓ · cube.glb`,
|
||
and the state hash stays fixed while the mesh spins — §4.1 made
|
||
visible. 92 native tests, 10/10 vectors, clippy clean.
|
||
- **Session receipts + seal-to-replay SHIPPED** (2026-06-12, extension
|
||
§8 implemented): `world/receipt.rs` builds a `SessionReceipt` — a JWP
|
||
*profile*, not a fork: op batches (one per checkpoint interval,
|
||
content-hashed `wai:world-ops\x01`) are Merkle-bound with JWP's exact
|
||
leaf/node domain separators, and one Ed25519 signature covers the
|
||
world-profile payload (world hash + checkpoint `state_hash` list +
|
||
carried microjoules). Joules are **carried, never fabricated** — the
|
||
builder takes the figure; an unmetered session signs `0` and says so.
|
||
New `world_hash` (BLAKE3 over scene = contract + tick-0 graph, the
|
||
§7.4 shareable identity), `collect_checkpoints` (unsealed replay →
|
||
checkpoint list). Browser: `WorldSink.seal_session(seed, signer_id,
|
||
joules)` → `SealedSession{replay_bytes, receipt_json, root_hash, …}`;
|
||
the `/worlds` page's "⏷ seal & download replay" turns a live, shared,
|
||
edited world into a portable `.wwld` + `.receipt.json`, re-opens the
|
||
fresh replay in a throwaway sink to prove it re-verifies, and shows
|
||
the Merkle root + batch count in the HUD. Proven on prod: fracture a
|
||
tower across the relay → seal → 3-batch receipt, valid sig, replay
|
||
re-verified. 90 native tests, 10/10 vectors, clippy clean.
|
||
- **Cross-device multiplayer SHIPPED** (2026-06-12, `wai-relay/` +
|
||
`wai-web` Actor binding): every tab/device is a signed actor. New
|
||
`Actor` wasm-bindgen class (browser-held Ed25519 key; clicks author
|
||
real `CausalOp`s; Lamport discipline via `observe_lamport`);
|
||
`wai-world-relay` reference transport (axum WS, per-room fan-out,
|
||
retains the signed op log for late joiners, **verifies every op's
|
||
Ed25519 signature at the door** with the same `wai` crate — it
|
||
adjudicates nothing; sinks converge because the math does). Page
|
||
transport is relay-first (room in the URL hash) with BroadcastChannel
|
||
fallback. Deployed at `wai.transaction.science/worlds-ws/*`
|
||
(launchd `com.wai.world-relay`, localhost:9670). Proven on prod with
|
||
two ISOLATED browser contexts: point-blank axial shoves snap two
|
||
members (23→21 nodes) and both contexts converge — "✓ hashes agree".
|
||
Also fixed `build.rs` emitting libFLAC link flags regardless of
|
||
features (pkg-config probe side effect): now gated on `codecs`.
|
||
- **Browser sink SHIPPED** (2026-06-12, `wai-web/src/world.rs` +
|
||
`wai-web/demo/world.{html,js}`): a `wai.world.replay` sink in a tab.
|
||
NOT a port — the `wai` crate itself now compiles to wasm32 via the
|
||
new default-on **`codecs` feature** (C codec chain gated; envelope +
|
||
`world` engine build with `--no-default-features --features world`),
|
||
and wai-web binds it (`WorldSink`: advance/particles/members/
|
||
state-hash/verify-seal; sim state stays exact Q32.32, f32 only for
|
||
display). WebGPU renderer (instanced SDF circles + member lines).
|
||
Proven in a real browser [name removed 2026-09-30] via Playwright: the 1,282-byte truss_fracture
|
||
payload replays 120 ticks, the member snaps, and the browser's
|
||
BLAKE3 state hash equals the native corpus hash byte-for-byte —
|
||
**replay-equivalence across arm64-native and wasm32 in a browser [name removed 2026-09-30],
|
||
demonstrated**, with the Ed25519 seal verified in-page.
|
||
- **Solver-backed registrant `wai.world.sim.truss0`** (2026-06-12,
|
||
`world/truss.rs`): structural simulation — particles + distance
|
||
members solved per tick by position-based dynamics (fixed 8-pass
|
||
Gauss–Seidel projection in canonical member order), entirely in
|
||
`wai.det.fixed64` including a new floor-rounded `Fx::sqrt` (128-bit
|
||
integer Newton). Members carry a yield strain measured on predicted
|
||
positions before the solve; an overloaded member **fractures** as an
|
||
emergent solver consequence — two actors pulling the same member in
|
||
opposite directions (the SUM rule feeding the solver) snap it
|
||
bit-identically on every machine (`truss_fracture` corpus vector).
|
||
Registration constants: `G = −10`, `ITERATIONS = 8`, ground at
|
||
`y = 0`, velocity damping `63/64`/tick (static equilibrium).
|
||
`LiveWorld` gained `state()`/`current_tick()` accessors (what a
|
||
renderer reads). Where the float-based CAD solvers can't transplant
|
||
(floats aren't cross-platform bit-exact), solver *form* does:
|
||
iterative constraint projection in the deterministic profile.
|
||
|
||
### Inter-frame video, categorical/VQ images, and real-model validation
|
||
|
||
- **`wai.neural.dcvc_rt` / `wai.neural.dcvc_fm` — inter-frame video decoder
|
||
implemented** (`neural/video_dcvc.rs`). "Requires a GPU" [name removed 2026-09-30] was a *speed* claim:
|
||
inter-frame decode is a frame loop that propagates a reference feature, and
|
||
ONNX Runtime runs it on CPU. The per-frame entropy decode reuses the
|
||
CompressAI-validated hyperprior path; the temporal net is sink-supplied.
|
||
Validated end-to-end (3 inter-frames, feature propagation confirmed).
|
||
- **`wai.neural.glc` — categorical / VQ decoder implemented** (`neural/glc.rs`).
|
||
Not the Gaussian-conditional family: codebook indices decoded via a
|
||
per-position softmax → `pmf_to_quantized_cdf` (bit-exact with CompressAI's
|
||
C++, unit-tested) → a bypass-free categorical rANS over a checkerboard → VQ
|
||
synthesis. Surfaced that categorical entropy is the most determinism-sensitive
|
||
path — a per-position CDF flips on ~1e-5 logit drift where Gaussian scale
|
||
buckets don't.
|
||
- **Real-model validation** beyond synthetic stand-ins. SNAC's genuine
|
||
`snac_24khz` now exports (`tools/wai_snac_export.py`: zero-noise so the decode
|
||
is deterministic at all + a reshape-free snake) and `wai-rs` decodes it to
|
||
~9e-7 of the torch reference. A real PTQ-trained, activation-calibrated
|
||
`int_synth` model decodes **bit-identically** (max|Δ|=0) to its integer-forward
|
||
reference (`tools/wai_int_synth_train.py`; also fixed a zero-gradient
|
||
`torch.floor` bug in the QAT trainer).
|
||
|
||
### Learned-image codec family — decoders implemented and validated
|
||
|
||
- The reference crate now decodes the full conditional-entropy family
|
||
natively, not just the factorized baseline: **`wai.neural.mbt2018_mean`**
|
||
(mean-scale hyperprior, Gaussian-conditional), **`wai.neural.elic`**
|
||
(checkerboard spatial context), **`wai.neural.mlicpp`**
|
||
(channel-autoregressive multi-reference), and the integer-exact
|
||
**`wai.neural.int_synth`** (all-integer transposed-conv synthesis — a
|
||
decode that is bit-identical on every machine *by construction*, the
|
||
first capability that can soundly back `intent=replicate` cross-hardware).
|
||
The rANS + entropy decode live in `wai-rs`; the analysis/synthesis nets
|
||
are sink-supplied ONNX.
|
||
- **Current-SOTA entries added as live routes:** `wai.neural.mlicv2`
|
||
(MLICv2/v2+, the current SOTA learned image) and `wai.neural.cmic`
|
||
(Content-Aware Mamba) both decode through the multi-reference core —
|
||
their entropy model is the same channel-slice + checkerboard structure;
|
||
the gains live in their sink-supplied transforms.
|
||
- **Validation against the reference codec.** `bmshj2018` and
|
||
`mbt2018_mean` are checked byte-for-byte against pretrained CompressAI
|
||
models (reconstruction matches to within final-layer float rounding);
|
||
`elic`/`mlicpp` are checked end-to-end against minimal CompressAI-
|
||
compatible models; `int_synth` against a constructed integer model.
|
||
Surfacing this also caught (and the spec now documents) a real
|
||
determinism hazard: a learned codec's entropy decode desyncs unless the
|
||
encoder and decoder compute the scale buckets with the *same* model —
|
||
float drift between two implementations of `h_s` flips bucket
|
||
assignments and corrupts the stream.
|
||
|
||
### Audio: SNAC hierarchical codec
|
||
|
||
- **`wai.neural.snac`** — SNAC's multi-resolution RVQ, distinct from the
|
||
flat single-grid RVQ runtime: N variable-length code streams at
|
||
hierarchical rates, decoded via dynamic-arity ONNX inputs. Ubiquitous in
|
||
low-bitrate TTS.
|
||
|
||
### Removed: off-charter Python runtime (`wai-neural/`)
|
||
|
||
- Moved `wai-neural/` OUT of this repo to `../../wai-neural-prototype/`.
|
||
It was a parallel **Python** runtime (`WAI4` generative-semantic
|
||
container + CLIP-embedding payloads + SD-XL-Turbo regeneration +
|
||
CLIP-similarity conformance) — a disconnected second implementation in
|
||
the wrong language. The reference implementation is **Rust** (`wai-rs`);
|
||
nothing referenced the Python package. The *idea* it proved — a
|
||
behavioral/perceptual neural path that conforms by semantic similarity
|
||
rather than a weights hash — is sound and belongs in `wai-rs` as a future
|
||
Rust path alongside the bit-exact, prior-pinned path. The Python is kept
|
||
only as a prototype reference (and it hardcodes a machine-local model
|
||
path, so it never ran portably). This does NOT touch the Python
|
||
model-export tools in `tools/`, which are legitimate dev tooling.
|
||
|
||
### Neural condition: working end-to-end
|
||
|
||
- `tools/wai_encodec_encode.py` / `wai_encodec_decode.py` — first
|
||
working **neural capability** (`wai.neural.encodec32`): source
|
||
encodes audio via locally-installed EnCodec, ships only the
|
||
quantized tokens in a standard WAI envelope (236 MB of model
|
||
weights stay on the source's disk, never enter the envelope); sink
|
||
with EnCodec installed decodes. Measured: **5 s of audio → 2.3 KB
|
||
envelope (141× compressed)**, model on the wire = 0 B.
|
||
- Rust SDK gains `wai inspect` CLI: parses an envelope, resolves the
|
||
capability + fallback against `sink_capabilities()`, and prints
|
||
verdict (`DECODABLE via primary` / `via fallback` / `INERT here`).
|
||
|
||
### Browser runtime (`wai-web/`)
|
||
|
||
- New crate: wasm-bindgen wrapper exposing `parse(bytes) → WaiEnvelope`,
|
||
`pack(manifest_json, payload)`, and `known_capabilities()`. 107 KB
|
||
wasm + 16 KB JS glue.
|
||
- `wai-web/demo/wai.js` — thin JS shim with `WaiRuntime.register(cap,
|
||
handler)` dispatch. Backends are **pluggable** (browser-native APIs,
|
||
transformers.js, WebGPU compute, WebNN, ONNX runtime web) — WAI
|
||
doesn't bundle any. The sink-side compute fabric is heterogeneous;
|
||
the deployer picks per capability.
|
||
- `wai-web/demo/index.html` — drop-and-decode demo with handlers wired
|
||
to: `createImageBitmap` for `wai.image.{png,jpeg,avif,jxl}`,
|
||
`AudioContext` for `wai.audio.{opus,flac}`, placeholders for
|
||
`wai.neural.*` showing where transformers.js / WebNN / WebGPU plug
|
||
in. Run with `python3 wai-web/demo/serve.py`.
|
||
- 4 sample `.wai` files in `wai-web/demo/samples/` covering PNG, JXL,
|
||
Opus, and EnCodec for testing.
|
||
|
||
### Spec: v1.1 multi-rendition envelope (SPEC.md §6.1)
|
||
|
||
- New magic `WAI2` for envelopes that carry multiple renditions of the
|
||
same content (e.g. EnCodec + Opus, or AVIF + JPEG-XL). Reframed from
|
||
"graceful fallback for missing capability" to **policy selection** —
|
||
in a self-contained ecosystem every sink has every required
|
||
capability; renditions exist so the sink picks by compute budget,
|
||
bandwidth, or runtime preference (browser-native vs WebGPU vs WebNN
|
||
vs native ML). v1.0 (`WAI1`) remains valid and unchanged.
|
||
|
||
## v0.3.0
|
||
|
||
**Pivot**: WAI is the **envelope + capability-dispatch + neural-shared-
|
||
prior** standard. The zeroth condition is a *menu of registered SOTA
|
||
codec capabilities* (AVIF, JPEG-XL, PNG, JPEG, Opus, FLAC, AV1, zstd,
|
||
XZ), not a custom codec. The Python prototype and all hand-rolled
|
||
codec implementations from v0.2.x were removed; Rust is the single
|
||
reference implementation and the SDK.
|
||
|
||
### Standard
|
||
|
||
- `SPEC.md` rewritten as a capability-menu spec. Mandatory floor:
|
||
`wai.image.png`, `wai.audio.flac`, `wai.text.zstd` (every conforming
|
||
sink MUST decode these). Recommended modern set: `wai.image.jxl`,
|
||
`wai.image.avif`, `wai.image.jpeg`, `wai.audio.opus`, `wai.video.av1`,
|
||
`wai.video.av1.lossless`, `wai.text.xz`. Neural capabilities
|
||
(`wai.neural.*`) remain OPTIONAL and sink-advertised.
|
||
- Container envelope (`WAI1` magic + length-prefixed manifest + length-
|
||
prefixed payload) and manifest schema are unchanged from v0.2.x.
|
||
- Conformance: bit-exact decode-equivalence with each capability's
|
||
reference library (libjxl / libavif / libopus / libFLAC / rav1e / etc.).
|
||
|
||
### Rust reference (`wai-rs/`, crate `wai` 0.3.0)
|
||
|
||
- `cargo build --release` produces `libwai.dylib`/`.so`/`.dll` (cdylib),
|
||
`libwai.a` (staticlib), and `libwai.rlib` (Rust-native). The crate
|
||
type list is `["lib", "cdylib", "staticlib"]` — drop-in for any
|
||
language's C-FFI binding.
|
||
- Codec wrappers: `image` (PNG, JPEG via `jpeg-encoder`, AVIF via
|
||
`ravif` + `libavif-image` for decode, JPEG-XL via `jpegxl-rs`), `opus`
|
||
(RFC 7845 Ogg-Opus container so output drops into ffmpeg/VLC/browsers),
|
||
`claxon`+`flac-bound` for FLAC, `rav1e`+`dav1d` for AV1 (lossy +
|
||
lossless), `zstd`+`xz2` for text.
|
||
- Generated C header: `include/wai.h` is regenerated by `build.rs` via
|
||
`cbindgen` on every build. SDK consumers get a typed header rather
|
||
than hand-writing `extern "C"` decls.
|
||
- Cross-platform linkage: `build.rs` probes `pkg-config` for libflac;
|
||
the macOS-hardcoded `.cargo/config.toml` from v0.2.x is gone. Builds
|
||
clean on macOS (Homebrew) and Linux (apt) without `RUSTFLAGS`.
|
||
|
||
### C FFI / SDK surface
|
||
|
||
All `wai_*` functions return a `wai_status_t`:
|
||
|
||
| code | meaning |
|
||
|---|---|
|
||
| `WAI_OK` (0) | success |
|
||
| `WAI_ERR_INVALID_ARG` (1) | null pointer / mismatched length / out-of-range dimension |
|
||
| `WAI_ERR_CODEC` (2) | codec-level failure (details in `wai_last_error()`) |
|
||
| `WAI_ERR_CONTAINER` (3) | bad magic / truncated envelope / unparseable manifest JSON |
|
||
| `WAI_ERR_PANIC` (4) | internal Rust panic caught at the boundary (a wrapper bug — never UB into the C caller) |
|
||
|
||
Every FFI function is wrapped in `std::panic::catch_unwind` so Rust
|
||
panics never unwind into C (UB). `av1_decode` validates header bounds
|
||
to prevent libdav1d's C-level `abort()`. `wai_last_error()` is per-
|
||
thread, populated on any non-OK return.
|
||
|
||
### Testing
|
||
|
||
- **13 lib tests**: per-codec round-trip + envelope round-trip
|
||
- **21 FFI robustness tests**: null pointers, length mismatches,
|
||
dimension overflow, malformed magic, truncated payloads, bad JSON,
|
||
invalid Opus sample rates, zero fps, garbage decoder input
|
||
- **10 fuzz smoke targets**: 1024 adversarial random byte sequences per
|
||
decoder asserting no panic. Stable Rust, no `cargo-fuzz` toolchain
|
||
needed. Found and fixed a real envelope-truncation panic.
|
||
- **`wai_bench` corpus walker**: quick mode (4 images + 4 audio + 3
|
||
text + 2 video) for spot-checks; `--full` walks the entire corpus
|
||
(169 images + 5 audio + 13 text + 12 video sequences = 588 codec
|
||
runs in ~9.4 min). Per-codec aggregate stats: runs, ok / fail counts,
|
||
WAI vs reference byte sizes, PSNR delta vs ffmpeg, encode time.
|
||
- **`--report file.json`** writes a structured bench report;
|
||
`wai_bench_diff baseline.json head.json` flags regressions on bytes
|
||
(±5% default tolerance), encode time (±25%), or new failures. Non-zero
|
||
exit on regression — ready for CI gating.
|
||
- **Hosted CI** [name removed 2026-09-30] (`.github/workflows/ci.yml`): ubuntu + macOS
|
||
matrix, apt/brew codec deps, `cargo test --release`, cdylib/staticlib
|
||
artifact verification, quick bench, JSON report uploaded as artifact.
|
||
|
||
### Honest codec measurements (vs ffmpeg reference, full Kodak/Tecnick/
|
||
CLIC/synthetic corpus = 169 images, plus SQAM audio + Silesia text + Derf CIF video)
|
||
|
||
| codec | runs | size vs ref | PSNR Δ vs ref | notes |
|
||
|---|---:|---:|---:|---|
|
||
| PNG | 169 | — | lossless | ffmpeg-interop ok |
|
||
| JPEG q=50 | 169 | +12.7% | +0.27 dB | q-knob calibrated to libjpeg |
|
||
| JPEG q=80 | 169 | +19.7% | −0.31 dB | residual gap is pure-Rust vs libjpeg-turbo asm |
|
||
| AVIF q=70 | 169 | — | — | ffmpeg-interop ok |
|
||
| JPEG-XL lossless | 169 | — | lossless | bit-exact via libjxl |
|
||
| FLAC | 5 | **−10.1%** | lossless | WAI compression_level 8 vs ffmpeg default 5 |
|
||
| Opus 64k | 5 | — | — | standard Ogg-Opus container, ffmpeg-interop ok |
|
||
| zstd-19 | 13 | ±0.0% | — | **byte-identical to system zstd** |
|
||
| xz-6 | 13 | ±0.0% | — | **byte-identical to system xz** |
|
||
| AV1 lossy | 12 | +28.3% | — | rav1e vs ffmpeg libsvtav1 (different encoders) |
|
||
|
||
All 588 corpus runs and all 44 tests pass.
|
||
|
||
### Removed
|
||
|
||
- The Python prototype (`wai/`) and Python BD-rate harness (`bench/`).
|
||
v0.2.x's hand-rolled wire format is dead; Rust is the single source
|
||
of truth.
|
||
- The `webm_lottie_new/wai-rs/` pre-pivot Rust crate (Wasm-decoder-in-
|
||
bundle model). Replaced by the clean `wai-rs/` SOTA-wrapper crate.
|
||
|
||
---
|
||
|
||
## v0.2.0 — superseded (historical record)
|
||
|
||
The v0.2.x direction was hand-rolling custom codec wire formats (DCT +
|
||
quantization + rANS for image, MDCT for audio, I/P inter-frame for
|
||
video). Two byte-interoperable reference implementations (Python +
|
||
Rust) were built and cross-validated.
|
||
|
||
**Why superseded**: re-implementing JPEG/MPEG/Opus poorly is strictly
|
||
worse than wrapping the field's SOTA libraries. WAI's actual value is
|
||
the envelope + capability dispatch + the neural-shared-prior model,
|
||
which v0.3 keeps. Custom codecs are gone; standard libraries are in.
|
||
|
||
### What v0.2 had
|
||
|
||
- `SPEC.md` defined byte-precise wire formats for image (8×8 DCT-II +
|
||
YCbCr + JPEG-Q + zigzag + DC-Δ + rANS), audio (orthonormal MDCT +
|
||
sine window + TDAC), video (I/P inter-frame with integer-pel motion).
|
||
- Python (`wai/`) + Rust (`wai-rs/`) implementations, byte-equivalent on
|
||
the lossless paths (rANS, container) and reconstruction-equivalent on
|
||
the lossy paths.
|
||
- Honest measured standing: image at JPEG-1992 parity (~5% behind
|
||
libjpeg), audio uniform-MDCT (not perceptually competitive with MP3),
|
||
video I/P at ~41% smaller than MJPEG floor but far behind H.264.
|
||
|
||
### Licensing (unchanged)
|
||
|
||
- Apache-2.0 for the standard and the reference implementation.
|
||
Royalty-free implementation is a precondition for adoption (VP9/AV1
|
||
open-codec precedent; the H.264/H.265 royalty trap is avoided).
|