name: CI on: pull_request: paths-ignore: - "docs/**" - "website/**" - "README.md" - ".github/actions/build-docs/action.yml" - ".github/workflows/deploy.yml" - ".github/workflows/pr-preview.yml" - ".github/workflows/pr-preview-cleanup.yml" push: branches: - main permissions: contents: read concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: ios-runner-swift-compat: name: iOS Runner Swift Compatibility runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Disallow trailing commas before closing parenthesis in Swift run: | if rg -nU --glob '*.swift' ',\s*\n\s*\)' apple/runner; then echo "Found trailing commas before ')' in Swift files. This syntax requires Swift 6.1+ and breaks older Xcode toolchains." exit 1 fi swift-runner-unit-compile: name: Swift Runner Unit Compile runs-on: macos-26 timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Compile Swift runner unit-test surface uses: ./.github/actions/setup-apple-replay with: derived-path: ${{ github.workspace }}/.tmp/swift-runner-unit-derived cache-key-prefix: swift-runner-unit build-command: AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 pnpm build:xcuitest:macos xcuitest-platform: macos no-test-di-seams: name: No test-only DI seams runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Fail if test-only DI seams reappear in production code run: | if rg '\?\s*:\s*typeof\s+' src/ --glob '!**/__tests__/**' --glob '!*.test.ts'; then echo "Found test-only DI seams (optional typeof params) in production code." exit 1 fi lint: name: Lint & Format runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Run oxlint run: pnpm lint - name: Check formatting run: pnpm format:check layering-guard: name: Layering Guard runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm with: install-deps: false - name: Check import-direction DAG # Generalizes the former inline commands/-import grep into a structured # import-direction lint over the resolved graph. See scripts/layering/check.ts # and CONTEXT.md (Architecture: folder DAG + layering lint). Invoked directly # (no deps needed) so the job stays fast and does not require a pnpm install. run: | node --experimental-strip-types --test scripts/layering/model.test.ts node --experimental-strip-types scripts/layering/check.ts affected-selector: name: Affected-check Selector runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm with: install-deps: false # The selector is fail-open and advisory (GitHub CI stays authoritative), # so the gate only guards the derivation model. Invoked directly with no # deps, mirroring the layering guard. - name: Check affected-selector model run: node --experimental-strip-types --test scripts/check-affected/model.test.ts scripts/check-affected/run.test.ts packaged-cli-node-22-12: name: Packaged CLI Node 22.12 runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup build toolchain uses: ./.github/actions/setup-node-pnpm - name: Build and pack CLI run: | pnpm build pnpm check:bundle-owner-files mkdir -p .tmp/node-compat npm pack --ignore-scripts --pack-destination .tmp/node-compat - name: Setup Node.js 22.12 uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: "22.12" - name: Install and run global CLI on Node.js 22.12 run: | set -euo pipefail prefix="$RUNNER_TEMP/agent-device-node-22-12" tarball="$(find "$GITHUB_WORKSPACE/.tmp/node-compat" -name 'agent-device-*.tgz' -print -quit)" test -n "$tarball" npm install --global --prefix "$prefix" --ignore-scripts "$tarball" "$prefix/bin/agent-device" --version "$prefix/bin/agent-device" help "$prefix/bin/agent-device" doctor --remote --json fallow: name: Fallow Code Quality runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Run Fallow audit env: FALLOW_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} run: pnpm check:fallow --base "$FALLOW_BASE" - name: Check for production-unused exports run: pnpm check:production-exports coverage: # Runs the full unit + provider-integration suites under coverage with # thresholds, so a separate unit-tests job would rerun the same tests. name: Coverage runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Run coverage env: OUTPUT_ECONOMY_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} run: pnpm test:coverage typecheck: name: Typecheck runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Run typecheck run: pnpm typecheck integration: name: Integration Tests runs-on: ubuntu-latest timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm - name: Run integration tests run: | pnpm clean:daemon pnpm test:integration:node - name: Run provider-backed integration tests run: pnpm test:integration:provider - name: Check Provider-backed integration architecture progress run: pnpm test:integration:progress:check web-smoke: name: Web Platform Smoke runs-on: ubuntu-latest timeout-minutes: 30 env: AGENT_DEVICE_WEB_E2E: "1" steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup toolchain uses: ./.github/actions/setup-node-pnpm with: node-version: "24.13" - name: Run live web smoke run: | pnpm clean:daemon pnpm test:smoke:web - name: Upload web smoke artifacts if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: web-smoke-artifacts if-no-files-found: ignore path: | test/artifacts/web/**