diff --git a/.github/workflows/e2e-blockchain-chrome-devnet.yml b/.github/workflows/e2e-blockchain-chrome-devnet.yml
new file mode 100644
index 00000000..0bf5f8d9
--- /dev/null
+++ b/.github/workflows/e2e-blockchain-chrome-devnet.yml
@@ -0,0 +1,121 @@
+name: E2E Blockchain (Chrome, devnet) — Nightly
+
+# Chrome extension blockchain-backed E2E suite against devnet — NIGHTLY only.
+# Push-on-main coverage (with the OR-tolerance gate across both networks)
+# lives in `e2e-blockchain-chrome.yml`; this workflow exists per-network
+# so the devnet network-monitor card can pin to a single workflow id
+# without contention from push runs. Symmetric testnet sibling:
+# `e2e-blockchain-chrome-testnet.yml`.
+
+on:
+ schedule:
+ - cron: '0 4 * * *'
+ workflow_dispatch: {}
+
+permissions:
+ contents: read
+ issues: write # for the schedule-only failure-issue step below
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ chrome-devnet:
+ name: Chrome E2E (devnet)
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ env:
+ E2E_NETWORK: devnet
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: yarn
+
+ # cargo is needed to install miden-client-cli from crates.io on first run.
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache miden-client-cli
+ # Cache key follows the root package.json so a SDK version bump
+ # (or any other root-level dep change) naturally invalidates — the
+ # CLI is installed version-matched to @miden-sdk/miden-sdk by
+ # helpers/miden-cli.ts.
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/bin/miden-client
+ key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
+
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+
+ # Pre-install the miden-client-cli so the cold-cache ~7 min cargo
+ # compile happens in its own step, not inside Playwright's 5-min
+ # per-test timeout. `cargo install` is a no-op on warm cache.
+ - name: Install miden-client-cli
+ shell: bash
+ run: |
+ VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
+ if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
+ echo "miden-client already present from cache:"
+ "$HOME/.cargo/bin/miden-client" --version || true
+ else
+ echo "Installing miden-client-cli@${VERSION}..."
+ cargo install miden-client-cli --version "$VERSION"
+ fi
+
+ - name: Install xvfb
+ run: sudo apt-get update && sudo apt-get install -y xvfb
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps chromium
+
+ - name: Run blockchain E2E (devnet)
+ run: xvfb-run -a yarn test:e2e:blockchain:devnet
+
+ - name: Upload artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: chrome-e2e-devnet-${{ github.run_id }}
+ path: test-results/
+ retention-days: 7
+ if-no-files-found: ignore
+
+ # Auto-create a tracking issue when the SCHEDULED nightly fails. Push
+ # failures stay visible on the merge commit + Actions tab and don't need
+ # an issue (PR context already covers them); only the nightly cadence —
+ # which has no PR context — gets an issue so a red night doesn't rot
+ # silently.
+ nightly-failure-issue:
+ name: Open issue on nightly failure
+ needs: chrome-devnet
+ if: failure() && github.event_name == 'schedule'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ steps:
+ - name: Create issue
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
+ const today = new Date().toISOString().slice(0, 10);
+ const title = `Nightly Chrome E2E (devnet) failed — ${today}`;
+ const body = [
+ `Nightly run [\`${context.runId}\`](${runUrl}) failed on devnet.`,
+ ``,
+ `Workflow: \`${context.workflow}\``,
+ `Commit: \`${context.sha}\``,
+ ``,
+ `Auto-created by \`e2e-blockchain-chrome-devnet.yml\` on schedule failure.`,
+ ].join('\n');
+ await github.rest.issues.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title,
+ body,
+ labels: ['ci-failure', 'nightly-e2e', 'devnet'],
+ });
diff --git a/.github/workflows/e2e-blockchain-chrome-testnet.yml b/.github/workflows/e2e-blockchain-chrome-testnet.yml
new file mode 100644
index 00000000..a5bd0d97
--- /dev/null
+++ b/.github/workflows/e2e-blockchain-chrome-testnet.yml
@@ -0,0 +1,113 @@
+name: E2E Blockchain (Chrome, testnet) — Nightly
+
+# Chrome extension blockchain-backed E2E suite against testnet — NIGHTLY only.
+# Push-on-main coverage (with the OR-tolerance gate across both networks)
+# lives in `e2e-blockchain-chrome.yml`; this workflow exists per-network
+# so the testnet network-monitor card can pin to a single workflow id
+# without contention from push runs. Symmetric devnet sibling:
+# `e2e-blockchain-chrome-devnet.yml`.
+
+on:
+ schedule:
+ - cron: '0 4 * * *'
+ workflow_dispatch: {}
+
+permissions:
+ contents: read
+ issues: write # for the schedule-only failure-issue step below
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ chrome-testnet:
+ name: Chrome E2E (testnet)
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ env:
+ E2E_NETWORK: testnet
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: yarn
+
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache miden-client-cli
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/bin/miden-client
+ key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
+
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+
+ - name: Install miden-client-cli
+ shell: bash
+ run: |
+ VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
+ if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
+ echo "miden-client already present from cache:"
+ "$HOME/.cargo/bin/miden-client" --version || true
+ else
+ echo "Installing miden-client-cli@${VERSION}..."
+ cargo install miden-client-cli --version "$VERSION"
+ fi
+
+ - name: Install xvfb
+ run: sudo apt-get update && sudo apt-get install -y xvfb
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps chromium
+
+ - name: Run blockchain E2E (testnet)
+ run: xvfb-run -a yarn test:e2e:blockchain:testnet
+
+ - name: Upload artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: chrome-e2e-testnet-${{ github.run_id }}
+ path: test-results/
+ retention-days: 7
+ if-no-files-found: ignore
+
+ # Auto-create a tracking issue when the SCHEDULED nightly fails. Push
+ # failures stay visible on the merge commit + Actions tab and don't need
+ # an issue (PR context already covers them); only the nightly cadence —
+ # which has no PR context — gets an issue so a red night doesn't rot
+ # silently.
+ nightly-failure-issue:
+ name: Open issue on nightly failure
+ needs: chrome-testnet
+ if: failure() && github.event_name == 'schedule'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ steps:
+ - name: Create issue
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
+ const today = new Date().toISOString().slice(0, 10);
+ const title = `Nightly Chrome E2E (testnet) failed — ${today}`;
+ const body = [
+ `Nightly run [\`${context.runId}\`](${runUrl}) failed on testnet.`,
+ ``,
+ `Workflow: \`${context.workflow}\``,
+ `Commit: \`${context.sha}\``,
+ ``,
+ `Auto-created by \`e2e-blockchain-chrome-testnet.yml\` on schedule failure.`,
+ ].join('\n');
+ await github.rest.issues.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title,
+ body,
+ labels: ['ci-failure', 'nightly-e2e', 'testnet'],
+ });
diff --git a/.github/workflows/e2e-blockchain-chrome.yml b/.github/workflows/e2e-blockchain-chrome.yml
new file mode 100644
index 00000000..023b9b92
--- /dev/null
+++ b/.github/workflows/e2e-blockchain-chrome.yml
@@ -0,0 +1,181 @@
+name: E2E Blockchain (Chrome) — Push
+
+# Chrome extension blockchain-backed E2E suites against public networks.
+# Runs on every push to main. The gate job passes as long as AT LEAST
+# ONE network (devnet OR testnet) succeeded — this tolerates short
+# windows where wallet@main and a single network are protocol-mismatched
+# (e.g. devnet ahead of the pinned SDK version).
+#
+# This workflow only runs on push. Nightly per-network coverage lives in
+# the schedule-only siblings:
+# - e2e-blockchain-chrome-devnet.yml
+# - e2e-blockchain-chrome-testnet.yml
+#
+# The split exists because the network-monitor's devnet/testnet
+# instances each pin to one schedule-only workflow file; running both
+# networks in a single push workflow keeps the merge-gate tolerance the
+# team has come to rely on.
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+ inputs:
+ network:
+ description: 'Network(s) to test against'
+ required: false
+ default: 'both'
+ type: choice
+ options:
+ - both
+ - devnet
+ - testnet
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ chrome-devnet:
+ name: Chrome E2E (devnet)
+ if: github.event_name != 'workflow_dispatch' || inputs.network == 'both' || inputs.network == 'devnet'
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ env:
+ E2E_NETWORK: devnet
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: yarn
+
+ # cargo is needed to install miden-client-cli from crates.io on first run.
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache miden-client-cli
+ # Cache key follows the root package.json so a SDK version bump
+ # (or any other root-level dep change) naturally invalidates — the
+ # CLI is installed version-matched to @miden-sdk/miden-sdk by
+ # helpers/miden-cli.ts.
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/bin/miden-client
+ key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
+
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+
+ # Pre-install the miden-client-cli so the cold-cache ~7 min cargo
+ # compile happens in its own step, not inside Playwright's 5-min
+ # per-test timeout. `cargo install` is a no-op on warm cache.
+ - name: Install miden-client-cli
+ shell: bash
+ run: |
+ VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
+ if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
+ echo "miden-client already present from cache:"
+ "$HOME/.cargo/bin/miden-client" --version || true
+ else
+ echo "Installing miden-client-cli@${VERSION}..."
+ cargo install miden-client-cli --version "$VERSION"
+ fi
+
+ - name: Install xvfb
+ run: sudo apt-get update && sudo apt-get install -y xvfb
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps chromium
+
+ - name: Run blockchain E2E (devnet)
+ run: xvfb-run -a yarn test:e2e:blockchain:devnet
+
+ - name: Upload artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: chrome-e2e-devnet-${{ github.run_id }}
+ path: test-results/
+ retention-days: 7
+ if-no-files-found: ignore
+
+ chrome-testnet:
+ name: Chrome E2E (testnet)
+ if: github.event_name != 'workflow_dispatch' || inputs.network == 'both' || inputs.network == 'testnet'
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ env:
+ E2E_NETWORK: testnet
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: yarn
+
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache miden-client-cli
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/bin/miden-client
+ key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
+
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+
+ - name: Install miden-client-cli
+ shell: bash
+ run: |
+ VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
+ if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
+ echo "miden-client already present from cache:"
+ "$HOME/.cargo/bin/miden-client" --version || true
+ else
+ echo "Installing miden-client-cli@${VERSION}..."
+ cargo install miden-client-cli --version "$VERSION"
+ fi
+
+ - name: Install xvfb
+ run: sudo apt-get update && sudo apt-get install -y xvfb
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps chromium
+
+ - name: Run blockchain E2E (testnet)
+ run: xvfb-run -a yarn test:e2e:blockchain:testnet
+
+ - name: Upload artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: chrome-e2e-testnet-${{ github.run_id }}
+ path: test-results/
+ retention-days: 7
+ if-no-files-found: ignore
+
+ chrome-gate:
+ name: Chrome E2E Gate
+ needs: [chrome-devnet, chrome-testnet]
+ if: always()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Require at least one network to pass
+ shell: bash
+ env:
+ DEVNET_RESULT: ${{ needs.chrome-devnet.result }}
+ TESTNET_RESULT: ${{ needs.chrome-testnet.result }}
+ run: |
+ echo "devnet=$DEVNET_RESULT testnet=$TESTNET_RESULT"
+ if [[ "$DEVNET_RESULT" == "success" || "$TESTNET_RESULT" == "success" ]]; then
+ echo "Chrome E2E: at least one network passed."
+ exit 0
+ fi
+ echo "Chrome E2E: both networks failed or were skipped."
+ exit 1
diff --git a/.github/workflows/e2e-blockchain.yml b/.github/workflows/e2e-blockchain-mobile.yml
similarity index 59%
rename from .github/workflows/e2e-blockchain.yml
rename to .github/workflows/e2e-blockchain-mobile.yml
index 8d161e57..8a8a3bbf 100644
--- a/.github/workflows/e2e-blockchain.yml
+++ b/.github/workflows/e2e-blockchain-mobile.yml
@@ -1,11 +1,14 @@
-name: E2E Blockchain
-
-# Runs the blockchain-backed E2E suites (Chrome extension + iOS simulator)
-# against public networks on every push to main. The per-platform gate jobs
-# pass as long as AT LEAST ONE network (devnet OR testnet) succeeded for
-# that platform — this tolerates short windows where wallet@main and a
-# single network are protocol-mismatched (e.g. devnet ahead of the pinned
-# SDK version).
+name: E2E Blockchain (Mobile)
+
+# iOS-simulator blockchain-backed E2E suites against public networks.
+# Runs on every push to main. The gate job passes as long as AT LEAST
+# ONE network (devnet OR testnet) succeeded — same protocol-mismatch
+# tolerance as the Chrome suite.
+#
+# NOT on schedule: mobile lanes are macOS-runner-bound (~10× the cost
+# of the Chrome lanes) and the on-push cadence already gives same-day
+# signal. If a nightly mobile cadence is ever wanted, mirror the
+# `schedule:` block from `e2e-blockchain-chrome.yml`.
on:
push:
@@ -31,150 +34,6 @@ concurrency:
cancel-in-progress: true
jobs:
- # --- Chrome ---------------------------------------------------------------
-
- chrome-devnet:
- name: Chrome E2E (devnet)
- if: github.event_name != 'workflow_dispatch' || inputs.network == 'both' || inputs.network == 'devnet'
- runs-on: ubuntu-latest
- timeout-minutes: 60
- env:
- E2E_NETWORK: devnet
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
- with:
- node-version: 22
- cache: yarn
-
- # cargo is needed to install miden-client-cli from crates.io on first run.
- - uses: dtolnay/rust-toolchain@stable
-
- - name: Cache miden-client-cli
- # Cache key follows the root package.json so a SDK version bump
- # (or any other root-level dep change) naturally invalidates — the
- # CLI is installed version-matched to @miden-sdk/miden-sdk by
- # helpers/miden-cli.ts.
- uses: actions/cache@v4
- with:
- path: ~/.cargo/bin/miden-client
- key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
-
- - name: Install dependencies
- run: yarn install --frozen-lockfile
-
- # Pre-install the miden-client-cli so the cold-cache ~7 min cargo
- # compile happens in its own step, not inside Playwright's 5-min
- # per-test timeout. `cargo install` is a no-op on warm cache.
- - name: Install miden-client-cli
- shell: bash
- run: |
- VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
- if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
- echo "miden-client already present from cache:"
- "$HOME/.cargo/bin/miden-client" --version || true
- else
- echo "Installing miden-client-cli@${VERSION}..."
- cargo install miden-client-cli --version "$VERSION"
- fi
-
- - name: Install xvfb
- run: sudo apt-get update && sudo apt-get install -y xvfb
-
- - name: Install Playwright browsers
- run: npx playwright install --with-deps chromium
-
- - name: Run blockchain E2E (devnet)
- run: xvfb-run -a yarn test:e2e:blockchain:devnet
-
- - name: Upload artifacts
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: chrome-e2e-devnet-${{ github.run_id }}
- path: test-results/
- retention-days: 7
- if-no-files-found: ignore
-
- chrome-testnet:
- name: Chrome E2E (testnet)
- if: github.event_name != 'workflow_dispatch' || inputs.network == 'both' || inputs.network == 'testnet'
- runs-on: ubuntu-latest
- timeout-minutes: 60
- env:
- E2E_NETWORK: testnet
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
- with:
- node-version: 22
- cache: yarn
-
- - uses: dtolnay/rust-toolchain@stable
-
- - name: Cache miden-client-cli
- uses: actions/cache@v4
- with:
- path: ~/.cargo/bin/miden-client
- key: miden-client-cli-${{ runner.os }}-${{ hashFiles('package.json') }}
-
- - name: Install dependencies
- run: yarn install --frozen-lockfile
-
- - name: Install miden-client-cli
- shell: bash
- run: |
- VERSION=$(node -p "require('./node_modules/@miden-sdk/miden-sdk/package.json').version")
- if [[ -x "$HOME/.cargo/bin/miden-client" ]]; then
- echo "miden-client already present from cache:"
- "$HOME/.cargo/bin/miden-client" --version || true
- else
- echo "Installing miden-client-cli@${VERSION}..."
- cargo install miden-client-cli --version "$VERSION"
- fi
-
- - name: Install xvfb
- run: sudo apt-get update && sudo apt-get install -y xvfb
-
- - name: Install Playwright browsers
- run: npx playwright install --with-deps chromium
-
- - name: Run blockchain E2E (testnet)
- run: xvfb-run -a yarn test:e2e:blockchain:testnet
-
- - name: Upload artifacts
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: chrome-e2e-testnet-${{ github.run_id }}
- path: test-results/
- retention-days: 7
- if-no-files-found: ignore
-
- chrome-gate:
- name: Chrome E2E Gate
- needs: [chrome-devnet, chrome-testnet]
- if: always()
- runs-on: ubuntu-latest
- steps:
- - name: Require at least one network to pass
- shell: bash
- env:
- DEVNET_RESULT: ${{ needs.chrome-devnet.result }}
- TESTNET_RESULT: ${{ needs.chrome-testnet.result }}
- run: |
- echo "devnet=$DEVNET_RESULT testnet=$TESTNET_RESULT"
- if [[ "$DEVNET_RESULT" == "success" || "$TESTNET_RESULT" == "success" ]]; then
- echo "Chrome E2E: at least one network passed."
- exit 0
- fi
- echo "Chrome E2E: both networks failed or were skipped."
- exit 1
-
- # --- iOS Simulator --------------------------------------------------------
-
mobile-devnet:
name: Mobile E2E (devnet)
if: github.event_name != 'workflow_dispatch' || inputs.network == 'both' || inputs.network == 'devnet'