All checks were successful
smart-byte conformance gate / Validate conformance JSON files (pull_request) Successful in 3s
smart-byte conformance gate / Second implementation reproduces every vector (pull_request) Successful in 4s
smart-byte conformance gate / Spec changes require vector-pack updates (pull_request) Successful in 3s
smart-byte conformance gate / VERSIONS.md and README.md must exist (pull_request) Successful in 3s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 1m41s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m5s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 4s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m55s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m26s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m13s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m11s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m39s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 23s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 3m18s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 12s
WAI conformance / independent verifiers · claims, JWP and key discovery (Python) (pull_request) Successful in 2m10s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 28s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 47s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m5s
wai formal verification (integer core) / kani (pull_request) Successful in 4m15s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m1s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Successful in 1m6s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 45s
WAI section references / cited sections exist (pull_request) Successful in 9s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 3m41s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m37s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 16m3s
standards workspaces build + test / aac-rs (pull_request) Successful in 5m33s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 4m18s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 3m7s
standards workspaces build + test / map-rs (pull_request) Successful in 5m44s
standards workspaces build + test / mesh-rs (pull_request) Successful in 2m36s
standards workspaces build + test / openpay (pull_request) Successful in 12m8s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Successful in 1m19s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 8m29s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m11s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m30s
standards workspaces build + test / arl-rs (pull_request) Successful in 2m20s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 2m56s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 2m57s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 2m47s
standards workspaces build + test / sense-rs (pull_request) Successful in 2m50s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 4m21s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 30s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 4m57s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 28s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 1m26s
jcp-energy `nvml`, eoc-meter `cuda` and jouleclaw-energy `nvml` are features no workspace member enables, so `cargo test --workspace` never compiled the NVML backends, nor ran the tests behind them. That includes #129's plausibility rule, which marks a counter that runs past its board Implausible. A matrix field `feature_tests` now runs those crates once more with the feature on. In the container there is no GPU and powercap is masked, so the hardware tests take their "unavailable, and the oracle agrees" branch. The unit tests behind the features run in full. Co-Authored-By: Claude Opus 5.5 <[email protected]>
332 lines
17 KiB
YAML
332 lines
17 KiB
YAML
name: standards workspaces build + test
|
|
|
|
# Build gate for every Rust workspace in the repo.
|
|
#
|
|
# It started as three. Then a toolchain bump compiled the rest for the first
|
|
# time and found FIVE more that did not build — smart-byte (47k LOC), openpay
|
|
# (six separate breakages across five crates), eoc, mesh and joule-ui — all
|
|
# broken by the same handful of upstream migrations (hmac -> KeyInit, reqwest
|
|
# feature-gating, cipher 0.4 -> 0.5, a struct gaining fields), and all invisible
|
|
# because nothing ever compiled them. Partial coverage is how that happens, so
|
|
# the matrix now covers everything.
|
|
#
|
|
# The original case, still the clearest: smart-byte-rs had never been compiled by
|
|
# anything, and it did not build. A single wrong `rand` pin — 0.10 against code
|
|
# written for 0.8, whose crypto stack requires rand_core 0.6 — silently disabled
|
|
# the entire identity pillar, BBS+ included. `cargo check` would have caught it
|
|
# on day one. An unbuilt standard is not a standard, so this workflow is the
|
|
# floor under every claim the specs make.
|
|
#
|
|
# One workflow with a matrix rather than sixteen files: the bootstrap step is
|
|
# identical everywhere, and a shared dependency change should rebuild all of
|
|
# them anyway. `fail-fast: false` so one red workspace does not mask the others.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
# Deliberately NO `paths:` filter.
|
|
#
|
|
# The first attempt used `**/*.rs` and `**/Cargo.toml`, and the workflow then
|
|
# silently did not fire — pushes touching exactly those files triggered other
|
|
# workflows but not this one, so `**`-prefixed globs evidently do not match here
|
|
# the way they do on GitHub. A build gate that quietly fails to run is worse than
|
|
# no gate at all: it reports nothing and everyone assumes green.
|
|
#
|
|
# This is the repo-wide floor, so it runs on every push and every PR. The cost is
|
|
# CI minutes on doc-only changes. The alternative cost is what this branch spent
|
|
# a day fixing — five workspaces silently broken because nothing compiled them.
|
|
|
|
jobs:
|
|
build:
|
|
name: ${{ matrix.name }}
|
|
# Spread across the fleet's runners rather than pinning every job to `linux`.
|
|
# For most of these the runner is a capacity choice, not a correctness one —
|
|
# and 17 jobs on a single runner queue behind whatever else is using it.
|
|
# `jouleclaw-rs` is the exception: see its matrix entry.
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { name: jcp-rs, dir: jcp/jcp-rs, runner: linux, conformance: jcp-conformance, feature_tests: "-p jcp-energy --features nvml" }
|
|
- { name: sandbox-rs, dir: sandbox/sandbox-rs, runner: linux, conformance: sandbox-conformance }
|
|
- { name: smart-byte-rs, dir: smart-byte/smart-byte-rs, runner: linux }
|
|
- { name: openpay, dir: openpay, runner: linux-arm64 }
|
|
- { name: joule-code-rs, dir: joule-code/joule-code-rs, runner: linux-arm64, z3: true }
|
|
- { name: map-rs, dir: map/map-rs, runner: linux-arm64, conformance: map-conformance }
|
|
- { name: aac-rs, dir: aac/aac-rs, runner: linux-arm64, conformance: aac-conformance }
|
|
- { name: joulecontract-rs, dir: joulecontract/joulecontract-rs, runner: linux-arm64, z3: true }
|
|
- { name: jouledesk-rs, dir: jouledesk/jouledesk-rs, runner: macos }
|
|
- { name: arl-rs, dir: arl/arl-rs, runner: macos }
|
|
- { name: sense-rs, dir: sense/sense-rs, runner: macos }
|
|
- { name: proof-rs, dir: proof/proof-rs, runner: linux, z3: true, conformance: proof-conformance }
|
|
- { name: joulehook-rs, dir: joulehook/joulehook-rs, runner: macos }
|
|
- { name: eoc-rs, dir: eoc/eoc-rs, runner: linux, feature_tests: "-p eoc-meter --features cuda" }
|
|
- { name: mesh-rs, dir: mesh/mesh-rs, runner: linux-arm64 }
|
|
- { name: joule-ui-rs, dir: joule-ui/joule-ui-rs, runner: macos }
|
|
# jouleclaw-rs is the one entry whose runner IS a correctness choice.
|
|
# It reaches candle → ug → gemm, which uses inline aarch64 assembly
|
|
# (`fmla {0:v}.8h, ...`) requiring the ARMv8.2 FP16 extension.
|
|
# `aarch64-apple-darwin` carries target_feature="fp16" in its baseline
|
|
# because Apple Silicon guarantees it; `aarch64-unknown-linux-gnu`
|
|
# baselines at ARMv8.0-A and does not, so the same asm fails to
|
|
# ASSEMBLE — a codegen error `cargo check` never reaches. Verified on a
|
|
# native arm64 Linux container (rustc 1.98.0): without the flag, 12
|
|
# errors; with it, 4457 tests pass.
|
|
#
|
|
# CAPACITY: this is by far the largest entry — its target tree is
|
|
# ~16 GB, against a 120 GB volume on tx-dev-linux-arm64 that already
|
|
# holds ~74 GB of other workspaces' caches. If linux-arm64 jobs start
|
|
# failing for no apparent reason, check `df -h /mnt/lima-colima-txarm`
|
|
# on the txarm VM before suspecting the code: a full cache volume
|
|
# fails whatever happens to be building, not whatever changed.
|
|
- { name: jouleclaw-rs, dir: jouleclaw/jouleclaw-rs, runner: linux-arm64, rustflags: "-C target-feature=+fp16", cache_suffix: "-nodebug", feature_tests: "-p jouleclaw-energy --features rapl,nvml" }
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
# Three workspaces shell out to `z3`, and the runner image has no solver.
|
|
#
|
|
# This step used to be load-bearing for correctness: without a solver the
|
|
# ladder escalated past its real rungs into a model STAND-IN that reported
|
|
# Proved, so every goal on a solverless machine came back proved. That is
|
|
# now fixed at the source — the real ladders hold decision procedures only
|
|
# and an absent rung yields Unproved (proof-solvers `ladder_is_sound` and
|
|
# `ladder_never_fabricates_a_proof_when_a_rung_is_absent`).
|
|
#
|
|
# So the suites pass with or without z3, and this step is kept purely for
|
|
# COVERAGE: with a solver present the real T1 rung actually runs, and the
|
|
# SMT paths are exercised rather than skipped. The absent-rung semantics
|
|
# stay covered either way — that regression test uses a goal z3 cannot
|
|
# parse, so it asserts the same thing on a machine that has one.
|
|
#
|
|
# joule-code-remediate's formal gate is the third. Its tests assert in both
|
|
# branches, so they pass without a solver, and JOULE_FIX_EXPECT_Z3=1 below
|
|
# makes a missing z3 fail them where this step installed one. Otherwise CI
|
|
# would only ever run the no-solver branch.
|
|
#
|
|
# The z3 jobs are pinned to Linux runners deliberately: the macOS runner is
|
|
# a host executor with no apt. `sudo` may or may not exist depending on
|
|
# whether the container runs as root, so fall back to a bare invocation.
|
|
- name: Install z3
|
|
if: matrix.z3
|
|
shell: bash
|
|
run: |
|
|
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
|
$SUDO apt-get update -qq
|
|
$SUDO apt-get install -y -qq z3
|
|
z3 --version
|
|
|
|
# The docker runner image ships no Rust at all, so rustup is bootstrapped
|
|
# here. dtolnay/rust-toolchain cannot be used: it shells out to `rustup`,
|
|
# which the image lacks. Harmless where cargo already exists.
|
|
- name: Install Rust (workspace pin)
|
|
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
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
# jouleclaw-rs is the CAPACITY entry above. Linux embeds full DWARF in every
|
|
# test binary, so with default debuginfo its aarch64 test target outgrows
|
|
# the ~15 GB left on the shared txarm volume and the link fails with
|
|
# "No space left on device" (reproduced with the same toolchain, flags and
|
|
# image). Without debuginfo the same target is ~6 GB and the whole suite
|
|
# passes. Panic messages keep their file:line: that comes from the panic
|
|
# location, not from DWARF.
|
|
#
|
|
# Set through $GITHUB_ENV rather than a matrix field on purpose: a matrix
|
|
# field would hand every other job CARGO_PROFILE_DEV_DEBUG="", which cargo
|
|
# can reject. `cache_suffix` gives this job a fresh cache namespace so it
|
|
# cannot restore a full-debuginfo target and hold both builds at once;
|
|
# for every other job the suffix is empty and its key string is unchanged.
|
|
- name: Trim debuginfo (arm64 jouleclaw)
|
|
if: matrix.name == 'jouleclaw-rs'
|
|
shell: bash
|
|
run: echo "CARGO_PROFILE_DEV_DEBUG=0" >> "$GITHUB_ENV"
|
|
|
|
- name: Cache cargo registry + target
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
${{ matrix.dir }}/target
|
|
key: standards-${{ matrix.name }}${{ matrix.cache_suffix }}-${{ hashFiles(format('{0}/Cargo.lock', matrix.dir), 'rust-toolchain.toml') }}
|
|
restore-keys: |
|
|
standards-${{ matrix.name }}${{ matrix.cache_suffix }}-
|
|
|
|
# --all-targets is load-bearing: a plain `cargo check` does not build test
|
|
# targets, and smart-byte's dev-dependencies drifted independently of its
|
|
# normal ones (rand_chacha 0.10 against rand 0.8). The library compiled
|
|
# while the tests did not.
|
|
- name: Check (all targets)
|
|
working-directory: ${{ matrix.dir }}
|
|
env:
|
|
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
run: cargo check --workspace --all-targets
|
|
|
|
- name: Test
|
|
working-directory: ${{ matrix.dir }}
|
|
env:
|
|
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
# Where z3 was installed above, its absence is a failure, not a skip.
|
|
JOULE_FIX_EXPECT_Z3: ${{ matrix.z3 && '1' || '' }}
|
|
run: cargo test --workspace
|
|
|
|
# The NVML backends sit behind features no workspace member enables
|
|
# (jcp-energy `nvml`, eoc-meter `cuda`, jouleclaw-energy `nvml`), so the
|
|
# step above never compiles them, let alone runs their tests. Here they
|
|
# build and their tests run. The container has no GPU and Docker masks
|
|
# powercap, so the hardware tests take their "unavailable, and the
|
|
# oracle agrees" branch; the rule's unit tests run in full.
|
|
- name: Feature-gated energy tests
|
|
if: matrix.feature_tests
|
|
working-directory: ${{ matrix.dir }}
|
|
env:
|
|
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
run: cargo test ${{ matrix.feature_tests }}
|
|
|
|
# Run each standard's conformance suite.
|
|
#
|
|
# These suites live in a binary's `main()`, not in `#[test]`, so
|
|
# `cargo test` never executes them — verified, not assumed:
|
|
# `cargo test -p aac-conformance` runs **0 tests** while
|
|
# `cargo run -p aac-conformance` runs **40 checks**; map is 1 test vs 15
|
|
# checks. Same shape for jcp, proof and sandbox. The suites include
|
|
# negative vectors (`C-neg`, `Cneg`), which are the ones that catch a
|
|
# verifier ACCEPTING what it must reject.
|
|
#
|
|
# This is the `main()` instance of the same bug as a feature-gated test:
|
|
# the code compiles under `--all-targets`, reports nothing, and looks
|
|
# covered. Compiling a suite is not running it.
|
|
#
|
|
# `--bin` is explicit because these crates also ship a `gen-vectors`
|
|
# binary; `cargo run -p <crate>` is ambiguous with two binaries and fails
|
|
# rather than choosing — which is how this step would silently never run.
|
|
#
|
|
# No path argument: these binaries take none. They check the
|
|
# implementation against in-code expectations, which is a different claim
|
|
# from validating the published `conformance/v1` files on disk — nothing
|
|
# in CI does that yet, and this step does not pretend to.
|
|
- name: Conformance suite
|
|
if: matrix.conformance
|
|
working-directory: ${{ matrix.dir }}
|
|
env:
|
|
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
run: cargo run -q -p ${{ matrix.conformance }} --bin ${{ matrix.conformance }}
|
|
|
|
# ── The macOS energy backends are exercised NOWHERE otherwise ─────────────
|
|
#
|
|
# Five crates carry an Apple Silicon IOReport energy meter: jcp-energy,
|
|
# proof-cascade, eoc-meter, jouleclaw-energy and sandbox-meter. Each is
|
|
# behind `#[cfg(target_os = "macos")]` (jouleclaw-energy also behind its
|
|
# `ioreport` feature), and every workspace holding one is pinned to a linux
|
|
# runner above. A platform-gated test on a single-platform runner is zero
|
|
# coverage, and it is indistinguishable from passing.
|
|
#
|
|
# What this job checks, and why it used to flake. On macOS 27 an unprivileged
|
|
# reader does not see the IOReport "Energy Model" CPU rails (`CPU Energy`,
|
|
# `DIE_<n>_CPU Energy`) move under load. They are republished only in rare
|
|
# lumps: on the M3 Ultra both dies held for over 22 minutes, then jumped
|
|
# about 1.3 kJ each in a single 100 ms sample. On the M5 Max the GPU rail
|
|
# stays live. The old jcp-energy test asserted that the *summed* rails
|
|
# advanced during a CPU burn, and the GPU rail satisfied that whenever
|
|
# something drew on screen. It passed by day and failed at night. The
|
|
# 2026-09-19 zero that "healed" by 09-21 was this effect, not a recovery.
|
|
#
|
|
# Every meter now uses IOReport only when the CPU rail itself keeps
|
|
# advancing while the probe burns CPU: in at least 5 sample intervals, at
|
|
# least 4 in every 5, over at least 300 ms. A lump is published in steps (one
|
|
# was caught as +61.8 kJ, then +3.4 J 64 ms later), so two advancing
|
|
# intervals are still just a lump. Otherwise the meter falls back to its
|
|
# estimator rung. Its tests assert against an independent oracle that reads
|
|
# the CPU rail directly, so this job is deterministic:
|
|
# - green while each meter declines a rail that does not keep moving;
|
|
# - red if any meter selects one;
|
|
# - on a host whose rail is live, red unless CPU work meters non-zero.
|
|
#
|
|
# Each step fails unless its test printed the `ioreport liveness:` line. That
|
|
# line is the only proof the platform-gated test ran and asserted a branch,
|
|
# rather than a filter, a feature or a cfg leaving zero tests to pass. It goes
|
|
# to stderr and is captured on success, hence `--nocapture` and `2>&1`.
|
|
macos-energy-backend:
|
|
name: macOS energy backends
|
|
runs-on: macos
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install Rust (workspace pin)
|
|
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
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
- name: Cache cargo registry + targets
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
jcp/jcp-rs/target
|
|
proof/proof-rs/target
|
|
eoc/eoc-rs/target
|
|
jouleclaw/jouleclaw-rs/target
|
|
sandbox/sandbox-rs/target
|
|
key: standards-macos-energy-${{ hashFiles('jcp/jcp-rs/Cargo.lock', 'proof/proof-rs/Cargo.lock', 'eoc/eoc-rs/Cargo.lock', 'jouleclaw/jouleclaw-rs/Cargo.lock', 'sandbox/sandbox-rs/Cargo.lock', 'rust-toolchain.toml') }}
|
|
restore-keys: |
|
|
standards-macos-energy-
|
|
|
|
# Each crate's lib tests plus its integration tests. Each crate has a
|
|
# tests/ file that checks the liveness probe runs exactly once per process,
|
|
# and that check only holds in a fresh process. `--lib` would skip it.
|
|
- name: jcp-energy
|
|
shell: bash
|
|
working-directory: jcp/jcp-rs
|
|
run: |
|
|
set -o pipefail
|
|
log=$(mktemp)
|
|
cargo test -p jcp-energy -- --nocapture 2>&1 | tee "$log"
|
|
grep -q 'ioreport liveness:' "$log"
|
|
|
|
- name: proof-cascade
|
|
shell: bash
|
|
working-directory: proof/proof-rs
|
|
run: |
|
|
set -o pipefail
|
|
log=$(mktemp)
|
|
cargo test -p proof-cascade -- --nocapture 2>&1 | tee "$log"
|
|
grep -q 'ioreport liveness:' "$log"
|
|
|
|
- name: eoc-meter
|
|
shell: bash
|
|
working-directory: eoc/eoc-rs
|
|
run: |
|
|
set -o pipefail
|
|
log=$(mktemp)
|
|
cargo test -p eoc-meter -- --nocapture 2>&1 | tee "$log"
|
|
grep -q 'ioreport liveness:' "$log"
|
|
|
|
# Double-gated: `cfg(all(feature = "ioreport", target_os = "macos"))`.
|
|
# A macOS runner alone reaches none of it.
|
|
- name: jouleclaw-energy (feature AND platform gated)
|
|
shell: bash
|
|
working-directory: jouleclaw/jouleclaw-rs
|
|
run: |
|
|
set -o pipefail
|
|
log=$(mktemp)
|
|
cargo test -p jouleclaw-energy --features ioreport -- --nocapture 2>&1 | tee "$log"
|
|
grep -q 'ioreport liveness:' "$log"
|
|
|
|
- name: sandbox-meter
|
|
shell: bash
|
|
working-directory: sandbox/sandbox-rs
|
|
run: |
|
|
set -o pipefail
|
|
log=$(mktemp)
|
|
cargo test -p sandbox-meter -- --nocapture 2>&1 | tee "$log"
|
|
grep -q 'ioreport liveness:' "$log"
|