name: CI on: pull_request: types: [opened, synchronize, reopened] push: branches: - main workflow_dispatch: inputs: base_ref: description: Base commit or ref (defaults to the previous commit) required: false default: "" head_ref: description: Head commit or ref (defaults to the dispatched commit) required: false default: "" concurrency: group: ci-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: "0" GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: safe.directory GIT_CONFIG_VALUE_0: ${{ github.workspace }} jobs: resolve-refs: name: Resolve exact revisions runs-on: ubuntu-latest outputs: base_sha: ${{ steps.refs.outputs.base_sha }} head_sha: ${{ steps.refs.outputs.head_sha }} execution_order: ${{ steps.refs.outputs.execution_order }} steps: - name: Checkout exact HEAD uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false ref: ${{ github.event.pull_request.head.sha || inputs.head_ref || github.sha }} - name: Resolve HEAD and common ancestor id: refs shell: bash env: TARGET_BASE_REF: ${{ github.event.pull_request.base.sha || inputs.base_ref }} HEAD_REF: ${{ github.event.pull_request.head.sha || inputs.head_ref || github.sha }} EVENT_BEFORE: ${{ github.event.before }} run: | set -euo pipefail head_sha=$(git rev-parse "${HEAD_REF}^{commit}") target_base_ref=$TARGET_BASE_REF if [[ -z "$target_base_ref" ]]; then if [[ "$GITHUB_EVENT_NAME" == push \ && -n "$EVENT_BEFORE" \ && "$EVENT_BEFORE" != 0000000000000000000000000000000000000000 ]]; then target_base_ref=$EVENT_BEFORE else target_base_ref="${head_sha}^" fi fi target_base_sha=$(git rev-parse "${target_base_ref}^{commit}") base_sha=$(git merge-base "$target_base_sha" "$head_sha") if (( GITHUB_RUN_NUMBER % 2 == 0 )); then execution_order=head-first else execution_order=base-first fi echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT" echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT" echo "execution_order=$execution_order" >> "$GITHUB_OUTPUT" format: name: Format runs-on: ubuntu-latest container: rust:1.96.1-bookworm steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install rustfmt run: rustup component add rustfmt - name: Check formatting run: cargo fmt --all --check clippy: name: Clippy runs-on: ubuntu-latest container: rust:1.96.1-bookworm steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install AWS-LC build dependencies run: | apt-get update apt-get install --yes --no-install-recommends cmake libclang-dev - name: Install clippy run: rustup component add clippy - name: Check clippy run: cargo clippy --workspace --all-targets --all-features -- -D warnings nextest: name: Nextest runs-on: [self-hosted, moli-ci] container: image: rust:1.96.1-bookworm # Docker seeds new named volumes from the pinned image. Keep toolchains # and Cargo downloads local to each self-hosted Docker daemon. Bump both # volume names when the image or Rust toolchain changes. volumes: - moli-ci-cargo-home-1-96-1-v1:/usr/local/cargo - moli-ci-rustup-home-1-96-1-v1:/usr/local/rustup steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install AWS-LC build dependencies run: | apt-get update apt-get install --yes --no-install-recommends cmake libclang-dev - name: Install cargo-nextest uses: taiki-e/install-action@13608cbb45b01feb47ef444ab1a42dc41ad56f1a # v2.79.11 with: tool: nextest@0.9.132 - name: Run full workspace tests run: cargo nextest run --workspace --profile ci windows-persistent-storage: name: Windows persistent storage runs-on: windows-2025 timeout-minutes: 25 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install pinned Rust toolchain shell: pwsh run: | $ErrorActionPreference = "Stop" $PSNativeCommandUseErrorActionPreference = $true $toolchain = (Get-Content rust-toolchain -Raw).Trim() rustup toolchain install $toolchain --profile minimal --no-self-update - name: Test Windows profile, OPFS, and storage paths run: >- cargo test --locked -p moli-browser-profile -p moli-opfs -p moli-storage-service python-harness-tests: name: Python harness tests runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: | moli-benchmark/uv.lock moli-frontend-smoke/uv.lock moli-cdp-smoke/uv.lock moli-webdriver-smoke/uv.lock - name: Install harness dependencies run: | uv sync --project moli-benchmark --locked uv sync --project moli-frontend-smoke --locked uv sync --project moli-cdp-smoke --locked uv sync --project moli-webdriver-smoke --locked - name: Test benchmark harness shell: bash run: | set -euo pipefail for test_file in moli-benchmark/tests/test_*.py; do case "$test_file" in */test_wpt_cross*.py) continue ;; esac uv run --project moli-benchmark --no-sync python "$test_file" done - name: Test frontend differential harness run: uv run --project moli-frontend-smoke --no-sync pytest moli-frontend-smoke/tests - name: Test CDP smoke diagnostics run: >- uv run --project moli-cdp-smoke --no-sync python -m unittest discover --start-directory moli-cdp-smoke/tests - name: Test WebDriver smoke diagnostics run: >- uv run --project moli-webdriver-smoke --no-sync python -m unittest discover --start-directory moli-webdriver-smoke/tests node-harness-tests: name: Node harness and fixture tests runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24.15.0 cache: npm cache-dependency-path: | moli-frontend-smoke/package-lock.json moli-benchmark/browser-spider-local/package-lock.json - name: Install dependencies run: | npm ci --prefix moli-frontend-smoke npm ci --prefix moli-benchmark/browser-spider-local - name: Validate frontend fixtures working-directory: moli-frontend-smoke run: | npm run check:catalog npm run test:node npm run typecheck npm run build - name: Test Spider harness working-directory: moli-benchmark/browser-spider-local run: npm test - name: Test trusted report helpers run: node --test .github/scripts/*.test.cjs build-release-head: name: Build release · HEAD needs: resolve-refs runs-on: [self-hosted, moli-ci] timeout-minutes: 60 container: image: rust:1.96.1-bookworm volumes: - moli-ci-cargo-home-1-96-1-v1:/usr/local/cargo - moli-ci-rustup-home-1-96-1-v1:/usr/local/rustup steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Install AWS-LC build dependencies run: | apt-get update apt-get install --yes --no-install-recommends cmake libclang-dev - name: Build release binary run: cargo build --release --locked -p moli - name: Package release artifact shell: bash run: | set -euo pipefail package_dir=target/ci-package/moli-release-head mkdir -p "$package_dir" install -m 0755 target/release/moli "$package_dir/moli" printf '%s\n' '${{ needs.resolve-refs.outputs.head_sha }}' > "$package_dir/revision.txt" (cd "$package_dir" && sha256sum moli > SHA256SUMS) tar -C target/ci-package -czf target/moli-release-head.tar.gz moli-release-head - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: moli-release-head path: target/moli-release-head.tar.gz compression-level: 0 if-no-files-found: error retention-days: 3 build-release-base: name: Build release · common ancestor needs: resolve-refs runs-on: [self-hosted, moli-ci] timeout-minutes: 60 container: image: rust:1.96.1-bookworm volumes: - moli-ci-cargo-home-1-96-1-v1:/usr/local/cargo - moli-ci-rustup-home-1-96-1-v1:/usr/local/rustup steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.base_sha }} - name: Install AWS-LC build dependencies run: | apt-get update apt-get install --yes --no-install-recommends cmake libclang-dev - name: Build release binary run: cargo build --release --locked -p moli - name: Package release artifact shell: bash run: | set -euo pipefail package_dir=target/ci-package/moli-release-base mkdir -p "$package_dir" install -m 0755 target/release/moli "$package_dir/moli" printf '%s\n' '${{ needs.resolve-refs.outputs.base_sha }}' > "$package_dir/revision.txt" (cd "$package_dir" && sha256sum moli > SHA256SUMS) tar -C target/ci-package -czf target/moli-release-base.tar.gz moli-release-base - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: moli-release-base path: target/moli-release-base.tar.gz compression-level: 0 if-no-files-found: error retention-days: 3 cdp-smoke: name: CDP smoke needs: [resolve-refs, build-release-head] runs-on: ubuntu-latest timeout-minutes: 60 container: rust:1.96.1-bookworm steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: moli-cdp-smoke/uv.lock - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24.15.0 cache: npm cache-dependency-path: moli-cdp-smoke/package-lock.json - name: Install dependencies run: | uv sync --project moli-cdp-smoke --locked npm ci --prefix moli-cdp-smoke - name: Run full CDP smoke run: | export MOLI_BIN="$PWD/target/ci-bin/moli-release-head/moli" uv run --project moli-cdp-smoke --no-sync moli-cdp-smoke \ --jobs 4 --output-dir target/smoke/cdp-ci - name: Upload diagnostics if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: cdp-smoke-diagnostics path: target/smoke/cdp-ci if-no-files-found: warn retention-days: 7 webdriver-smoke: name: WebDriver smoke needs: [resolve-refs, build-release-head] runs-on: ubuntu-latest timeout-minutes: 60 container: rust:1.96.1-bookworm steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: moli-webdriver-smoke/uv.lock - name: Install dependencies run: uv sync --project moli-webdriver-smoke --locked - name: Run full WebDriver smoke shell: bash run: | export MOLI_BIN="$PWD/target/ci-bin/moli-release-head/moli" mkdir -p target/smoke/webdriver-ci set -o pipefail uv run --project moli-webdriver-smoke --no-sync \ moli-webdriver-smoke --continue-on-failure \ 2>&1 | tee target/smoke/webdriver-ci/run.log - name: Upload diagnostics if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: webdriver-smoke-diagnostics path: target/smoke/webdriver-ci if-no-files-found: warn retention-days: 7 frontend-differential: name: Frontend differential · 1020 cases needs: [resolve-refs, build-release-head] runs-on: [self-hosted, moli-ci] timeout-minutes: 45 container: mcr.microsoft.com/playwright/python:v1.58.0-noble steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: | moli-frontend-smoke/uv.lock moli-cdp-smoke/uv.lock - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24.15.0 - name: Install dependencies and build fixtures run: | uv sync --project moli-frontend-smoke --locked uv sync --project moli-cdp-smoke --locked npm ci --prefix moli-frontend-smoke npm run build --prefix moli-frontend-smoke - name: Resolve pinned Chromium id: chromium shell: bash run: | set -euo pipefail chromium_bin=$(uv run --project moli-cdp-smoke --no-sync python -c \ 'from playwright.sync_api import sync_playwright; p = sync_playwright().start(); print(p.chromium.executable_path); p.stop()') test -x "$chromium_bin" wrapper="$PWD/target/ci-bin/chromium-ci" printf '%s\n' '#!/bin/sh' "exec \"$chromium_bin\" --no-sandbox \"\$@\"" > "$wrapper" chmod 0755 "$wrapper" echo "binary=$wrapper" >> "$GITHUB_OUTPUT" - name: Run full differential id: frontend continue-on-error: true # The full catalog runs on shared hosts; leave headroom for transient # Chromium stalls without changing any readiness or comparison rules. run: >- uv run --project moli-frontend-smoke --no-sync moli-frontend-smoke --chromium-bin "${{ steps.chromium.outputs.binary }}" --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --timeout-ms 30000 --reference-infrastructure-retries 1 --output target/frontend-smoke - name: Validate frontend artifacts id: analysis if: ${{ always() && steps.frontend.outcome != 'skipped' }} continue-on-error: true run: >- uv run --project moli-frontend-smoke --no-sync moli-frontend-smoke-analyze results target/frontend-smoke - name: Upload frontend evidence if: ${{ always() && steps.frontend.outcome != 'skipped' }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: frontend-differential-results path: target/frontend-smoke if-no-files-found: warn retention-days: 7 - name: Require differential and artifact validation if: ${{ always() }} env: FRONTEND_OUTCOME: ${{ steps.frontend.outcome }} ANALYSIS_OUTCOME: ${{ steps.analysis.outcome }} run: | test "$FRONTEND_OUTCOME" = success test "$ANALYSIS_OUTCOME" = success runtime-contracts: name: Runtime and CDP session contracts needs: [resolve-refs, build-release-head] runs-on: [self-hosted, moli-ci] timeout-minutes: 30 container: rust:1.96.1-bookworm steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: moli-benchmark/uv.lock - name: Install dependencies run: uv sync --project moli-benchmark --locked - name: Run deterministic fetch contracts run: >- uv run --project moli-benchmark --no-sync moli-benchmark synthetic --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --runs 5 --output-dir target/benchmark/runtime-synthetic - name: Run long-lived CDP session contracts run: >- uv run --project moli-benchmark --no-sync moli-benchmark cdp-session --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --target moli-cdp --gate-target moli-cdp --runs 3 --output-dir target/benchmark/cdp-session - name: Upload evidence if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: runtime-contract-results path: target/benchmark if-no-files-found: warn retention-days: 7 agent-episode: name: Agent episodes · Moli vs Chromium needs: [resolve-refs, build-release-head] runs-on: [self-hosted, moli-ci] timeout-minutes: 45 container: mcr.microsoft.com/playwright/python:v1.58.0-noble steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: | moli-benchmark/uv.lock moli-cdp-smoke/uv.lock - name: Install dependencies run: | uv sync --project moli-benchmark --locked uv sync --project moli-cdp-smoke --locked - name: Resolve pinned Chromium id: chromium run: | chromium_bin=$(uv run --project moli-cdp-smoke --no-sync python -c \ 'from playwright.sync_api import sync_playwright; p = sync_playwright().start(); print(p.chromium.executable_path); p.stop()') test -x "$chromium_bin" echo "binary=$chromium_bin" >> "$GITHUB_OUTPUT" - name: Run fast cross-engine contract run: >- uv run --project moli-benchmark --no-sync moli-benchmark agent-episode --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --chrome-bin "${{ steps.chromium.outputs.binary }}" --step-dwell-ms 0 --output-dir target/benchmark/agent-fast - name: Run idle-resource episode run: >- uv run --project moli-benchmark --no-sync moli-benchmark agent-episode --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --chrome-bin "${{ steps.chromium.outputs.binary }}" --output-dir target/benchmark/agent-resource - name: Upload evidence if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: agent-episode-results path: target/benchmark if-no-files-found: warn retention-days: 7 release-regression: name: Release startup, size, and matrix A/B needs: [resolve-refs, build-release-head, build-release-base] runs-on: [self-hosted, moli-ci] timeout-minutes: 60 container: rust:1.96.1-bookworm steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use base release uses: ./.github/actions/use-ci-release with: side: base revision: ${{ needs.resolve-refs.outputs.base_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: moli-benchmark/uv.lock - run: uv sync --project moli-benchmark --locked - name: Measure common-ancestor startup and package continue-on-error: true run: >- uv run --project moli-benchmark --no-sync moli-benchmark startup --moli-bin "$PWD/target/ci-bin/moli-release-base/moli" --profile formal --output-dir target/release-regression/base-startup - name: Require HEAD startup and package workflow run: >- uv run --project moli-benchmark --no-sync moli-benchmark startup --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --profile formal --baseline-report target/release-regression/base-startup --output-dir target/release-regression/head-startup - name: Measure common-ancestor concurrency matrix continue-on-error: true run: >- uv run --project moli-benchmark --no-sync moli-benchmark synthetic-matrix --moli-bin "$PWD/target/ci-bin/moli-release-base/moli" --profile smoke --output-dir target/release-regression/base-matrix - name: Require HEAD concurrency matrix run: >- uv run --project moli-benchmark --no-sync moli-benchmark synthetic-matrix --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --profile smoke --baseline-report target/release-regression/base-matrix --output-dir target/release-regression/head-matrix - name: Upload evidence if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: release-regression-results # Package sizes stay in the JSON reports and image manifests. Upload # report data only instead of duplicating the full rootfs archives. path: | target/release-regression/**/*.json target/release-regression/**/*.csv target/release-regression/**/*.md if-no-files-found: warn retention-days: 7 spider-fixture: name: Deterministic Spider fixture needs: [resolve-refs, build-release-head] runs-on: [self-hosted, moli-ci] timeout-minutes: 20 container: rust:1.96.1-bookworm steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24.15.0 - run: npm ci --prefix moli-benchmark/browser-spider-local - name: Require exact fixture contract run: >- node moli-benchmark/browser-spider-local/bench.mjs --target moli --moli-bin "$PWD/target/ci-bin/moli-release-head/moli" --workers 1 --parallelism 1 --runs 1 --site-source fixture --goto-timeout-ms 1500 --server-hang-ms 3000 --sample-interval-ms 100 --assert-fixture-contract --output-dir target/spider-fixture - name: Upload evidence if: ${{ always() }} continue-on-error: true uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: spider-fixture-results path: target/spider-fixture if-no-files-found: warn retention-days: 7 spider-bench: name: Spider public-web A/B needs: [resolve-refs, build-release-head, build-release-base] if: >- github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') runs-on: [self-hosted, moli-ci] timeout-minutes: 90 container: rust:1.96.1-bookworm env: BENCHMARK_OUTPUT: spider-bench-results steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use base release uses: ./.github/actions/use-ci-release with: side: base revision: ${{ needs.resolve-refs.outputs.base_sha }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24.15.0 - run: npm ci --prefix moli-benchmark/browser-spider-local - name: Run deterministic and public-web A/B run: >- node moli-benchmark/browser-spider-local/compare.mjs run --base-bin "$PWD/target/ci-bin/moli-release-base/moli" --head-bin "$PWD/target/ci-bin/moli-release-head/moli" --base-sha "${{ needs.resolve-refs.outputs.base_sha }}" --head-sha "${{ needs.resolve-refs.outputs.head_sha }}" --execution-order "${{ needs.resolve-refs.outputs.execution_order }}" --output-dir "$BENCHMARK_OUTPUT" --run-url "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - name: Render job summary if: ${{ always() && !cancelled() }} continue-on-error: true run: | if [[ -f "$BENCHMARK_OUTPUT/comment.md" ]]; then cat "$BENCHMARK_OUTPUT/comment.md" >> "$GITHUB_STEP_SUMMARY" fi - name: Upload full evidence if: ${{ always() && !cancelled() }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: spider-bench-results path: ${{ env.BENCHMARK_OUTPUT }} if-no-files-found: error retention-days: 14 sequential-navigation-soak: name: Sequential navigation soak · 200 A/B needs: [resolve-refs, build-release-head, build-release-base] if: >- github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') runs-on: [self-hosted, moli-ci] timeout-minutes: 90 container: rust:1.96.1-bookworm env: BENCHMARK_OUTPUT: sequential-navigation-soak-results steps: - uses: actions/checkout@v4 with: persist-credentials: false ref: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use HEAD release uses: ./.github/actions/use-ci-release with: side: head revision: ${{ needs.resolve-refs.outputs.head_sha }} - name: Use base release uses: ./.github/actions/use-ci-release with: side: base revision: ${{ needs.resolve-refs.outputs.base_sha }} - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: version: 0.11.14 python-version: "3.12" enable-cache: true cache-dependency-glob: moli-benchmark/uv.lock - run: uv sync --project moli-benchmark --locked - name: Run one-session public-web A/B id: soak shell: bash env: EXECUTION_ORDER: ${{ needs.resolve-refs.outputs.execution_order }} run: | set -euo pipefail run_side() { local side=$1 local binary=$2 set +e uv run --project moli-benchmark --no-sync python \ moli-benchmark/scripts/fuzz-cdp-sequential-navigate.py \ --engine moli --moli-bin "$binary" \ --url https://www.csdn.net/ \ --url https://segmentfault.com/ \ --url https://huaban.com/ \ --url https://example.com/ \ --rounds 50 --network-diagnostics --navigation-resource-samples \ --response-timeout 15 --dcl-timeout 14 --load-timeout 20 \ --postcheck-timeout 5 --recovery-timeout 8 \ --output "$BENCHMARK_OUTPUT/$side.json" local status=$? set -e echo "${side}_exit_code=$status" >> "$GITHUB_OUTPUT" } if [[ "$EXECUTION_ORDER" == head-first ]]; then run_side head "$PWD/target/ci-bin/moli-release-head/moli" run_side base "$PWD/target/ci-bin/moli-release-base/moli" else run_side base "$PWD/target/ci-bin/moli-release-base/moli" run_side head "$PWD/target/ci-bin/moli-release-head/moli" fi - name: Compare resilience and memory evidence id: comparison if: ${{ always() && !cancelled() && steps.soak.outcome == 'success' }} env: PYTHONPATH: moli-benchmark run: >- uv run --project moli-benchmark --no-sync python -m moli_benchmark.sequential_navigate_ci compare --base-report "$BENCHMARK_OUTPUT/base.json" --head-report "$BENCHMARK_OUTPUT/head.json" --base-exit-code "${{ steps.soak.outputs.base_exit_code }}" --head-exit-code "${{ steps.soak.outputs.head_exit_code }}" --base-sha "${{ needs.resolve-refs.outputs.base_sha }}" --head-sha "${{ needs.resolve-refs.outputs.head_sha }}" --execution-order "${{ needs.resolve-refs.outputs.execution_order }}" --expected-navigations 200 --output "$BENCHMARK_OUTPUT/comparison.json" --comment-output "$BENCHMARK_OUTPUT/comment.md" --run-url "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" --conclusion local - name: Render job summary if: ${{ always() && !cancelled() }} continue-on-error: true run: | if [[ -f "$BENCHMARK_OUTPUT/comment.md" ]]; then cat "$BENCHMARK_OUTPUT/comment.md" >> "$GITHUB_STEP_SUMMARY" fi - name: Upload full evidence if: ${{ always() && !cancelled() }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: sequential-navigation-soak-results path: ${{ env.BENCHMARK_OUTPUT }} if-no-files-found: error retention-days: 14 - name: Require complete HEAD resilience evidence if: ${{ always() && !cancelled() && steps.comparison.outcome == 'success' }} env: PYTHONPATH: moli-benchmark run: >- uv run --project moli-benchmark --no-sync python -m moli_benchmark.sequential_navigate_ci check --input "$BENCHMARK_OUTPUT/comparison.json"