The staged-measure-conformance README takes main's provenance paragraph: the int_codec audio is now coded on a generated signal, so the branch's note that it codes a recorded sound is dropped, here and in the CHANGELOG. Co-Authored-By: Claude Opus 5.5 <[email protected]>
730 lines
45 KiB
YAML
730 lines
45 KiB
YAML
name: WAI conformance
|
|
|
|
# WAI's load-bearing claim is bit-exact reconstruction on every machine,
|
|
# no tolerance parameter. This workflow enforces it mechanically: every
|
|
# cargo-class corpus — and the integer-exact neural decoders, which make the
|
|
# same promise (the registry's NeuralIntegerExact class: i64-only, no float
|
|
# ONNX) — is re-derived on every OS/arch leg of the matrix below (x86-64 and
|
|
# arm64) and must reproduce its frozen BLAKE3 hashes; and the integer
|
|
# byte-equality goldens (rANS / int_synth / JPEG-AI mANS, generated once on
|
|
# a reference machine) must decode identically everywhere. All of it is pure
|
|
# Rust (no C codec chain, no ONNX Runtime), so the matrix needs no system
|
|
# dependencies.
|
|
|
|
# One run per branch at a time. A push supersedes the run before it.
|
|
#
|
|
# There is one `macos` runner and it is the dev Mac, so every macOS job in this
|
|
# repo queues on it. With no concurrency group, a session pushing four versions
|
|
# of a branch queued four complete sets of runs and every one of them had to
|
|
# drain: on 2026-10-05 a PR's 21 contexts settled none in 70 minutes behind a
|
|
# backlog of 25 runs, and the queue grew faster than it cleared. Only the newest
|
|
# commit's verdict is wanted, so the older runs are cancelled.
|
|
#
|
|
# Verified on this forge (Forgejo 16.0.5) in `ci-test` before being added here:
|
|
# the key parses, and a newer push moves an older run to `cancelled` even while
|
|
# it is still queued, which is the case that matters.
|
|
#
|
|
# `cancel-in-progress: true` also applies to `main`. That is deliberate and
|
|
# safe here: main advances by merge commits whose tree contains the commits
|
|
# before them, so the newer run covers the older one's changes.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'wai/wai-rs/**'
|
|
# wai-rs path-depends on wai-quantum, and the quantum corpora below are
|
|
# verified through it: a change there changes what this workflow checks.
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/*-conformance/**'
|
|
- 'wai/byte-exact-conformance/**'
|
|
# The registry export and the two library tests that read files outside
|
|
# wai-rs (the codec glossary, SPEC.md) must fire this workflow too: a
|
|
# hand edit of an exported file, of SPEC §5 or of the glossary is checked
|
|
# here. vocab/v1's and v2's pin terms are checked by a library test.
|
|
- 'wai/registry/**'
|
|
- 'wai/vocab/**'
|
|
- 'wai/glossary/**'
|
|
- 'wai/SPEC.md'
|
|
- 'wai/REGISTRY.md'
|
|
# A library test checks SPEC §5's pinned integer payloads against this
|
|
# extension's sections, so an edit of it is checked here too.
|
|
- 'wai/extensions/integer-payloads.md'
|
|
# A library test (staged::tests::staged_delivery_text_lists_the_codes) checks
|
|
# that this extension's §4.2 and A.6 list the codes the sink reports.
|
|
- 'wai/extensions/staged-delivery.md'
|
|
# A library test (codecs::registry::tests::wire_identifiers_are_in_their_documents)
|
|
# checks every wire identifier against the section of these texts it names, and
|
|
# staged::moq::tests::moq_streaming_format_lists_the_codes checks §4.1's codes.
|
|
- 'wai/extensions/http-delivery.md'
|
|
- 'wai/extensions/moq-streaming-format.md'
|
|
- 'wai/extensions/jwp-receipts.md'
|
|
- 'wai/wai-web/**'
|
|
# The audio provenance check below reads the corpora above with this tool.
|
|
- 'wai/tools/wai_audio_provenance_check.py'
|
|
- '.github/workflows/wai-conformance.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-rs/**'
|
|
# wai-rs path-depends on wai-quantum, and the quantum corpora below are
|
|
# verified through it: a change there changes what this workflow checks.
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/*-conformance/**'
|
|
- 'wai/byte-exact-conformance/**'
|
|
# The registry export and the two library tests that read files outside
|
|
# wai-rs (the codec glossary, SPEC.md) must fire this workflow too: a
|
|
# hand edit of an exported file, of SPEC §5 or of the glossary is checked
|
|
# here. vocab/v1's and v2's pin terms are checked by a library test.
|
|
- 'wai/registry/**'
|
|
- 'wai/vocab/**'
|
|
- 'wai/glossary/**'
|
|
- 'wai/SPEC.md'
|
|
- 'wai/REGISTRY.md'
|
|
# A library test checks SPEC §5's pinned integer payloads against this
|
|
# extension's sections, so an edit of it is checked here too.
|
|
- 'wai/extensions/integer-payloads.md'
|
|
# A library test (staged::tests::staged_delivery_text_lists_the_codes) checks
|
|
# that this extension's §4.2 and A.6 list the codes the sink reports.
|
|
- 'wai/extensions/staged-delivery.md'
|
|
# A library test (codecs::registry::tests::wire_identifiers_are_in_their_documents)
|
|
# checks every wire identifier against the section of these texts it names, and
|
|
# staged::moq::tests::moq_streaming_format_lists_the_codes checks §4.1's codes.
|
|
- 'wai/extensions/http-delivery.md'
|
|
- 'wai/extensions/moq-streaming-format.md'
|
|
- 'wai/extensions/jwp-receipts.md'
|
|
- 'wai/wai-web/**'
|
|
# The audio provenance check below reads the corpora above with this tool.
|
|
- 'wai/tools/wai_audio_provenance_check.py'
|
|
- '.github/workflows/wai-conformance.yml'
|
|
|
|
# No global `shell:` default. The Windows runner is a host executor whose
|
|
# `bash` is the WSL stub (a different OS), so shell-specific steps below pick
|
|
# their shell per-OS: bash on unix, pwsh on Windows. Bare `cargo …` steps take
|
|
# each runner's native default, which runs cargo identically either way. The
|
|
# Windows legs are out of the matrices for now (see the comment on each matrix);
|
|
# their pwsh steps stay, inactive, so restoring a leg is a one-line change.
|
|
|
|
jobs:
|
|
cargo-classes:
|
|
name: cargo-class corpora · ${{ matrix.os }}
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# Self-hosted fleet labels. Both architectures: x86_64 by linux, arm64
|
|
# by linux-arm64 + macos — what matters for the Fx integer floor is the
|
|
# arch, not the OS. No Intel-Mac runner exists in the fleet, and it
|
|
# would add no arch coverage anyway.
|
|
# Windows x86_64 leg removed 2026-09-30: runner offline. Restore by
|
|
# re-adding `windows` here once the runner is back online.
|
|
os: [linux, linux-arm64, macos]
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
# unix: the docker runners (linux / linux-arm64) ship no Rust, so rustup
|
|
# is bootstrapped; the macOS `host` runner has it but launchd's PATH omits
|
|
# ~/.cargo/bin. dtolnay/rust-toolchain is unusable — it calls a `rustup`
|
|
# the docker image lacks.
|
|
- name: Install Rust 1.98.0 (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
rustup component add --toolchain 1.98.0 clippy
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
# Windows: a host executor with rustup already installed. Only ensure the
|
|
# pinned toolchain is present + default. pwsh, never bash (that is WSL here).
|
|
- name: Install Rust 1.98.0 (Windows)
|
|
if: matrix.os == 'windows'
|
|
shell: pwsh
|
|
run: |
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
|
|
- name: Build the cargo classes (pure Rust, no C codec chain)
|
|
working-directory: wai/wai-rs
|
|
# neural_int joins the pure-Rust set: the integer-synthesis decoder is
|
|
# i64-only (no ONNX, no float), so it carries the same bit-exact promise
|
|
# and is checkable on this matrix with no ONNX Runtime present. receipt_claims
|
|
# is in the set because the reference meters (wai_meter, wai_world_meter) write a
|
|
# measurement report by default and require it: without it they would drop out
|
|
# of this build on every leg of this matrix, without a word. staged and the integer
|
|
# media features build the staged-delivery core, its corpus tool and `wai stage`;
|
|
# stage_measure builds the staged-measurement core and wai_stage_measure; telemetry
|
|
# the delivery-telemetry mapping and wai_telemetry; http the HTTP delivery binding,
|
|
# its corpus tool and `wai http`.
|
|
run: cargo build --no-default-features --features world,audio_scene,haptic,splat4d,score,provenance,neural_int,film,receipt_claims,staged,video_int,audio_int,splat_int,stage_measure,telemetry,http
|
|
# video_package runs the package receipt's dedup tests, which content_dedup feeds.
|
|
# The dev profile keeps overflow checks on, so an arithmetic overflow in the HTTP
|
|
# binding (http_binding::tests, among them a seeded test of every parser of untrusted
|
|
# input) fails here rather than wrapping.
|
|
- name: Library unit tests (the fixed-point determinism floors)
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --lib --no-default-features --features world,audio_scene,haptic,splat4d,score,provenance,neural_int,film,oer,receipt_claims,key_discovery,staged,video_int,audio_int,splat_int,stage_measure,telemetry,video_package,http
|
|
# The JPEG AI exact decoder reproduces across platforms only if no platform math
|
|
# library is on its path. Two guards, each failing the step: clippy's
|
|
# disallowed-methods (wai-rs/clippy.toml), which each module on the decode path
|
|
# denies, rejects a listed float method however it is spelled; and
|
|
# tests/jpegai_decode_symbols.rs lists the linked decoder's symbols with `nm` and
|
|
# rejects any C math-library function it imports, whatever made the call, and any
|
|
# it defines other than the round-to-integral ones (see its doc). `-A clippy::all`
|
|
# keeps the output to that guard. Unix legs only: `nm` is not on the Windows
|
|
# runner, and for clippy the unix legs already cover both architectures.
|
|
- name: JPEG AI exact decode calls no platform math library (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
cargo clippy --lib --no-default-features --features neural_int -- -A clippy::all
|
|
cargo test --no-default-features --features world,audio_scene,haptic,splat4d,score,provenance,neural_int,film --test jpegai_decode_symbols
|
|
|
|
# The integer decoders run fast kernels (src/int_kernels.rs) that must give
|
|
# the same integer as the scalar reference kernels for every output element.
|
|
# The feature list above leaves out audio_int, video_int, world_int and
|
|
# splat_int, so without these two steps int_audio's kernel-equivalence tests
|
|
# and tests/int_kernels_vectors.rs (every committed integer-decoder vector
|
|
# decoded under the reference and under each fast policy, against its frozen
|
|
# hash) would run nowhere. `--lib` also carries the other kernels' equivalence
|
|
# modules; tests/int_kernels_geometry.rs runs edge geometry (strides up to
|
|
# usize::MAX, padding far beyond the kernel) through the public decoders
|
|
# under every policy. Both profiles, because the overflow-parity tests assert
|
|
# different things in each: with overflow checks on (dev) that the fast path panics
|
|
# where the reference panics, with them off (release) that it returns the
|
|
# same integers. All pure Rust, so every leg runs them.
|
|
# tests/int_mlicpp.rs decodes the committed wai.neural.int_mlicpp vector against
|
|
# its pinned hashes (image, every scale bucket, every symbol) under every kernel
|
|
# policy and thread count, and checks that damaged payloads are refused rather than
|
|
# overflowing — which is why it runs in both profiles too.
|
|
# tests/int_mlicpp_preflight.rs (its own binary: it installs a counting global
|
|
# allocator) checks that a header the model cannot fit is refused before the
|
|
# decode allocates anything sized from it.
|
|
# tests/int_audio.rs and tests/int_splat.rs decode the committed wai.audio.int_codec and
|
|
# wai.splat.int_codec vectors against the hashes the Python integer reference computed at
|
|
# export, and check each vector's metadata. `--lib` runs int_payload's unit tests: each
|
|
# binary payload (WIV1, WIH1, WIA1, WIS1) against the JSON it is converted from, under
|
|
# every kernel policy, and decode_int for every integer capability in this build, which
|
|
# has no `neural`. tests/int_payload.rs refuses every fixture of int-payload-conformance
|
|
# with its code, every strict prefix of every payload, and runs a byte-change sweep in
|
|
# which nothing may panic; tests/int_payload_preflight.rs (its own binary, a counting
|
|
# allocator) checks that a header past the caps is refused before anything sized from it
|
|
# is allocated; tests/int_payload_cost.rs (its own binary, a live-bytes allocator) checks
|
|
# that int_payload::declared_cost bounds what each decode holds, and, in the release
|
|
# step only, decodes a 2^30-byte clip a 3 KB payload codes; tests/int_video_enc_memory.rs
|
|
# (its own binary, the same allocator) checks that int_video_enc::working_bytes bounds
|
|
# what the encoder holds. tests/staged_resources.rs (its
|
|
# own binary, a counting allocator) checks that the staged sink's work and memory per
|
|
# layer grow linearly in the layer's units, that a layer past the chain budget is
|
|
# refused before any stream is decoded, that what no unit holds is dropped, and that a
|
|
# WIR1 parse holds no more than its declared working bytes. tests/stage_measure.rs counts every
|
|
# committed staged envelope as exactly its length, refuses JSON, base64 and another capability's
|
|
# format, and evaluates a measured title read from a map of files to its golden, refusing a
|
|
# changed byte of a layer, the base, the source or a parameter-set file (extensions/
|
|
# staged-measurement.md). Cargo.toml declares them, and the other
|
|
# feature-gated targets named here, with required-features, so if this step ever loses a
|
|
# feature, cargo stops with an error instead of compiling an empty binary that reports ok.
|
|
# Each committed vector's decoded output is also checked against the SHA-256 in
|
|
# its sha256.json sidecar (SPEC §7 *Digests*), and
|
|
# int_kernels_vectors::every_decoded_output_states_a_sha256 requires a sidecar,
|
|
# or a named exclusion, for every corpus directory. tests/generate.rs checks the
|
|
# wai.generate.spectral vector, which `generate` enables.
|
|
- name: Fast integer kernels ≡ reference kernels (overflow checks on)
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --features neural_int,video_int,audio_int,world_int,splat_int,generate,staged,stage_measure --lib --test int_kernels_vectors --test int_kernels_geometry --test int_mlicpp --test int_mlicpp_preflight --test int_audio --test int_splat --test generate --test int_payload --test int_payload_preflight --test int_payload_cost --test int_video_enc_memory --test cli_stage --test staged_resources --test stage_measure --test stage_measure_bounds
|
|
- name: Fast integer kernels ≡ reference kernels (release, overflow checks off)
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --release --no-default-features --features neural_int,video_int,audio_int,world_int,splat_int,generate,staged,stage_measure --lib --test int_kernels_vectors --test int_kernels_geometry --test int_mlicpp --test int_mlicpp_preflight --test int_audio --test int_splat --test generate --test int_payload --test int_payload_preflight --test int_payload_cost --test int_video_enc_memory --test cli_stage --test staged_resources --test stage_measure --test stage_measure_bounds
|
|
# The integer decode path builds under each of its features alone, so a sink can take
|
|
# one medium without the others, and for wasm32 (a browser sink); `int_payload` arms a
|
|
# medium only under its feature, and none needs `neural`. So does the staged-measurement
|
|
# core (`stage_measure`), which pulls no ONNX and reads no file on wasm32. The HTTP
|
|
# binding (`http`) performs no I/O, so it builds alone and for wasm32 too.
|
|
- name: Each integer feature alone, and wasm32 (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
for f in neural_int video_int audio_int splat_int staged stage_measure telemetry http; do
|
|
cargo check --lib --no-default-features --features "$f"
|
|
done
|
|
# The decode bench prints its machine line from bench_env; no other step builds it.
|
|
cargo build --no-default-features --features neural_int,video_int,audio_int,world_int,splat_int --bin wai_decode_bench
|
|
rustup target add --toolchain 1.98.0 wasm32-unknown-unknown
|
|
cargo check --lib --target wasm32-unknown-unknown --no-default-features --features video_int,audio_int,splat_int,staged,http
|
|
# The staged-measurement core is wasm32-clean: a browser computes the same figures.
|
|
# Built, not only checked, so codegen and linking of the library run for wasm32.
|
|
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features stage_measure
|
|
# With the claim classes too: a browser sink seals its reconstruct-session claim
|
|
# (jwp-receipts §2.4.12), and wai-web's measurement binding signs with them.
|
|
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features receipt_claims,stage_measure
|
|
# So is delivery telemetry: a browser sink writes the same reports.
|
|
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features telemetry
|
|
# carriage: every registered capability round-trips byte-exact. cli_wrap_intent and
|
|
# cli_registry run the `wai` binary, which builds without the codecs feature: the
|
|
# intent rule of `wai wrap`, its refusal of candidate capabilities (SPEC §5), its media
|
|
# class, and `wai registry export --check` on the tree and on a drifted copy.
|
|
# tests/manifest_builders.rs: the manifest types are #[non_exhaustive], and the
|
|
# builders write the JSON a struct literal wrote; members the types keep only
|
|
# as `extra` (and `security`) survive a round trip.
|
|
# corpus_provenance: every corpus README states where its content comes from.
|
|
- name: Carriage conformance, the wai CLI (wrap, registry export) and corpus provenance
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --test carriage --test manifest_builders --test cli_wrap_intent --test cli_registry --test corpus_provenance
|
|
# tests/cli_http.rs runs `wai http publish`, `codecs`, `origin` and `bench` as a user
|
|
# does: objects named by their content hash with their head proofs, a sequence's union
|
|
# and floor, the origin's headers, ranges and If-Range on loopback, and a fetch by
|
|
# the byte-range procedure whose record states energy as not measured; and `hls`,
|
|
# `mpd`, `seal-playlist` and `check-playlist`, whose receipts need video_manifest.
|
|
# Cargo.toml declares it with required-features = ["http"]. tests/dash_read_bounds.rs
|
|
# holds Appendix I's reader to the document's size under a counting allocator, and an
|
|
# element's attributes to linear CPU time; a binary of its own, one test.
|
|
- name: The wai http commands
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --features http,video_manifest --test cli_http --test dash_read_bounds
|
|
# tests/staged_moq.rs runs the MoQ trace of staged-conformance/moq/ (moq-streaming-format
|
|
# §4.1) with signed receipts: a chain for each track, a receipt held for its parent and
|
|
# admitted when it arrives, the relay's per-hop delivery timeout, dropped objects checked
|
|
# by inclusion and named by the relay's delivery-gap claims, and each base's stage claim,
|
|
# which seals and verifies; the subscriber of two tracks holds exactly their chains.
|
|
# Cargo.toml declares it with required-features = ["staged", "video_int", "receipt_claims"].
|
|
- name: Staged tracks over MoQ, with signed receipts
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --features staged,video_int,receipt_claims --test staged_moq
|
|
# The machine-readable registry (wai/registry/, wai/vocab/v2.*, the table in
|
|
# wai/REGISTRY.md) is generated from wai-rs/src/codecs/registry.rs. Regenerate it in
|
|
# memory and fail on any difference; the message says how to regenerate. One leg: the
|
|
# export is the same text on every platform.
|
|
- name: The registry export is up to date (linux)
|
|
if: matrix.os == 'linux'
|
|
working-directory: wai/wai-rs
|
|
run: cargo run -q --no-default-features --bin wai -- registry export --check
|
|
# The C ABI's envelope surface builds with no codec library (feature `ffi`):
|
|
# unpack returns each manifest as the envelope carries it, the WAI2 table carries
|
|
# every component's identity and selectability, wai_envelope_select follows
|
|
# SPEC §7.1 and never picks a candidate, and the packers refuse a candidate
|
|
# (SPEC §5). Cargo.toml declares the target with required-features.
|
|
- name: C ABI envelope surface (no codec library)
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --features ffi --test ffi_envelope
|
|
# The browser crate wraps the same envelope types (wai::container), so role,
|
|
# companion_of, component identity and the pin reach JS, and its pick is SPEC
|
|
# §7.1's. Its tests target the pure-Rust half (`core`), which runs natively; one
|
|
# leg suffices, the logic is architecture-independent. The JS surface itself runs
|
|
# under wasm32 in wai-web-wasm.yml.
|
|
- name: wai-web envelope surface (native tests)
|
|
if: matrix.os == 'linux'
|
|
working-directory: wai/wai-web
|
|
run: cargo test --locked
|
|
# jwp-receipts §7.1, §7.8: no `did:web`/`did:key` literal in a tracked .rs, .json or
|
|
# .js file under wai/ names a real web origin — every key the tree signs with has a
|
|
# published seed. Ungated: it reads the tree through `git ls-files` and classifies
|
|
# with `signer_id`, which builds with no feature.
|
|
- name: Every signer id in the tree is a label
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --test signer_ids_are_labels
|
|
# jwp-receipts §7: `wai-keys` builds a key log from operator-supplied seeds, appends
|
|
# revocations and rotations, signs statuses and module grants, verifies a log as
|
|
# §7.3 anchors one, and checks a DID document against it. The test drives the binary.
|
|
- name: wai-keys — build, check and publish an origin key log
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --no-default-features --features key_discovery --test wai_keys_cli
|
|
# key_log has no I/O, clock or threads, so a browser sink could run it before it
|
|
# instantiates a module (§7.9); no browser sink does yet. This step checks that it
|
|
# builds for wasm32 on the unix legs (the target is added to the pinned toolchain
|
|
# here; the Windows host runner is not set up for it).
|
|
- name: key_log builds for wasm32 (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
rustup target add --toolchain 1.98.0 wasm32-unknown-unknown
|
|
cargo check --lib --target wasm32-unknown-unknown --no-default-features --features key_discovery
|
|
|
|
# The corpus-verify loop is genuinely shell-specific (a helper that groups
|
|
# log output and propagates the first non-zero exit), so it ships once per
|
|
# shell family. The command it runs is identical; only the wrapper differs.
|
|
- name: Every cargo-class corpus reproduces its frozen hashes (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
F=world,audio_scene,haptic,splat4d,score,provenance,neural_int,film,quantum,quantum_ops,oer,receipt_claims,key_discovery,video_int,audio_int,splat_int,staged,stage_measure,telemetry,http,video_manifest
|
|
v() { echo "::group::$1"; cargo run -q --no-default-features --features "$F" --bin "$2" -- verify "$3"; echo "::endgroup::"; }
|
|
v worlds wai_world_vectors ../world-conformance
|
|
v spatial-audio wai_audio_vectors ../audio-conformance
|
|
v haptics wai_haptic_vectors ../haptic-conformance
|
|
v state-feeds wai_feed_vectors ../feed-conformance
|
|
v avatars wai_avatar_vectors ../avatar-conformance
|
|
v volumetric wai_splat4d_vectors ../volumetric-conformance
|
|
v score wai_score_vectors ../score-conformance
|
|
v int-synth wai_int_synth_vectors ../neural-conformance
|
|
v film wai_film_vectors ../film-conformance
|
|
v quantum wai_quantum_vectors ../quantum-conformance
|
|
v quantum-ops wai_quantum_ops_vectors ../quantum-ops-conformance
|
|
v oer wai_oer_vectors ../oer-conformance
|
|
v jwp wai_jwp_vectors ../jwp-conformance
|
|
v claims wai_claim_vectors ../claim-conformance
|
|
v pin wai_pin_vectors ../pin-conformance
|
|
v signers wai_signer_vectors ../signer-conformance
|
|
v int-payload wai_int_payload_vectors ../int-payload-conformance
|
|
v staged wai_staged_vectors ../staged-conformance
|
|
v http wai_http_vectors ../http-conformance
|
|
v staged-measure wai_stage_measure ../staged-measure-conformance
|
|
v telemetry wai_telemetry ../telemetry-conformance
|
|
# The pin corpus's second implementation: Python standard library only, sharing no
|
|
# code with wai-rs. One leg is enough — it checks digests and listing rules, which
|
|
# do not depend on the architecture — and the macOS host has python3.
|
|
- name: The pin corpus checks against an independent verifier (macOS)
|
|
if: matrix.os == 'macos'
|
|
shell: bash
|
|
working-directory: wai
|
|
run: python3 pin-conformance/pin_verify.py pin-conformance
|
|
# Every committed audio vector must come from a seeded generator. This check reads
|
|
# each one to PCM (each audio base's stage 0 in the staged corpus, each measured
|
|
# source over one, each spatial-audio source, every audio file under wai/) and
|
|
# fails if any correlates above 0.5, at any lag, with a recording in the reference
|
|
# set the host has: its system sound directory, or the directories named in
|
|
# WAI_AUDIO_REFERENCE_DIRS. A generated signal stays near 0.2; a lossy coding of a
|
|
# recording is near 1. A host with no reference set, or no converter to read it,
|
|
# prints why and skips: the linux legs skip, the macOS leg checks.
|
|
- name: No committed audio vector is a coding of a reference recording (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai
|
|
run: |
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
python3 tools/wai_audio_provenance_check.py
|
|
else
|
|
echo "audio provenance: SKIPPED — no python3 on this host"
|
|
fi
|
|
# OER's second implementation is portable C, not Rust. Rebuild it from source and
|
|
# require its output to match the pinned corpus byte for byte: the kernel is
|
|
# deterministic, and wai_oer_vectors above has already verified the pinned copy,
|
|
# so equality carries that verification to what the source produces today.
|
|
# `cc` with the Makefile's flags, so the step needs no `make` on the runner.
|
|
- name: OER C kernel reproduces its pinned receipts (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
working-directory: wai/oer-conformance/settle
|
|
run: |
|
|
set -euo pipefail
|
|
cc -O2 -std=c99 -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare \
|
|
-o "$RUNNER_TEMP/settle_host" host_main.c settle.c sha256.c tweetnacl.c -lm
|
|
"$RUNNER_TEMP/settle_host" "$RUNNER_TEMP/firmware_vectors.json" 400
|
|
cmp "$RUNNER_TEMP/firmware_vectors.json" ../firmware_vectors.json
|
|
- name: Every cargo-class corpus reproduces its frozen hashes (Windows)
|
|
if: matrix.os == 'windows'
|
|
shell: pwsh
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
$F = "world,audio_scene,haptic,splat4d,score,provenance,neural_int,film,quantum,quantum_ops,oer,receipt_claims,key_discovery,video_int,audio_int,splat_int,staged,stage_measure,telemetry,http,video_manifest"
|
|
function v($grp, $bin, $path) {
|
|
Write-Output "::group::$grp"
|
|
cargo run -q --no-default-features --features $F --bin $bin -- verify $path
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
Write-Output "::endgroup::"
|
|
}
|
|
v worlds wai_world_vectors ../world-conformance
|
|
v spatial-audio wai_audio_vectors ../audio-conformance
|
|
v haptics wai_haptic_vectors ../haptic-conformance
|
|
v state-feeds wai_feed_vectors ../feed-conformance
|
|
v avatars wai_avatar_vectors ../avatar-conformance
|
|
v volumetric wai_splat4d_vectors ../volumetric-conformance
|
|
v score wai_score_vectors ../score-conformance
|
|
v int-synth wai_int_synth_vectors ../neural-conformance
|
|
v film wai_film_vectors ../film-conformance
|
|
v quantum wai_quantum_vectors ../quantum-conformance
|
|
v quantum-ops wai_quantum_ops_vectors ../quantum-ops-conformance
|
|
v oer wai_oer_vectors ../oer-conformance
|
|
v jwp wai_jwp_vectors ../jwp-conformance
|
|
v claims wai_claim_vectors ../claim-conformance
|
|
v pin wai_pin_vectors ../pin-conformance
|
|
v signers wai_signer_vectors ../signer-conformance
|
|
v int-payload wai_int_payload_vectors ../int-payload-conformance
|
|
v staged wai_staged_vectors ../staged-conformance
|
|
v http wai_http_vectors ../http-conformance
|
|
v staged-measure wai_stage_measure ../staged-measure-conformance
|
|
v telemetry wai_telemetry ../telemetry-conformance
|
|
|
|
# The generated v2 vocabulary (wai/vocab/v2.*) is checked as a consumer uses it:
|
|
# sample manifests, typed and untyped, WAI1 and WAI2, are expanded through the
|
|
# JSON-LD context exactly as written on the wire and validated against the SHACL
|
|
# shapes, each with the result it must give (a candidate in any place is
|
|
# rejected; a media value no revision lists is accepted). One leg: RDF tooling,
|
|
# not arithmetic. The tool versions are pinned.
|
|
vocab-shapes:
|
|
name: v2 vocabulary · manifests validate as stated
|
|
runs-on: linux
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
- name: Install the RDF tools
|
|
shell: bash
|
|
run: python -m pip install rdflib==7.6.0 PyLD==3.3.0 pyshacl==0.40.1
|
|
- name: Sample manifests against vocab/v2.shapes.ttl
|
|
shell: bash
|
|
run: python wai/vocab/check_shapes.py
|
|
|
|
# The corpora above are generated and verified by wai-rs, one implementation. These are
|
|
# the second implementations, written from the extension text in Python and sharing no
|
|
# code with wai-rs: claim_verify.py (every claim vector, the canonical fixture, and the
|
|
# bracket arithmetic of energy-measurement §6.2 recomputed with Python's integers),
|
|
# differential.py (both verifiers' verdicts on generated inputs the corpus does not hold,
|
|
# under a fixed seed, so a run is reproducible), jwp_verify.py (every JWP vector), and
|
|
# signer_verify.py (every key-discovery vector and the canonical fixture, with its
|
|
# verdicts required to equal wai_signer_vectors' line for line), and
|
|
# int_payload_verify.py (the integer payloads and their refusal fixtures), and
|
|
# staged_verify.py (the staged-delivery corpus), and http_verify.py (the HTTP delivery
|
|
# corpus).
|
|
# Each exits non-zero on any failure or any difference, and the step fails with it.
|
|
# One platform: what they check is the reading of the text, not the architecture.
|
|
independent-verifiers:
|
|
name: independent verifiers · claims, JWP, key discovery and integer payloads (Python)
|
|
runs-on: linux
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Install Rust 1.98.0
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
|
|
# The verifiers check Ed25519's equation with the `cryptography` package and make the
|
|
# rest of jwp-receipts §3.1 step 3 themselves.
|
|
- name: Install the Ed25519 library
|
|
shell: bash
|
|
run: python -m pip install cryptography
|
|
|
|
# differential.py drives wai_claim_vectors' `verdicts` mode; the key-discovery
|
|
# verdicts are compared with wai_signer_vectors'.
|
|
- name: Build the corpus verifiers wai-rs ships
|
|
working-directory: wai/wai-rs
|
|
shell: bash
|
|
run: cargo build --no-default-features --features key_discovery --bin wai_claim_vectors --bin wai_jwp_vectors --bin wai_signer_vectors
|
|
|
|
# The integer payload corpus's second implementation (int-payload-conformance/README):
|
|
# it parses every payload with the extension's rules and check order, converts every
|
|
# JSON source independently and requires the committed bytes, decodes the WIV1
|
|
# keyframe-0 payloads in full to their sidecars' SHA-256, and checks every refusal
|
|
# fixture. Standard library only, so it needs nothing installed.
|
|
- name: int_payload_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: python3 int-payload-conformance/int_payload_verify.py int-payload-conformance
|
|
# The staged-delivery corpus's second implementation (staged-conformance/README): it
|
|
# replays every case from its stage-0 buffers through its own reading of the
|
|
# extension (refines, WIR1, the sink's steps, holds, batches, conflicts, deadlines,
|
|
# tiers, stage hashes), decodes the WIV1 keyframe-0, PNG and FLAC bases, explodes the
|
|
# WAI2 files, and replays the schedule. Standard library only.
|
|
- name: staged_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: python3 staged-conformance/staged_verify.py staged-conformance
|
|
# The HTTP delivery corpus's second implementation (http-conformance/README): the head,
|
|
# the head proof (every committed proof rebuilt from the text), response checks, plans,
|
|
# selection, the byte-range procedure and its fallback, the codecs union and floor, the
|
|
# playlists (read, rendered, held adjacent, the classical master unchanged), playlist
|
|
# receipts (Ed25519 from RFC 8032) and the live edge, and the adaptation policies,
|
|
# checked first against published BLAKE3 vectors. Standard library only. Its
|
|
# mutations and the differential against wai-rs run in wai-http.yml's falsify job.
|
|
- name: http_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: python3 http-conformance/http_verify.py http-conformance
|
|
# Outside the corpus: wai_http_vectors fuzz writes seeded cases (Content-Range values,
|
|
# codecs values, #WAI-LAYER lines, manifests and their member types, encryption,
|
|
# refines, layers declared against their bases, layers over WAI2 components, objects
|
|
# past the first request through a tampering origin; playlists, adjacent pairs,
|
|
# published masters, renderings, adaptation states, the integer log and changed
|
|
# receipt files) with wai-rs's outcomes, and
|
|
# http_verify.py must give every one, a refusal by the same code. It prints each
|
|
# family's count of cases and of refusals. Two fixed seeds, so a run is reproducible.
|
|
- name: The HTTP differential outside the corpus (wai-rs against http_verify.py)
|
|
working-directory: wai
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
(cd wai-rs && cargo build --no-default-features --features http,video_manifest --bin wai_http_vectors)
|
|
for seed in 11 12; do
|
|
wai-rs/target/debug/wai_http_vectors fuzz http-conformance --seed "$seed" --count 400 --out "$RUNNER_TEMP/http-fuzz-$seed.json"
|
|
python3 http-conformance/http_verify.py cases "$RUNNER_TEMP/http-fuzz-$seed.json" http-conformance
|
|
done
|
|
# The staged-measurement corpus's second implementation (staged-measure-conformance/README):
|
|
# it counts every object, applies every layer from its base's stage 0, computes the quality
|
|
# integers, the output tree, the prefixes, the overhead points and the dedup totals itself,
|
|
# and requires its decode to be the fixture tool's and its figures to be goldens.json's.
|
|
# Standard library only.
|
|
- name: measure_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: python3 staged-measure-conformance/measure_verify.py staged-measure-conformance
|
|
# The delivery simulator's second implementation: Appendix B from the text, every case
|
|
# of staged-measure-conformance/sim/ reproduced and its claim checked, with a
|
|
# counter-case required for each part of the stall bound a schedule can lose.
|
|
- name: sim_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: python3 staged-measure-conformance/sim_verify.py staged-measure-conformance
|
|
# chain_differential.py signs receipt chains around jwp-receipts §3 step 3 and §6.1
|
|
# (two keys, two tracks, both payloads, replays, forks, gaps, the guard and the
|
|
# freshness bound), and chain_gap_differential.py genuine chains up to 90 groups
|
|
# delivered shuffled, withheld and walked back with rewritten links and forks; both
|
|
# require wai_jwp_vectors and jwp_verify.py to give every chain the same verdict. Two
|
|
# fixed seeds each, so a run is reproducible.
|
|
- name: claim_verify.py, differential.py, jwp_verify.py, chain_differential.py, chain_gap_differential.py, signer_verify.py
|
|
working-directory: wai
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
python3 claim-conformance/claim_verify.py claim-conformance/vectors.json
|
|
python3 claim-conformance/differential.py "$GITHUB_WORKSPACE/wai/wai-rs/target/debug/wai_claim_vectors" --random 2000 --seed 1
|
|
python3 jwp-conformance/jwp_verify.py jwp-conformance/vectors.json
|
|
for seed in 1 2; do
|
|
python3 jwp-conformance/chain_differential.py "$GITHUB_WORKSPACE/wai/wai-rs/target/debug/wai_jwp_vectors" --n 3000 --seed "$seed"
|
|
python3 jwp-conformance/chain_gap_differential.py "$GITHUB_WORKSPACE/wai/wai-rs/target/debug/wai_jwp_vectors" --n 400 --seed "$seed"
|
|
done
|
|
python3 signer-conformance/signer_verify.py signer-conformance/vectors.json
|
|
python3 signer-conformance/signer_verify.py verdicts signer-conformance/vectors.json > "$RUNNER_TEMP/signer-py.tsv"
|
|
"$GITHUB_WORKSPACE/wai/wai-rs/target/debug/wai_signer_vectors" verdicts signer-conformance/vectors.json > "$RUNNER_TEMP/signer-rs.tsv"
|
|
diff "$RUNNER_TEMP/signer-py.tsv" "$RUNNER_TEMP/signer-rs.tsv"
|
|
|
|
# Delivery telemetry's second implementation is an independent open-source CMCD and CMSD
|
|
# library at locked versions (telemetry-conformance/cml/package-lock.json). The
|
|
# differential builds each observation's report from the extension's tables, encodes it
|
|
# with the library, requires the bytes of expected/cmcd.json and expected/cmsd.json, reads
|
|
# the expected strings back as dictionary, query and header shards, and validates every
|
|
# report with no error. One platform: string encoding, not arithmetic.
|
|
telemetry-differential:
|
|
name: delivery telemetry · CMCD and CMSD against an independent library
|
|
runs-on: linux
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
- name: npm ci, then the differential
|
|
working-directory: wai/telemetry-conformance/cml
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
npm ci --no-audit --no-fund
|
|
node differential.mjs ..
|
|
|
|
byte-exact:
|
|
name: cross-arch byte-equality · ${{ matrix.os }}
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# Self-hosted fleet labels. Both architectures: x86_64 by linux, arm64
|
|
# by linux-arm64 + macos — what matters for the Fx integer floor is the
|
|
# arch, not the OS. No Intel-Mac runner exists in the fleet, and it
|
|
# would add no arch coverage anyway.
|
|
# Windows x86_64 leg removed 2026-09-30: runner offline. Restore by
|
|
# re-adding `windows` here once the runner is back online.
|
|
os: [linux, linux-arm64, macos]
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Install Rust 1.98.0 (unix)
|
|
if: matrix.os != 'windows'
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: Install Rust 1.98.0 (Windows)
|
|
if: matrix.os == 'windows'
|
|
shell: pwsh
|
|
run: |
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
|
|
- name: Integer rANS + int_synth + JPEG-AI mANS goldens (one machine → every machine)
|
|
working-directory: wai/byte-exact-conformance
|
|
run: cargo test --release
|
|
|
|
# The C2PA emitter is gated behind an off-by-default feature, so the four-platform
|
|
# job above — which builds a fixed feature list — never compiles it. A feature no
|
|
# workflow enables has no CI coverage at all: the suite passes, and the tests in it
|
|
# simply do not run.
|
|
#
|
|
# One platform, deliberately. Everything the matrix above exists to prove is about
|
|
# byte-identical reconstruction across architectures; a signed manifest makes no
|
|
# such claim and would not become more true for being emitted on four machines. What
|
|
# this job proves is that the manifest is still well-formed, still carries the three
|
|
# assertions, and still validates — which is architecture-independent.
|
|
c2pa-emitter:
|
|
name: C2PA manifest emission · signed, read back, validated
|
|
runs-on: linux
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install Rust 1.98.0
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
# `rust_native_crypto` rather than the SDK's default openssl backend, so this
|
|
# job needs no system libraries — the same property the matrix above depends on.
|
|
# Asserted, not assumed: an openssl-linked build would still pass the tests here
|
|
# while quietly making the crate undistributable to a bare runner.
|
|
- name: The emitter pulls in no system crypto
|
|
working-directory: wai/wai-rs
|
|
shell: bash
|
|
run: |
|
|
for dep in openssl openssl-sys; do
|
|
if cargo tree --no-default-features --features c2pa_emit -i "$dep" >/dev/null 2>&1; then
|
|
echo "::error::$dep reached the c2pa_emit dependency graph"
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "no system crypto in the graph"
|
|
|
|
- name: Emit, sign, read back, validate
|
|
working-directory: wai/wai-rs
|
|
run: cargo test --lib --no-default-features --features c2pa_emit c2pa_emit
|
|
|
|
# The feature must stay optional. If `provenance` ever pulls c2pa in, every
|
|
# consumer that wanted only the binding types inherits a large dependency tree
|
|
# they did not ask for, and the "off by default" claim in the extension is false.
|
|
- name: The feature stays optional
|
|
working-directory: wai/wai-rs
|
|
shell: bash
|
|
run: |
|
|
if cargo tree --no-default-features --features provenance -i c2pa >/dev/null 2>&1; then
|
|
echo "::error::c2pa is reachable from the provenance feature; it must stay opt-in"
|
|
exit 1
|
|
fi
|
|
cargo build --no-default-features --features provenance
|
|
echo "provenance builds without c2pa"
|