Skip to content

Admit observation uncertainty without workspace authority #847

Admit observation uncertainty without workspace authority

Admit observation uncertainty without workspace authority #847

Workflow file for this run

# SPDX-License-Identifier: Apache-2.0
# © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots>
name: det-gates
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: det-gates-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
classify-changes:
name: classify-changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
max_class: ${{ steps.classify.outputs.max_class }}
run_g1: ${{ steps.classify.outputs.run_g1 }}
run_g2: ${{ steps.classify.outputs.run_g2 }}
run_g3: ${{ steps.classify.outputs.run_g3 }}
run_g4: ${{ steps.classify.outputs.run_g4 }}
run_none: ${{ steps.classify.outputs.run_none }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changed files
id: changed
env:
BASE_REF: ${{ github.base_ref }}
EVENT_NAME: ${{ github.event_name }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
git fetch origin "$BASE_REF" --depth=1
git diff --name-only "origin/$BASE_REF...HEAD" > changed.txt
else
git diff --name-only HEAD~1..HEAD > changed.txt || true
fi
if [ "$EVENT_NAME" = "push" ] && [ ! -s changed.txt ]; then
echo "Warning: empty changelist on push, treating as full run" >&2
echo "det-policy.yaml" > changed.txt
fi
echo "Changed files:"
cat changed.txt || true
- name: Convert policy to JSON
run: |
yq -o=json det-policy.yaml > det-policy.json
- name: Classify path impact from det-policy.yaml
id: classify
run: |
node ./scripts/classify_changes.cjs det-policy.json changed.txt >> "$GITHUB_OUTPUT"
determinism-linux:
name: G1 determinism (linux)
needs: classify-changes
if: needs.classify-changes.outputs.run_g1 == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Run parity tests (linux)
run: |
cargo test -p echo-scene-port test_float_parity_with_js -- --nocapture 2>&1 | tee det-linux.log
grep -q " 0 passed" det-linux.log && echo "FATAL: zero tests matched filter" && exit 1 || true
- name: Trig oracle golden vectors (linux)
run: |
cargo test -p warp-math --test trig_golden_vectors -- trig_oracle_matches_golden_vectors --nocapture 2>&1 | tee trig-linux.log
cargo test -p warp-math --test deterministic_sin_cos_tests -- --nocapture 2>&1 | tee -a trig-linux.log
- name: Run DIND suite (linux)
run: |
node scripts/dind-run-suite.mjs --mode run | tee dind-linux.log
- name: Create digest table
env:
COMMIT_SHA: ${{ github.sha }}
RUN_ID: ${{ github.run_id }}
run: |
mkdir -p artifacts
echo "target,commit,run_id,digest" > artifacts/digest-table.csv
echo "linux,${COMMIT_SHA},${RUN_ID},$(sha256sum dind-report.json | cut -d' ' -f1)" >> artifacts/digest-table.csv
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: det-linux-artifacts
path: |
det-linux.log
trig-linux.log
dind-linux.log
dind-report.json
artifacts/digest-table.csv
determinism-macos:
name: G1 determinism (macos)
needs: classify-changes
if: needs.classify-changes.outputs.run_g1 == 'true'
runs-on: macos-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Run parity tests (macos)
run: |
cargo test -p echo-scene-port test_float_parity_with_js -- --nocapture 2>&1 | tee det-macos.log
grep -q " 0 passed" det-macos.log && echo "FATAL: zero tests matched filter" && exit 1 || true
- name: Trig oracle golden vectors (macos)
run: |
cargo test -p warp-math --test trig_golden_vectors -- trig_oracle_matches_golden_vectors --nocapture 2>&1 | tee trig-macos.log
cargo test -p warp-math --test deterministic_sin_cos_tests -- --nocapture 2>&1 | tee -a trig-macos.log
- name: Run DIND suite (macos)
run: |
node scripts/dind-run-suite.mjs --mode run | tee dind-macos.log
- name: Create digest table
env:
COMMIT_SHA: ${{ github.sha }}
RUN_ID: ${{ github.run_id }}
run: |
mkdir -p artifacts
echo "target,commit,run_id,digest" > artifacts/digest-table.csv
echo "macos,${COMMIT_SHA},${RUN_ID},$(shasum -a 256 dind-report.json | cut -d' ' -f1)" >> artifacts/digest-table.csv
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: det-macos-artifacts
path: |
det-macos.log
trig-macos.log
dind-macos.log
dind-report.json
artifacts/digest-table.csv
static-inspection:
name: Static nondeterminism inspection
needs: classify-changes
if: needs.classify-changes.outputs.run_g1 == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install ripgrep
run: command -v rg >/dev/null || (sudo apt-get update && sudo apt-get install -y ripgrep)
- name: Compute DETERMINISM_PATHS from policy
id: det_paths
run: |
PATHS=$(yq -o=json det-policy.yaml | jq -r '
.crates | to_entries[] |
select(.value.class == "DET_CRITICAL") |
.value.paths[]' |
grep '^crates/' | sed 's|/\*\*$||' | sort -u | tr '\n' ' ')
echo "paths=$PATHS" >> "$GITHUB_OUTPUT"
- name: Run determinism check
id: det_check
env:
DETERMINISM_PATHS: ${{ steps.det_paths.outputs.paths }}
run: |
./scripts/ban-nondeterminism.sh | tee static-inspection.log
- name: Upload inspection artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: static-inspection
path: static-inspection.log
decoder-security:
name: G2 decoder security tests
needs: classify-changes
if: needs.classify-changes.outputs.run_g2 == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Run codec tests
run: |
cargo test -p echo-scene-codec --lib cbor::tests -- --nocapture 2>&1 | tee sec-tests.log
grep -q " 0 passed" sec-tests.log && echo "FATAL: zero tests matched filter" && exit 1 || true
- name: Upload security artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: sec-artifacts
path: |
sec-tests.log
perf-regression:
name: G3 perf regression (criterion)
needs: classify-changes
if: needs.classify-changes.outputs.run_g3 == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Run benchmarks
run: |
cargo bench -p warp-benches --bench materialization_hotpath -- --output-format bencher | tee perf.log
- name: Check regression against baseline
run: |
node scripts/check_perf_regression.cjs perf-baseline.json perf.log --threshold 15
- name: Upload perf artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: perf-artifacts
path: |
perf.log
perf-report.json
build-repro-candidate:
name: G4 build candidate ${{ matrix.candidate }}
needs: classify-changes
if: needs.classify-changes.outputs.run_g4 == 'true'
runs-on: ubuntu-24.04
container:
image: docker.io/library/rust@sha256:3914072ca0c3b8aad871db9169a651ccfce30cf58303e5d6f2db16d1d8a7e58f
options: --platform linux/amd64
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
candidate: [1, 2]
steps:
- name: Checkout exact candidate source
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Install wasm target
run: rustup target add wasm32-unknown-unknown
- name: Build isolated candidate
env:
CANDIDATE: ${{ matrix.candidate }}
GIT_CONFIG_COUNT: 1
GIT_CONFIG_KEY_0: safe.directory
GIT_CONFIG_VALUE_0: ${{ github.workspace }}
run: |
repo_root="$(pwd -P)"
export RUSTFLAGS="--remap-path-prefix=${repo_root}=/workspace"
cargo build --release --target wasm32-unknown-unknown -p warp-wasm --features engine
sha256sum target/wasm32-unknown-unknown/release/warp_wasm.wasm > "hash${CANDIDATE}.txt"
cp target/wasm32-unknown-unknown/release/warp_wasm.wasm "build${CANDIDATE}.wasm"
cargo xtask provider-lowerer-component designated-build \
--target-dir target/provider-lowerer-repro \
--output target/lowerer.echo-dpo.component.wasm
sha256sum target/lowerer.echo-dpo.component.wasm > "lowerer-hash${CANDIDATE}.txt"
cp target/lowerer.echo-dpo.component.wasm "build${CANDIDATE}.lowerer.component.wasm"
cargo xtask provider-verifier-component designated-build \
--target-dir target/provider-verifier-repro \
--output target/verifier.echo-dpo.component.wasm
sha256sum target/verifier.echo-dpo.component.wasm > "verifier-hash${CANDIDATE}.txt"
cp target/verifier.echo-dpo.component.wasm "build${CANDIDATE}.verifier.component.wasm"
- name: Upload isolated candidate
uses: actions/upload-artifact@v4
with:
name: build-repro-candidate-${{ matrix.candidate }}
overwrite: true
path: |
hash${{ matrix.candidate }}.txt
build${{ matrix.candidate }}.wasm
lowerer-hash${{ matrix.candidate }}.txt
build${{ matrix.candidate }}.lowerer.component.wasm
verifier-hash${{ matrix.candidate }}.txt
build${{ matrix.candidate }}.verifier.component.wasm
build-repro:
name: G4 independent build comparison
needs:
- classify-changes
- build-repro-candidate
if: needs.classify-changes.outputs.run_g4 == 'true' && needs.build-repro-candidate.result == 'success'
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout exact candidate source
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Rust for portable promotion
uses: dtolnay/rust-toolchain@1.90.0
- name: Download candidate 1
uses: actions/download-artifact@v4
with:
name: build-repro-candidate-1
path: candidate1
- name: Download candidate 2
uses: actions/download-artifact@v4
with:
name: build-repro-candidate-2
path: candidate2
- name: Assemble exact evidence names
run: |
cp candidate1/hash1.txt hash1.txt
cp candidate2/hash2.txt hash2.txt
cp candidate1/build1.wasm build1.wasm
cp candidate2/build2.wasm build2.wasm
cp candidate1/lowerer-hash1.txt lowerer-hash1.txt
cp candidate2/lowerer-hash2.txt lowerer-hash2.txt
cp candidate1/build1.lowerer.component.wasm build1.lowerer.component.wasm
cp candidate2/build2.lowerer.component.wasm build2.lowerer.component.wasm
cp candidate1/verifier-hash1.txt verifier-hash1.txt
cp candidate2/verifier-hash2.txt verifier-hash2.txt
cp candidate1/build1.verifier.component.wasm build1.verifier.component.wasm
cp candidate2/build2.verifier.component.wasm build2.verifier.component.wasm
- name: Compare, promote, and check exact bytes
run: |
diff hash1.txt hash2.txt || (echo "Reproducibility failure: Hashes differ!" && exit 1)
diff lowerer-hash1.txt lowerer-hash2.txt || (echo "Lowerer reproducibility failure: Hashes differ!" && exit 1)
cmp build1.wasm build2.wasm
cmp build1.lowerer.component.wasm build2.lowerer.component.wasm
cargo xtask provider-lowerer-component promote \
--candidate-a build1.lowerer.component.wasm \
--candidate-b build2.lowerer.component.wasm \
--output target/lowerer.echo-dpo.promoted.component.wasm \
--write
cmp build1.lowerer.component.wasm target/lowerer.echo-dpo.promoted.component.wasm
cmp build1.lowerer.component.wasm schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm
cmp build2.lowerer.component.wasm schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm
diff verifier-hash1.txt verifier-hash2.txt || (echo "Verifier reproducibility failure: Hashes differ!" && exit 1)
cmp build1.verifier.component.wasm build2.verifier.component.wasm
cargo xtask provider-verifier-component promote \
--candidate-a build1.verifier.component.wasm \
--candidate-b build2.verifier.component.wasm \
--output target/verifier.echo-dpo.promoted.component.wasm \
--write
cmp build1.verifier.component.wasm target/verifier.echo-dpo.promoted.component.wasm
cmp build1.verifier.component.wasm schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm
cmp build2.verifier.component.wasm schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm
echo "Hashes match: $(cat hash1.txt)"
echo "Lowerer hashes match: $(cat lowerer-hash1.txt)"
echo "Verifier hashes match: $(cat verifier-hash1.txt)"
- name: Upload build artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: build-repro-artifacts
overwrite: true
path: |
hash1.txt
hash2.txt
build1.wasm
build2.wasm
lowerer-hash1.txt
lowerer-hash2.txt
build1.lowerer.component.wasm
build2.lowerer.component.wasm
verifier-hash1.txt
verifier-hash2.txt
build1.verifier.component.wasm
build2.verifier.component.wasm
validate-evidence:
name: Evidence artifact presence
needs:
- classify-changes
- determinism-linux
- determinism-macos
- static-inspection
- decoder-security
- perf-regression
- build-repro
if: always() && needs.classify-changes.result == 'success' && needs.classify-changes.outputs.run_none != 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: gathered-artifacts
- name: Verify upstream results and exact artifact payloads
env:
RUN_G1: ${{ needs.classify-changes.outputs.run_g1 }}
RUN_G2: ${{ needs.classify-changes.outputs.run_g2 }}
RUN_G3: ${{ needs.classify-changes.outputs.run_g3 }}
RUN_G4: ${{ needs.classify-changes.outputs.run_g4 }}
DECODER_RESULT: ${{ needs.decoder-security.result }}
PERF_RESULT: ${{ needs.perf-regression.result }}
REPRO_RESULT: ${{ needs.build-repro.result }}
LINUX_RESULT: ${{ needs.determinism-linux.result }}
MACOS_RESULT: ${{ needs.determinism-macos.result }}
INSPECTION_RESULT: ${{ needs.static-inspection.result }}
run: |
ls -R gathered-artifacts
required_artifacts=()
if [ "$RUN_G1" = "true" ]; then
[ "$LINUX_RESULT" = "success" ] || { echo "determinism-linux: $LINUX_RESULT"; exit 1; }
[ "$MACOS_RESULT" = "success" ] || { echo "determinism-macos: $MACOS_RESULT"; exit 1; }
[ "$INSPECTION_RESULT" = "success" ] || { echo "static-inspection: $INSPECTION_RESULT"; exit 1; }
required_artifacts+=(
gathered-artifacts/det-linux-artifacts/det-linux.log
gathered-artifacts/det-linux-artifacts/trig-linux.log
gathered-artifacts/det-linux-artifacts/dind-linux.log
gathered-artifacts/det-linux-artifacts/dind-report.json
gathered-artifacts/det-linux-artifacts/artifacts/digest-table.csv
gathered-artifacts/det-macos-artifacts/det-macos.log
gathered-artifacts/det-macos-artifacts/trig-macos.log
gathered-artifacts/det-macos-artifacts/dind-macos.log
gathered-artifacts/det-macos-artifacts/dind-report.json
gathered-artifacts/det-macos-artifacts/artifacts/digest-table.csv
gathered-artifacts/static-inspection/static-inspection.log
)
fi
if [ "$RUN_G2" = "true" ]; then
[ "$DECODER_RESULT" = "success" ] || { echo "decoder-security: $DECODER_RESULT"; exit 1; }
required_artifacts+=(
gathered-artifacts/sec-artifacts/sec-tests.log
)
fi
if [ "$RUN_G3" = "true" ]; then
[ "$PERF_RESULT" = "success" ] || { echo "perf-regression: $PERF_RESULT"; exit 1; }
required_artifacts+=(
gathered-artifacts/perf-artifacts/perf.log
gathered-artifacts/perf-artifacts/perf-report.json
)
fi
if [ "$RUN_G4" = "true" ]; then
[ "$REPRO_RESULT" = "success" ] || { echo "build-repro: $REPRO_RESULT"; exit 1; }
required_artifacts+=(
gathered-artifacts/build-repro-artifacts/hash1.txt
gathered-artifacts/build-repro-artifacts/hash2.txt
gathered-artifacts/build-repro-artifacts/build1.wasm
gathered-artifacts/build-repro-artifacts/build2.wasm
gathered-artifacts/build-repro-artifacts/lowerer-hash1.txt
gathered-artifacts/build-repro-artifacts/lowerer-hash2.txt
gathered-artifacts/build-repro-artifacts/build1.lowerer.component.wasm
gathered-artifacts/build-repro-artifacts/build2.lowerer.component.wasm
gathered-artifacts/build-repro-artifacts/verifier-hash1.txt
gathered-artifacts/build-repro-artifacts/verifier-hash2.txt
gathered-artifacts/build-repro-artifacts/build1.verifier.component.wasm
gathered-artifacts/build-repro-artifacts/build2.verifier.component.wasm
)
fi
for artifact in "${required_artifacts[@]}"; do
[ -s "$artifact" ] || { echo "Missing or empty artifact: $artifact"; exit 1; }
done