open-standards/.github/workflows/wai-audit.yml
Transaction Science 6517401f01 CI: the gate that decides whether a workflow runs was queued behind the work it gates
Every path-gated workflow begins with one small job -- `does this change touch
it`, or `which workspaces this change touches` for the standards build -- that
all of its real jobs declare in `needs`. All 55 of those gate jobs ran on
`linux-arm64`.

That is the one label the fleet cannot spare. Sampled while 267 runs were
queued, every waiting run had the same shape:

    [waiting]  linux-arm64    does this change touch it
    [blocked]  linux          member · dataflow device, simulated
    [blocked]  macos          member · dataflow device absent

237 runs, each holding a gate in the longest queue, and every real job blocked
behind it. Not only `linux` jobs: `macos`, `linux-hw`, `linux-arm64-hw` and
`macos-m3-ultra` jobs were blocked too. The measured effect was `linux-arm64`
263 deep against `linux` **zero**, with jetson-hub's 20 cores taking no job for
a full hour -- starved not for want of work but because the work could not be
unblocked. A gate waiting on the resource it is rationing is head-of-line
blocking, and it makes every other runner in the fleet idle on its behalf.

The gates move to `linux`. Nothing else changes: the diff is 55 `runs-on` lines
and no others, and every touched file still parses.

Why `linux` and not elsewhere. The gate is not free -- it compares
`BASE...HEAD`, which needs the merge base, so `fetch-depth: 0` is a full clone
rather than a ten-second script. `macos` is served by hosts on a Mac Studio
sitting at load 185; `linux` is a separate 20-core box that was measurably
empty. This moves a serialisation point to the least contended label; it does
not remove it, and the gate is still a single point of failure for its
workflow. The structural fix is an early-exit first step inside each job, which
costs one checkout per leg and is a larger change.

Follow-up, deliberately not bundled: jetson-hub runs at capacity 2 with 15 GB of
RAM, so 55 clones still serialise two at a time. Raising capacity helps gates
and hurts the heavy emulated-architecture jobs that share the label, so it wants
measuring on its own.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-03 15:19:13 -05:00

136 lines
6.1 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: wai-audit collector and player integration
# WAI's stream audit kit (wai/wai-audit, extensions/stream-audit.md): the collector's tests (every
# session of the corpus live-conformance/audit posted gets the corpus's outcome, and the stored
# sessions report exactly the corpus's served reports, byte for byte and signature; CORS, body
# limits, per-client limits and CMCD intake), clippy with warnings as errors, on Linux and on the
# macOS runner; and on the macOS runner, the host with the browser installed, the player
# integration's unit tests against the corpus and the browser end to end (e2e/run.sh): an HLS
# stream made with the host's media tool, served from another origin, played through an
# open-source web player twice (its loader adapter, and the network tap), each session verified,
# stored and reported by a collector on loopback, and the report recomputed from the bundles.
# The packages come from e2e/package-lock.json with install scripts off. Nothing listens beyond
# loopback.
#
# Push filters match the push's delta, so every path the jobs read is listed.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-audit/**'
- 'wai/wai-rs/**'
- 'wai/wai-quantum/**'
- 'wai/wai-ops/**'
- 'wai/live-conformance/audit/**'
- 'wai/live-conformance/native-sink/**'
- 'wai/extensions/stream-audit.md'
- 'rust-toolchain.toml'
- '.github/workflows/wai-audit.yml'
pull_request:
paths:
- 'wai/wai-audit/**'
- 'wai/wai-rs/**'
- 'wai/wai-quantum/**'
- 'wai/wai-ops/**'
- 'wai/live-conformance/audit/**'
- 'wai/live-conformance/native-sink/**'
- 'wai/extensions/stream-audit.md'
- 'rust-toolchain.toml'
- '.github/workflows/wai-audit.yml'
jobs:
relevant:
name: 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-audit.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-audit: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
collector-linux:
name: collector · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- 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 --component clippy
rustup default 1.98.0
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Tests and clippy
shell: bash
working-directory: wai/wai-audit
run: |
set -euo pipefail
cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
cargo clippy --locked --release --all-targets -- -D warnings
kit-macos:
name: collector, player integration and the browser end to end · macos
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo and the host's packages (the media tool, node) on the macOS
# host runner.
- name: Put cargo and the host's tools on PATH
shell: bash
run: |
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
echo "/opt/homebrew/bin" >> "$GITHUB_PATH"
- name: The collector's tests
shell: bash
working-directory: wai/wai-audit
run: |
set -euo pipefail
nice -n 10 cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
- name: The player integration and the browser end to end
shell: bash
env:
BROWSER_BIN: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome # allow-name: the runner host's installed browser, by its path
AUDIT_E2E_OUT: ${{ runner.temp }}/wai-audit-e2e
run: |
set -euo pipefail
test -x "$BROWSER_BIN" || { echo "::error::this runner has no browser at $BROWSER_BIN"; exit 1; }
command -v ffmpeg >/dev/null || { echo "::error::this runner has no media tool to make the stream with"; exit 1; }
nice -n 10 bash wai/wai-audit/e2e/run.sh 2>&1 | tee "$RUNNER_TEMP/audit-e2e.log"
grep -qE '^ℹ pass [1-9][0-9]*$' "$RUNNER_TEMP/audit-e2e.log" && ! grep -qE '^ℹ fail [1-9]' "$RUNNER_TEMP/audit-e2e.log" \
|| { echo "::error::the player integration's unit tests did not pass"; exit 1; }
grep -q '^all checks ok$' "$RUNNER_TEMP/audit-e2e.log" || { echo "::error::the browser end to end did not pass"; exit 1; }
if grep -q '^FAIL' "$RUNNER_TEMP/audit-e2e.log"; then echo "::error::a check failed"; exit 1; fi
# The page's integration and the native sink are two implementations of stream-audit's
# player: the page replays the native sink corpus's page-parity case call for call and
# signs the same bundle byte for byte (extensions/native-sink.md §10).
- name: The page replays the native sink's corpus
shell: bash
run: |
set -euo pipefail
node --test wai/wai-audit/web/native-sink.test.mjs 2>&1 | tee "$RUNNER_TEMP/native-parity.log"
grep -qE '^ℹ pass [1-9][0-9]*$' "$RUNNER_TEMP/native-parity.log" && ! grep -qE '^ℹ fail [1-9]' "$RUNNER_TEMP/native-parity.log" \
|| { echo "::error::the page did not sign the native sink's bundle"; exit 1; }