Approved by the owner on 2026-10-22. Procedure: change `channel`, run `rustup show`, then run the pipeline. What changed - rust-toolchain.toml and eoc/eoc-rs/rust-toolchain.toml: channel "1.98.0" becomes "1.99.1". These are the only two pins in the tree, and the root one governs every other cargo root. - 64 workflows under .github/workflows (the forge's CI): every `Install Rust 1.98.0`, `rustup toolchain install / default / component add / target add --toolchain 1.98.0` and `--default-toolchain 1.98.0` now names 1.99.1 (409 lines). Two of those lines are in build steps rather than install steps (wai-conformance.yml's two `wasm32 (unix)` steps each `rustup target add --toolchain`), and they move with the rest: a target added to a toolchain that is not installed is an error. - Left as they are, because they record what was measured on 1.98.0: standards-build.yml's "Verified on a native arm64 Linux container (rustc 1.98.0)", wai-conformance.yml's note on Rust 1.98's LLD 22, and the "rustc 1.98.0 (example)" strings in receipts, vectors and docs. The one-codegen-unit workaround that note explains also stays. 1.99.1 ships LLD 23.1.1, which the note says links those inputs, but removing the workaround should wait for a run at the CI path on x86_64. Why - 1.98.0 has a P-critical miscompile, rust-lang/rust#161441 (I-miscompile, regression-from-stable-to-stable). rustc emits a vacant (null) method slot in a trait-object vtable, so a safe dynamic call jumps to address zero (undefined behaviour). RELEASES.md records the fix in 1.98.1 ("rustc: fix miscompilation in generating vtables"). - 1.99.1 is the current stable (channel manifest dated 2026-10-15; rustc 58cf8f9c3 2026-10-13). It also fixes 1.99.0's own regressions: wcslen on UEFI and embedded targets, x86_64 FFI `bool` returns, and the rust-lld rpath on macOS. Evidence All of it ran on the build host ([email protected], aarch64-apple-darwin) through ~/kani-logs/remote/rb. Nothing was built on the dev Mac. - `rustup show`: the active toolchain is 1.99.1-aarch64-apple-darwin, "overridden by .../rust-toolchain.toml" (and by eoc/eoc-rs's own file there). rustc 1.99.1 (58cf8f9c3 2026-10-13), LLVM 23.1.1, clippy 0.1.99, rustfmt 1.10.0. The baseline is rustc 1.98.0 (88d9e12ae 2026-08-18). - Scope: every cargo root the pin governs, which is 46 of the 49 tracked lockfiles. The other three: two are vendored crates that resolve inside their parents, and wai/wai-rs/fuzz builds on its own pinned nightly. - Trees. The final tree is main at96117c570plus these two commits. Main moved four times while this ran, and each root's evidence comes from the newest tree it was run on: - 13 roots on the final tree: every root main changed after1a465f23d, and every root that consumes such a crate by path. - 12 roots on1a465f23dplus these two commits. - 7 roots onb16816671plus these two commits. - 14 roots onaa61ab3b9plus the pin change. For each root in the last three groups, checked with git diff and the lockfiles' path packages: none of its files, and no crate it reaches by path, differs on the final tree. - Each root got: (a) `cargo fmt --all -- --check` on the pin and on 1.98.0. The output is byte-identical in all 46. CI gates no root on rustfmt, and none is rustfmt-clean on main. (b) `cargo clippy --locked --keep-going --workspace --all-targets` on both toolchains, with the diagnostics compared line by line. After the previous commit, no root has a diagnostic that only 1.99.1 reports. openpay's one differing line is the same lint at the same site, reworded. (c) `cargo test --workspace --locked`. (d) the root's other CI steps that can run on this host: feature-gated tests, conformance suites, release-profile tests, the aac page-set checks, and every clippy -D warnings gate. The gates are standards-build's macOS meter and omni lints (jouleclaw-energy x2, jouleclaw-omni metal, jouleclaw-loader-gguf, jcp, proof, eoc, sandbox), wai-pack, wai-quantum-cli (default, no-default and metal), wai-quantum-service, wai-quantum-qpu and wai-quantum-desktop. - Totals: `cargo test --workspace` ran in 43 roots: 12,532 passed, 1 failed, 101 ignored. The other 3 roots are wasm32-only, and their `cargo build --release --target wasm32-unknown-unknown` passed. Every other CI step passed (62 of 62). - On the final tree: scripts/check-lockfiles.sh (cargo 1.99.1) found all 47 lockfiles current, and scripts/check-advisories.py passes. - tokio is 1.54.1 in every lockfile cargo reads. That is patched for both RUSTSEC-2026-0386 (>= 1.53.3) and RUSTSEC-2026-0384 (>= 1.54.1). The 1.53.1 in vendor/wtransport/Cargo.lock is the upstream file kept with the vendored source; cargo resolves wtransport inside its parents, and both gates skip that file. - What is not green, each one reproduced on 1.98.0: - wai-ops-gpu: under the debug profile, `corpus` trips the debug_assert at exec.rs:187 on both toolchains. CI runs it in release only, and that passes (6/6). - wai-ops: clippy::derived_hash_with_manual_eq (deny-by-default) on both toolchains. No CI job runs clippy there. - wai-vector/wasm: `--all-targets` cannot build the lib-test target (its own `panic_impl`) on either toolchain. `clippy --lib` for wasm32 reports 0 diagnostics on both. - eoc-cascade's hit_calibration_process_energy test reads real energy counters, and it fails some of the time on the loaded shared host. In interleaved runs each toolchain passed 18 of 20. The full eoc-rs suite passed on the final tree (741 passed). - Environment, not code: - wai-quantum-desktop embeds the Studio's build, so wai/quantum-studio was built first (`npm ci`, `node build.mjs`), as CI does. - Not covered here: the x86_64 and arm64 Linux and Windows legs; the GPU, NPU, FPGA, emulator and hardware-meter legs; wasm32 tests under Node, wasmtime and wasip2; and the fuzz job. Co-Authored-By: Claude Opus 5.5 <[email protected]>
224 lines
13 KiB
YAML
224 lines
13 KiB
YAML
name: wai-web reach builds
|
|
|
|
# The reach builds of wai-web (wai/wai-web/reach/): the shipped build, L1 (legalised at
|
|
# the JS interface), L2 (MVP-lowered and legalised) and J (the wasm2js translation),
|
|
# each run on an engine line it serves and compared with the shipped build output for
|
|
# output.
|
|
#
|
|
# On the linux runner, after a gate that reads the pull request's own diff, one leg per
|
|
# package:
|
|
#
|
|
# record the package of record (reach/record/: the two files the site serves under
|
|
# /wai/pkg/, built from the commit reach/manifest.json names). Both files must
|
|
# have the manifest's SHA-256, and build.sh runs with WAI_REACH_REQUIRE_RECORD=1,
|
|
# so every output must have the manifest's SHA-256 and size: the bytes the site
|
|
# serves under /wai/pkg/reach/. The package is kept, not rebuilt here: the same
|
|
# commit and recipe give different wasm on a Linux x86_64 host than on the
|
|
# macOS arm64 host that built it (the glue is the same).
|
|
# tree wai-web built from this tree, so a change to wai-web meets the reach builds
|
|
# before the package of record moves to it.
|
|
#
|
|
# Each leg then:
|
|
#
|
|
# 1. derives every reach build with reach/build.sh (pinned binaryen and JS tools; J's
|
|
# wasm2js is binaryen's WebAssembly build, the same on every host), which checks that
|
|
# the translation and the glue parse as ECMAScript 5 and writes each output's SHA-256
|
|
# to a manifest;
|
|
# 2. on current Node (reach/test/run-modern.mjs): the loader must choose the shipped
|
|
# build; every rung and profile, forced, must give the shipped build's outputs over
|
|
# the corpora; an engine stripped to ES5 must be given J and the same outputs; and
|
|
# the demo verify scripts must pass against shipped, L1, L2 and J. On the record leg
|
|
# those scripts, and the corpora they read, are the record's commit's
|
|
# (--demo-commit): the package is held to the checks of the code it was built from,
|
|
# and a check added since meets that code on the tree leg;
|
|
# 3. on older engine lines, each in its own Node image (reach/test/conformance.js,
|
|
# which is ECMAScript 5): the loader must choose the rung that line needs, by
|
|
# feature probes alone, and every workload must equal the shipped build's output
|
|
# from step 2:
|
|
# node:0.12 no WebAssembly -> J
|
|
# node:8.0.0 WebAssembly without the RegExp dotAll flag -> J (full), L2 (receipt)
|
|
# node:8 MVP WebAssembly, no BigInt -> L2
|
|
# node:10 MVP WebAssembly, BigInt -> L2
|
|
# node:12 post-MVP features, no BigInt at the boundary -> L1
|
|
# node:14 post-MVP features, no BigInt at the boundary -> L1
|
|
# These are proxies: engine lines that real devices also ship (the README says
|
|
# which device classes each stands for). No real old device runs here.
|
|
#
|
|
# The images run as `docker://` steps: each mounts this job's workspace, so the build
|
|
# and the reference of steps 1-2 are read where they were written. They run no
|
|
# JavaScript action, since their Node is too old to.
|
|
#
|
|
# Push filters match the push's delta, so every path a step reads is listed.
|
|
|
|
# One run per branch at a time. A push supersedes the run before it; on main a newer
|
|
# merge's tree contains the older one's.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-web/**'
|
|
- 'wai/wai-rs/**'
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/neural-conformance/**'
|
|
- 'wai/staged-conformance/**'
|
|
- 'wai/staged-measure-conformance/**'
|
|
- '.github/workflows/wai-web-reach.yml'
|
|
schedule:
|
|
- cron: '32 4 * * *'
|
|
|
|
env:
|
|
REACH: wai/wai-web/reach
|
|
|
|
jobs:
|
|
# Forgejo matches `paths:` against the push delta, so a rebase fires this workflow
|
|
# whatever the pull request changes. This job reads the pull request's own diff
|
|
# against the paths above (scripts/pr-touches-paths.py, which fails open) and the
|
|
# build runs only when it touches them. On a push to main it answers `true`.
|
|
relevant:
|
|
name: wai-web reach · 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-web-reach.yml \
|
|
"${{ github.event.pull_request.base.sha }}" \
|
|
"${{ github.event.pull_request.head.sha }}")
|
|
echo "this change touches the reach builds: $r"
|
|
echo "run=$r" >> "$GITHUB_OUTPUT"
|
|
|
|
reach:
|
|
name: wai-web reach builds · ${{ matrix.name }}
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- leg: record
|
|
name: the package of record, every output as recorded
|
|
- leg: tree
|
|
name: this tree's wai-web, every rung on its engine line
|
|
env:
|
|
DIST: wai/wai-web/reach/ci-dist-${{ matrix.leg }}
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
# The docker runner image ships no Rust.
|
|
- name: Install Rust 1.99.1 with the wasm32 target
|
|
if: matrix.leg == 'tree'
|
|
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.99.1
|
|
rustup toolchain install 1.99.1 --profile minimal
|
|
rustup default 1.99.1
|
|
rustup target add wasm32-unknown-unknown --toolchain 1.99.1
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
# Pinned to the wasm-bindgen version in wai-web/Cargo.lock: the CLI and the crate
|
|
# must match exactly.
|
|
- name: wasm-bindgen CLI 0.2.127
|
|
if: matrix.leg == 'tree'
|
|
shell: bash
|
|
run: cargo install --locked wasm-bindgen-cli --version 0.2.127
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '26'
|
|
# The site's package is wasm-pack's web target with wasm-opt off
|
|
# ([package.metadata.wasm-pack.profile.release]): cargo, then wasm-bindgen. It goes
|
|
# outside the tree, where no package.json decides how Node reads the glue.
|
|
- name: Build wai-web for wasm32 from this tree (web target)
|
|
if: matrix.leg == 'tree'
|
|
shell: bash
|
|
working-directory: wai/wai-web
|
|
run: |
|
|
set -euo pipefail
|
|
cargo build --locked --release --target wasm32-unknown-unknown
|
|
wasm-bindgen --target web --out-dir "$RUNNER_TEMP/pkg" --out-name wai_web \
|
|
target/wasm32-unknown-unknown/release/wai_web.wasm
|
|
# The package of record leaves the tree for the same reason, after its check.
|
|
- name: The package of record (reach/record/), held to the manifest
|
|
if: matrix.leg == 'record'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
node -e '
|
|
const fs = require("fs"), path = require("path"), crypto = require("crypto");
|
|
const [man, pkg] = process.argv.slice(1);
|
|
const m = JSON.parse(fs.readFileSync(man, "utf8"));
|
|
console.log(`the package of record: ${m.input.source}`);
|
|
let bad = 0;
|
|
for (const [n, e] of Object.entries(m.input.files)) {
|
|
const b = fs.readFileSync(path.join(pkg, n));
|
|
const got = crypto.createHash("sha256").update(b).digest("hex");
|
|
console.log(`${got} ${n} (${b.length} B); the manifest records ${e.sha256} (${e.bytes} B)`);
|
|
if (got !== e.sha256 || b.length !== e.bytes) bad++;
|
|
}
|
|
if (bad) { console.error(`reach: reach/record/ is not the package the manifest names (${bad} file(s) differ)`); process.exit(1); }' \
|
|
"$REACH/manifest.json" "$REACH/record"
|
|
mkdir -p "$RUNNER_TEMP/pkg"
|
|
cp "$REACH/record/wai_web_bg.wasm" "$REACH/record/wai_web.js" "$RUNNER_TEMP/pkg/"
|
|
# The commit the package was built from (input.commit): its demo verify scripts
|
|
# are the ones run-modern.mjs runs on this leg. The checkout is shallow.
|
|
c=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).input.commit' "$REACH/manifest.json")
|
|
git fetch --no-tags --depth=1 origin "$c"
|
|
echo "RECORD_COMMIT=$c" >> "$GITHUB_ENV"
|
|
# The build and the reference stay in the workspace: the engine images below
|
|
# mount it, and nothing else.
|
|
- name: Reach builds (reach/build.sh, pinned binaryen and JS tools)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "${{ matrix.leg }}" = record ]; then export WAI_REACH_REQUIRE_RECORD=1; fi
|
|
WAI_REACH_TOOLS="$RUNNER_TEMP/reach-tools" bash "$REACH/build.sh" "$RUNNER_TEMP/pkg" "$DIST"
|
|
cat "$DIST/manifest.json"
|
|
# The demo verify scripts: on the record leg, the record's commit's, over that
|
|
# commit's corpora; on the tree leg, this tree's.
|
|
- name: Current Node — every rung forced, the demo verify scripts on every full build
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
set --
|
|
if [ "${{ matrix.leg }}" = record ]; then set -- --demo-commit "$RECORD_COMMIT"; fi
|
|
node "$REACH/test/run-modern.mjs" --dist "$DIST" --shipped "$RUNNER_TEMP/pkg" --out "$REACH/ci-modern-${{ matrix.leg }}" "$@"
|
|
|
|
- name: node:0.12 (no WebAssembly) — J chosen and equal to the shipped build
|
|
uses: docker://node:0.12
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; for p in full verify_decode verify receipt; do node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile $p --expect-rung J --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json; done"
|
|
- name: node:8.0.0 (WebAssembly, no RegExp dotAll) — J for the full build, the L2 receipt build
|
|
uses: docker://node:8.0.0
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile full --expect-rung J --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile receipt --expect-rung L2 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json"
|
|
- name: node:8 (MVP WebAssembly, no BigInt) — L2 chosen and equal to the shipped build
|
|
uses: docker://node:8
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; for p in full verify_decode verify receipt; do node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile $p --expect-rung L2 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json; done; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --rung J --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json"
|
|
- name: node:10 (MVP WebAssembly, BigInt) — L2 chosen and equal to the shipped build
|
|
uses: docker://node:10
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile full --expect-rung L2 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json"
|
|
- name: node:12 (post-MVP WebAssembly, no BigInt at the boundary) — L1 chosen and equal to the shipped build
|
|
uses: docker://node:12
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile full --expect-rung L1 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --rung L2 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json"
|
|
- name: node:14 (post-MVP WebAssembly, no BigInt at the boundary) — L1 chosen and equal to the shipped build
|
|
uses: docker://node:14
|
|
with:
|
|
entrypoint: /bin/sh
|
|
args: -c "set -e; node --version; node wai/wai-web/reach/test/conformance.js --dist wai/wai-web/reach/ci-dist-${{ matrix.leg }} --wai wai --profile full --expect-rung L1 --reference wai/wai-web/reach/ci-modern-${{ matrix.leg }}/reference.json"
|