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 } - { name: sandbox-rs, dir: sandbox/sandbox-rs, runner: linux } - { 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 } - { name: aac-rs, dir: aac/aac-rs, runner: linux-arm64 } - { 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 } - { 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