Some checks failed
standards workspaces build + test / aac-rs (pull_request) Successful in 44s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 45s
standards workspaces build + test / jouleclaw-rs (pull_request) Failing after 2m49s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 38s
standards workspaces build + test / map-rs (pull_request) Successful in 41s
standards workspaces build + test / mesh-rs (pull_request) Successful in 27s
standards workspaces build + test / openpay (pull_request) Successful in 1m59s
standards workspaces build + test / eoc-rs (pull_request) Successful in 2m30s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m22s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m16s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m23s
standards workspaces build + test / arl-rs (pull_request) Successful in 10m3s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 2m49s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 4m23s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 3m41s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 3m35s
standards workspaces build + test / sense-rs (pull_request) Successful in 3m6s
Five standards ship a conformance runner whose suite lives in a binary's
`main()`, not in `#[test]`. `cargo test` therefore never executes them, and
`--all-targets` compiles the binary without running it — so the suites reported
nothing and the matrix looked green.
Verified rather than inferred:
cargo test -p aac-conformance -> 0 tests
cargo run -p aac-conformance -> 40 checks
cargo test -p map-conformance -> 1 test
cargo run -p map-conformance -> 15 checks
Same shape for jcp, proof and sandbox. Every one of these suites includes
negative vectors (`C-neg`, `Cneg`) — the checks that catch a verifier ACCEPTING
what it must reject, which no amount of positive-path testing finds.
This is the `main()` instance of the feature-gated-test bug already recorded for
this repo: code that compiles, reports nothing, and reads as covered. Compiling
a suite is not running it.
aac-conformance's own doc comment says "Exit code is the number of failed checks
(capped), so CI can gate on a clean 0" — it was written to be gated on, and
nothing ever gated on it. All five exit non-zero on failure (aac by failure
count, the rest by `exit(1)`), so the step has teeth.
Two things this step deliberately does NOT claim:
* No path argument. These binaries take none — they check the implementation
against in-code expectations. An earlier draft of this step passed
`-- ../conformance/v1`, which they silently ignore; a tampered copy of
map/conformance/v1 still passed 15/15, which is how the mistake surfaced.
* It does not validate the published `conformance/v1` files on disk. Nothing
in CI does that yet. Standards whose packs ARE read by a `#[test]` (jcp
action-receipt, joule-ui, jouleclaw, joulecontract, sandbox, sense) are
already covered by the Test step; joule-code has its own workflow. The
remaining on-disk-pack gap is real and stays open rather than being papered
over by a step that looks like it closes it.
Co-Authored-By: Claude Opus 5 <[email protected]>
181 lines
9.9 KiB
YAML
181 lines
9.9 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 }
|
|
- { 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 }
|
|
- { 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 }
|
|
- { 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" }
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
# Two 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.
|
|
#
|
|
# Both 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"
|
|
|
|
- name: Cache cargo registry + target
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
${{ matrix.dir }}/target
|
|
key: standards-${{ matrix.name }}-${{ hashFiles(format('{0}/Cargo.lock', matrix.dir), 'rust-toolchain.toml') }}
|
|
restore-keys: |
|
|
standards-${{ matrix.name }}-
|
|
|
|
# --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 }}
|
|
run: cargo test --workspace
|
|
|
|
# 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 }}
|