open-standards/.github/workflows/wai-conformance.yml
Transaction Science 23d8d31f65
Some checks failed
smart-byte conformance gate / Validate conformance JSON files (pull_request) Has been cancelled
smart-byte conformance gate / Second implementation reproduces every vector (pull_request) Has been cancelled
smart-byte conformance gate / Spec changes require vector-pack updates (pull_request) Has been cancelled
smart-byte conformance gate / VERSIONS.md and README.md must exist (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Has been cancelled
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Has been cancelled
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Has been cancelled
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (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 / 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 / C2PA manifest emission · signed, read back, validated (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 formal verification (integer core) / kani (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been cancelled
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Has been cancelled
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Has been cancelled
WAI section references / cited sections exist (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
ci: one run per branch at a time — no workflow declared a concurrency group
Not one of this repo's 15 workflows declared `concurrency:`, so every push
queued a complete set of runs and nothing superseded anything. A session
pushing four versions of a branch queued four complete sets, and all 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 backlog grew faster than it cleared —
0.3.31 through 0.3.34 of one branch, four full sets, when only the newest
commit's verdict was wanted.

Every workflow now groups on `${{ github.workflow }}-${{ github.ref }}` with
`cancel-in-progress: true`.

**Verified before being added here, not assumed.** An unparseable workflow
would mean no CI at all, which is a worse failure than a slow queue, and none
of the fleet's existing `concurrency:` users are on this forge — they are
GitHub-hosted, so they prove GitHub parses the key, not Forgejo. Pushed a probe
to `ci-test` on this forge (Forgejo 16.0.5) instead: runs were created, so the
key parses; and a newer push moved two older runs to `cancelled` **while they
were still queued**, which is exactly the case that matters here.

`cancel-in-progress: true` applies to `main` too. That is deliberate: main
advances by merge commits whose tree contains the commits before them, so a
newer run covers an older one's changes.

## The queue's real shape, which is worse than one busy runner

Worth recording because it decides where the next fix goes. The workspace
matrix asks for three labels:

  - **`linux` — 5 workspaces** (jcp-rs, sandbox-rs, smart-byte-rs, proof-rs,
    eoc-rs) → jetson-hub, which the forge reports idle and connected.
  - **`linux-arm64` — 7 workspaces** (openpay, joule-code-rs, map-rs, aac-rs,
    joulecontract-rs, mesh-rs, jouleclaw-rs) → the colima `txarm` VM, **on the
    dev Mac**.
  - **`macos` — 5 workspaces** plus the dedicated `macOS energy backends` job
    → **the dev Mac**.

So **13 of 21 contexts land on the dev Mac** through two runners, while that
same machine was carrying another session's VVC/VTM encodes at load average
132 on 18 CPUs. That is why `jcp-rs` and `eoc-rs` returned quickly — they are
the two on jetson-hub — while `mesh-rs` (linux-arm64) took 25 minutes and the
macOS job never ran at all.

Concurrency groups cut the number of queued runs, which helps every label. They
do not change the fact that one host serves 13 of 21 contexts. The structural
options are to move some `linux-arm64` workspaces onto jetson-hub's `linux`, or
to stand up a second arm64/macOS host — the Mac Studio M3 Ultra is online and
idle — and that is a fleet change rather than a repo one, so it is flagged here
rather than attempted.

No regression guard is added. A check that every workflow declares the key
would itself need a CI job on the runner this change exists to unblock, and an
unwired check is worse than none.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-05 12:01:57 -04:00

566 lines
33 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'
- 'wai/wai-web/**'
- '.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'
- 'wai/wai-web/**'
- '.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`.
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
- 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
# 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/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. 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 --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 cli_stage --test staged_resources
- 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 --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 cli_stage --test staged_resources
# 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`.
- 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; do
cargo check --lib --no-default-features --features "$f"
done
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
# 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.
- name: Carriage conformance and the wai CLI (wrap, registry export)
working-directory: wai/wai-rs
run: cargo test --no-default-features --test carriage --test manifest_builders --test cli_wrap_intent --test cli_registry
# 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
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
# 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
# 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"
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
# 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).
# 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
- name: claim_verify.py, differential.py, jwp_verify.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
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"
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"