name: wai cross-architecture byte-equality # The `replicate` determinism contract, checked on real silicon. WAI claims its # integer-exact decode paths (rANS entropy coder, integer fixed-point synthesis) # produce BYTE-IDENTICAL output on every machine. This matrix proves it: every # OS/arch leg of the matrix below decodes the same fixtures and asserts the same golden # bytes — rANS streams produced by CompressAI's upstream encoder, and an # int_synth output hash produced on the reference machine (macOS arm64). One # diverging byte anywhere fails the suite. # # Coverage is still-image AND temporal, classical AND learned: the rANS coder, # integer fixed-point synthesis, the JPEG-AI entropy stage, the inter-frame video # decoder (`wai.video.int_motion`), and the neural-keyframe variant # (`wai.video.int_hyper`) whose I-frame comes out of a learned conditional codec, # and the MLIC++-topology codec (`wai.neural.int_mlicpp`), whose entropy network runs # before every checkerboard pass and must give every platform the encoder's scale # bucket for every element. The learned integer audio and Gaussian-splat attribute codecs # (`wai.audio.int_codec`, `wai.splat.int_codec`) decode their committed WIA1 and WIS1 # payloads to their PCM and attribute digests. The binary payloads SPEC §5 pins (WIV1, # WIH1, WIA1, WIS1) are parsed # with the wire parser a sink runs and decoded to the same digests, and the staged-delivery # residual layers (WIR1) of the staged corpus are applied stage by stage to pinned digests. # Video is the strictest leg — inter-frame decode is a # feedback loop, so one divergent half-pel rounding does not stay one byte, it # predicts the next frame and compounds for the rest of the clip. # # The conformance crate (`wai/byte-exact-conformance`) #[path]-includes the # pure-integer sources from wai-rs — no system codec libraries, no ONNX runtime # — so every leg runs in about a minute. # 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: pull_request: paths: - 'wai/byte-exact-conformance/**' - 'wai/wai-rs/src/neural/int_synth.rs' - 'wai/wai-rs/src/neural/jpegai_mans.rs' # Every source the crate #[path]-includes must be able to fire this # workflow, or a change can break cross-arch equality and merge green. - 'wai/wai-rs/src/int_synth.rs' - 'wai/wai-rs/src/int_entropy.rs' - 'wai/wai-rs/src/jpegai_mans.rs' - 'wai/wai-rs/src/rans.rs' - 'wai/wai-rs/src/int_video.rs' - 'wai/wai-rs/src/int_transform.rs' - 'wai/wai-rs/src/int_hyper_synth.rs' - 'wai/wai-rs/src/int_mlicpp.rs' - 'wai/wai-rs/src/int_audio.rs' - 'wai/wai-rs/src/int_splat.rs' - 'wai/wai-rs/src/int_payload/wire.rs' - 'wai/wai-rs/src/staged/predict.rs' - 'wai/wai-rs/src/staged/wir1.rs' - 'wai/wai-rs/src/staged/wir1_arith.rs' - 'wai/staged-conformance/**' # The fast kernels int_synth, int_transform and int_hyper_synth dispatch to # by default: the goldens here decode through them. - 'wai/wai-rs/src/int_kernels.rs' # The integer operator set and the bridge to it: the goldens decode int_synth, # the audio and splat codecs, the conditional codec (WIH1, the WIV1 keyframe) # and the MLIC++-topology codec through their lowerings too. - 'wai/wai-rs/src/int_ops.rs' - 'wai/wai-ops/**' - 'wai/wai-rs/tests/vectors/int_video/**' - 'wai/wai-rs/tests/vectors/int_hyper/**' - 'wai/wai-rs/tests/vectors/int_mlicpp/**' - 'wai/wai-rs/tests/vectors/int_audio/**' - 'wai/wai-rs/tests/vectors/int_splat/**' - '.github/workflows/wai-byte-equality.yml' push: branches: - main paths: - 'wai/byte-exact-conformance/**' - 'wai/wai-rs/src/neural/int_synth.rs' - 'wai/wai-rs/src/neural/jpegai_mans.rs' # Every source the crate #[path]-includes must be able to fire this # workflow, or a change can break cross-arch equality and merge green. - 'wai/wai-rs/src/int_synth.rs' - 'wai/wai-rs/src/int_entropy.rs' - 'wai/wai-rs/src/jpegai_mans.rs' - 'wai/wai-rs/src/rans.rs' - 'wai/wai-rs/src/int_video.rs' - 'wai/wai-rs/src/int_transform.rs' - 'wai/wai-rs/src/int_hyper_synth.rs' - 'wai/wai-rs/src/int_mlicpp.rs' - 'wai/wai-rs/src/int_audio.rs' - 'wai/wai-rs/src/int_splat.rs' - 'wai/wai-rs/src/int_payload/wire.rs' - 'wai/wai-rs/src/staged/predict.rs' - 'wai/wai-rs/src/staged/wir1.rs' - 'wai/wai-rs/src/staged/wir1_arith.rs' - 'wai/staged-conformance/**' # The fast kernels int_synth, int_transform and int_hyper_synth dispatch to # by default: the goldens here decode through them. - 'wai/wai-rs/src/int_kernels.rs' # The integer operator set and the bridge to it: the goldens decode int_synth, # the audio and splat codecs, the conditional codec (WIH1, the WIV1 keyframe) # and the MLIC++-topology codec through their lowerings too. - 'wai/wai-rs/src/int_ops.rs' - 'wai/wai-ops/**' - 'wai/wai-rs/tests/vectors/int_video/**' - 'wai/wai-rs/tests/vectors/int_hyper/**' - 'wai/wai-rs/tests/vectors/int_mlicpp/**' - 'wai/wai-rs/tests/vectors/int_audio/**' - 'wai/wai-rs/tests/vectors/int_splat/**' - '.github/workflows/wai-byte-equality.yml' jobs: # Does this pull request's own diff touch the paths below? This forge matches a # workflow's `paths:` against the push, so a merge of main into a branch fires the # workflow whatever the pull request changes; every job here waits on this answer. # A push to main, a schedule, or anything the script cannot determine answers # `true` (scripts/pr-touches-paths.py). relevant: name: does this change touch it runs-on: linux outputs: run: ${{ steps.gate.outputs.run }} steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - id: gate run: | set -euo pipefail r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-byte-equality.yml \ "${{ github.event.pull_request.base.sha }}" \ "${{ github.event.pull_request.head.sha }}") echo "this change touches wai-byte-equality: $r" echo "run=$r" >> "$GITHUB_OUTPUT" byte-equality: needs: relevant if: needs.relevant.outputs.run == 'true' name: ${{ matrix.label }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # Self-hosted fleet labels, not hosted images. macos-x86_64 is # dropped: no Intel-Mac runner exists in the fleet, and by the same # argument the cargo-classes matrix already makes, it adds no arch # coverage — x86_64 is held by linux-x86_64 and windows-x86_64, arm64 # by linux-aarch64 and the macos-arm64 reference. # windows-x86_64 was removed on 2026-09-30 while its runner was offline, and # is back now that the runner is (a `windows` job passed in run 19310). Not a # required status, as wai-quantum-webgpu.yml's member-windows is not: like every # leg here it runs only when the `relevant` gate above answers true. A job on an # offline runner stays pending and never settles, so if the runner goes offline # again the leg comes out of this list until it is back. # The two disk steps below come in a bash form for the unix legs and a pwsh form # for Windows, whose `bash` is the WSL stub. include: - { os: linux, label: linux-x86_64 } - { os: linux-arm64, label: linux-aarch64 } - { os: macos, label: macos-arm64 (reference) } - { os: windows, label: windows-x86_64 } env: # The floor of the disk check below, in GiB. The leg writes under 1 GiB (a # shallow checkout of about 0.3 GiB plus its pack, and a release target under # 0.1 GiB, measured 2026-11-03), and a cold toolchain install about 1 GiB more # under HOME; the rest is room for the jobs that share the disk. Measured with the # step's own loop on 2026-11-04, every unix label had 50 GiB or more free under all # four directories (the Windows host was not measured), so the floor fails a leg # only on a host within a few GiB of full. The macOS runner's HOME and TMPDIR sit # on its internal disk, which has gone from 7 GiB free to 118 MiB within hours, and # at that level its jobs failed writing under HOME. DISK_FLOOR_GIB: '4' steps: # Infrastructure first, before the checkout writes anything. Main's run 17676 # failed on both arm64 legs with "No space left on device", linux-aarch64 in # `git fetch` and macOS in the toolchain's temporary files: the hosts' disks were # full and no fixture was decoded, yet the matrix read as a byte-equality failure. # This step measures the free space under every directory the leg writes to (the # workspace, the runner's temporary directory, TMPDIR, and HOME, where the # toolchain lives), and writes and syncs 32 MiB once on each filesystem, which a # disk out of inodes or remounted read-only fails though df shows room. Below the # floor, or on a write refused for space, an I/O error or a read-only filesystem, # it fails, and its name and its error say infrastructure. # # On linux-aarch64 the four directories are on the runner VM's data disk, so df # reads the guest's free space, not the host volume's. The disk is an image file # on that volume, and on 2026-11-04 all but 0.7 of its 200 GiB were already # allocated there, so a full host volume could take back at most that much; the # write probe catches the rest. # # The forge still reports the leg's own status context as failed (`wai # cross-architecture byte-equality /