name: WAI energy receipts # Tests of the energy acquisition class (extensions/energy-measurement.md §2): # the class rules, the optional labels, the resolution floor, the pins that # hold legacy receipts (unlabelled, or labelled with the legacy HwShunt) and # labelled ones to the signed bytes they have; the measurement report's # arithmetic and the liveness rule (§3.1, §6.2) with their fault-injection # tests; the figure views a report is checked against, for every receipt # family the set compiles; and the reference meters' report path. Three # steps, on linux (x86_64), linux-arm64 and macos (arm64): # # 1. wai-rs: `cargo test --locked` with the feature set in the step, over # --lib, --bins and --test video_receipt. That runs the library's unit # tests under those features (energy_class, energy_report, energy_burst, # energy_meter, energy_binding, energy_figure, the label and pin tests of # the wai-rs receipt families, the video and OTT receipt tests, and — with # receipt_claims — the claim classes and jwp_delivery); the unit tests of # every bin target # the set can build (one whose required features are all on), which also # proves those bins compile; and the one integration test file # tests/video_receipt.rs. # 2. wai-quantum: `cargo test --locked --features full --lib --tests`: the # crate's unit tests, among them the pins and label tests of every quantum # receipt, and its integration tests — tests/bit_identity.rs, which pins # one result of each f64 layer so every architecture here must produce # the same bits, and tests/utility_reference.rs, which holds the 127-qubit # results to published values. The tests already marked #[ignore] stay # ignored. # 3. wai-quantum-serve: `cargo test --locked --lib`, native: the verdict # logic behind POST /verify, for a receipt in either form, and that an # unmetered receipt's 0 is rendered as the unmetered marker (null), never # as zero joules; and that /run and /receipt build in the gateset the # request names (circuit2 by default), name its capability, give the # corpus's GHZ3 hashes in each, and answer 400 for an unknown gateset or # a circuit with no circuit hash. # # It does NOT: # - check that a newly sealed receipt in a family with an optional label # (§2.8) is labelled. A verifier cannot: there an unlabelled receipt # sealed today has the bytes of a legacy one. A test of each producer's # labelling path could, and none is written; those families' # constructors take no class, and a label is added afterwards. It checks # the label rules for the receipts that carry one, and that four of the # five reference meter bins still build with their report path # (wai_video_meter, wai_meter, wai_world_meter, wai_quantum_meter). # - run a meter against a power reader. The report path is tested in the # library (energy_figure::meter_report), and the protocol against # scripted readers (energy_burst, energy_meter). # - build wai_prov (needs `codecs`, which links system libjxl; the # codec-wrappers job of wai-conformance.yml builds and runs it) or the # wai-quantum CLI bin (needs quantum_toolchain, quantum_qir, # quantum_channel, quantum_comm and quantum_qudit). Cargo skips a bin whose # required features are off without saying so. # - run any other integration test in wai/wai-rs/tests (world_model and # world_replay among them), or a doc-test of any of the three crates. # - compile c2pa_emit, which carries the energy binding into a C2PA # manifest; the c2pa-emitter job of wai-conformance.yml tests it. Nor # `oer`: an OER receipt grades its figure and carries no acquisition # class, and energy-measurement.md §1 leaves it out of scope. # - verify the JWP corpus; wai-conformance.yml does that, and joule-code's # workflow tests joule-moq against literals copied from it. # - build wai-web, or the wasm32-wasip2 component of wai-quantum-serve. # - verify the conformance corpora; wai-conformance.yml does that. # - run on Windows. The pinned bytes are integers and hashes and make no # OS-specific claim; the conformance matrix has no Windows leg either (its # runner is offline). # # It triggers only on changes under the paths listed below; a change to # wai-web alone does not trigger it. # 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/wai-rs/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-serve/**' - 'wai/extensions/**' - 'wai/SPEC.md' - 'wai/CHANGELOG.md' # tests/video_receipt.rs reads the WIV1 refusal fixtures. - 'wai/int-payload-conformance/**' # wai_quantum_vectors' tests require the committed quantum corpus to be # what `gen` writes. - 'wai/quantum-conformance/**' - '.github/workflows/wai-energy-receipts.yml' schedule: - cron: '33 6 * * *' 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-arm64 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-energy-receipts.yml \ "${{ github.event.pull_request.base.sha }}" \ "${{ github.event.pull_request.head.sha }}") echo "this change touches wai-energy-receipts: $r" echo "run=$r" >> "$GITHUB_OUTPUT" energy-receipts: needs: relevant if: needs.relevant.outputs.run == 'true' name: energy receipts · ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [linux, linux-arm64, macos] steps: - uses: actions/checkout@v5 # 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. - 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" # One feature set that compiles the wai-rs receipts that carry an # acquisition class, except the C2PA emitter (see the header): the video # and OTT receipts and their legacy pins, the energy binding, the energy # meters, the optional-label families and their pins, and the quantum # re-exports with the cross-crate encoding test. --bins compiles every # bin whose required features this set enables — among them four of the five # reference meters (wai_video_meter, wai_meter, wai_world_meter, # wai_quantum_meter), whose report path needs receipt_claims — so one of # those that stops building fails here. # The fifth, wai_prov, is not built here: it requires the `codecs` # feature, which links system libjxl (>= 0.11). Cargo skips a bin whose # required features are off without saying so. The codec-wrappers job # of wai-conformance.yml, on the macOS runner, builds it and runs its # --carry path against the committed provenance receipt. Its metering is # energy_burst, whose tests run in the lib suite. - name: wai-rs — energy classes, labels and legacy pins working-directory: wai/wai-rs shell: bash run: | cargo test --locked --no-default-features \ --features world,audio_scene,haptic,splat4d,score,provenance,film,ternary_receipt,world_model,video_receipt,video_workflow,energy_meter,quantum_receipt,quantum_ops,quantum_qbom,quantum_phasor_meter,receipt_claims \ --lib --bins --test video_receipt # wai-quantum is published separately; its receipts' legacy pins and the # labelled forms are tested in its own workspace, against its own lock, # with the integration tests that pin its f64 layers' bits on every # architecture this matrix runs. - name: wai-quantum — receipt pins, labels and bit identity working-directory: wai/wai-quantum shell: bash run: cargo test --locked --features full --lib --tests # The HTTP handler's POST /verify must accept a receipt in either form — # as sealed, or labelled as wai_quantum_meter emits it — and report the # signer's level beside validity (jwp-receipts §7.1, §7.4). The verdict # logic is a plain function, tested natively; the wasm32-wasip2 build of # the component is not part of this workflow. - name: wai-quantum-serve — POST /verify verdicts, and /run and /receipt in the requested gateset working-directory: wai/wai-quantum-serve shell: bash run: cargo test --locked --lib