Some checks failed
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
WAI conformance / does this change touch it (pull_request) Successful in 8s
WAI energy receipts / does this change touch it (pull_request) Successful in 6s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 6s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been skipped
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 5s
wai-web wasm32 / does this change touch it (pull_request) Successful in 5s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
# Conflicts: # .github/workflows/wai-conformance.yml # wai/CHANGELOG.md # wai/REGISTRY.md # wai/SPEC.md # wai/live-conformance/README.md # wai/wai-rs/Cargo.toml # wai/wai-rs/src/codecs/registry.rs # wai/wai-rs/src/lib.rs # wai/wai-rs/tests/int_kernels_vectors.rs
6208 lines
450 KiB
Markdown
6208 lines
450 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.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).
|