name: ci # Cancel previous runs for the same PR/branch concurrency: group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true on: push: branches: [main] pull_request: branches: [main] # Default to least privilege. Override per-job where needed. permissions: contents: read jobs: # Test job test: # Skip duplicate runs: run on push to main, or on pull_request events only if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref_name == 'main') runs-on: ubuntu-latest # 45 not 35: typecheck runs at --concurrency=4 (see below, #15140), so a # near-total affected cone (e.g. a develop→main promote PR) needs headroom # on top of install + build + the 15-minute test step. timeout-minutes: 45 env: # Baseline CI is secret-free. Live/provider-key E2E runs in dedicated # opt-in or post-merge workflows, not in the PR test job. PGLITE_WASM_MODE: node steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: # Full history so `turbo run typecheck --affected` can resolve the PR # merge base (#12341); a shallow clone degrades to typechecking all. fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: node-version: "24.15.0" - name: Setup workspace dependencies uses: ./.github/actions/setup-bun-workspace with: bun-version: "1.3.14" # pinned: floating canary writes lockfileVersion 2 and breaks --frozen-lockfile (#11184/#9454); packageManager bun@1.4.0-canary.1 is unresolvable (GH+npm 404) install-command: bun install install-native-deps: "false" skip-avatar-clone: "true" no-vision-deps: "true" - name: Create test env file run: | echo "TEST_DATABASE_CLIENT=pglite" > packages/core/.env.test echo "NODE_ENV=test" >> packages/core/.env.test - name: Build packages run: bun run build:core # PR lane: typecheck only the merge base's dependency cone; on push to # main typecheck the whole workspace (#12341). --concurrency=4 not 8: # a develop→main promote PR's affected cone is near-total, and eight # concurrent tsgo processes exhaust a 16 GB hosted runner (#15140); # mirrors develop-pr.yml so the PR lanes stay in agreement. - name: Run typecheck (affected — PR) if: github.event_name == 'pull_request' run: NODE_OPTIONS='--max-old-space-size=8192' node packages/scripts/run-turbo.mjs run typecheck --concurrency=4 --affected env: TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }} - name: Run typecheck (full — push) if: github.event_name != 'pull_request' run: bun run typecheck - name: Run tests timeout-minutes: 15 env: NODE_OPTIONS: "--max-old-space-size=2048" run: bun run test:core - name: Run plugin tests timeout-minutes: 15 env: NODE_OPTIONS: "--max-old-space-size=2048" run: bun run test:plugins - name: Run interop (TypeScript) tests timeout-minutes: 10 run: | if [ -d packages/interop ]; then cd packages/interop && bun run test else echo "packages/interop is not present; skipping interop tests" fi # Lint and format job lint-and-format: # Skip duplicate runs: run on push to main, or on pull_request events only if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref_name == 'main') runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: # `audit:test-realness` compares touched tests against origin/develop. # Keep the merge base available so the diff-scoped ratchet runs in CI. fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: node-version: "24.15.0" - name: Setup workspace dependencies uses: ./.github/actions/setup-bun-workspace with: bun-version: "1.3.14" # pinned: floating canary writes lockfileVersion 2 and breaks --frozen-lockfile (#11184/#9454); packageManager bun@1.4.0-canary.1 is unresolvable (GH+npm 404) install-command: bun install install-native-deps: "false" install-protoc: "false" setup-python: "false" skip-avatar-clone: "true" no-vision-deps: "true" - name: Prompt secret scan run: cd packages/prompts && bun run check:secrets - name: Type safety ratchet self-test run: bun run audit:type-safety-ratchet:self-test - name: Type safety ratchet run: bun run audit:type-safety-ratchet - name: Check format run: bun run format:check - name: Run lint run: bun run lint # UI render-time determinism gate. Fails on NEW Date.now()/Math.random()/ # crypto.randomUUID()/locale-defaulted toLocale* in a component/hook render # path (the source of flaky screenshots + meaningless snapshots). The # existing backlog is tracked in packages/scripts/ui-determinism-baseline.json; # only regressions beyond it fail. Self-test asserts the AST classifier. - name: UI determinism self-test run: bun run audit:ui-determinism:self-test - name: UI determinism gate run: bun run audit:ui-determinism # Anti-larp test gate (#10718). Fails on focused tests (.only / fit / # fdescribe — a single .only silently drops every sibling test in the file) # and on orphaned hardcoded skips (it.skip("name", fn) with no reason, # tracking issue, deny-list ref, or Playwright skip-annotation). Legitimate # conditional/env-gated skips (cond ? describe : describe.skip, # test.skip(cond, reason)) are allowed. Self-test asserts the classifier. - name: Anti-larp test gate self-test run: bun run audit:focused-tests:self-test - name: Anti-larp test gate run: bun run audit:focused-tests - name: Test realness ratchet run: bun run audit:test-realness # Per-plugin mock-LLM e2e coverage. The seeded allowlist carries # historical debt; new unsuppressed gaps fail the main gate (#13620). - name: Per-plugin e2e coverage run: node packages/scripts/lint-lane-coverage.mjs # Build job build: # Skip duplicate runs: run on push to main, or on pull_request events only if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref_name == 'main') runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: node-version: "24.15.0" - name: Setup workspace dependencies uses: ./.github/actions/setup-bun-workspace with: bun-version: "1.3.14" # pinned: floating canary writes lockfileVersion 2 and breaks --frozen-lockfile (#11184/#9454); packageManager bun@1.4.0-canary.1 is unresolvable (GH+npm 404) install-command: bun install install-native-deps: "false" install-protoc: "false" setup-python: "false" skip-avatar-clone: "true" no-vision-deps: "true" - name: Build packages run: bun run build:core # Dev startup + HMR job: prove `bun run dev` boots to a usable state quickly # and that edits at every package dependency depth propagate to the running # client over HMR. Guards against startup-time regressions and broken # src/-resolution or workspace-source watching. dev-startup: if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref_name == 'main') runs-on: ubuntu-latest timeout-minutes: 25 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: node-version: "24.15.0" - name: Setup workspace dependencies uses: ./.github/actions/setup-bun-workspace with: bun-version: "1.3.14" # pinned: floating canary writes lockfileVersion 2 and breaks --frozen-lockfile (#11184/#9454); packageManager bun@1.4.0-canary.1 is unresolvable (GH+npm 404) install-command: bun install install-native-deps: "false" install-protoc: "false" setup-python: "false" skip-avatar-clone: "true" no-vision-deps: "true" - name: Build packages run: bun run build - name: Remove source-adjacent build output run: bun run clean:stale-js - name: Install Playwright Chromium run: bunx playwright install --with-deps chromium # The product gate is 60s (the script default enforced on dev machines). # Shared CI runners are ~2-4 cores vs a dev workstation, so the CI ceiling # is relaxed to 90s — still catches a startup-time doubling, without # flaky false-failures. Lower this if runner class improves. - name: Dev startup budget (boots + ready under budget) env: ELIZA_DEV_STARTUP_BUDGET_MS: "90000" run: bun run test:dev-startup - name: HMR propagation across dependency levels run: bun run test:hmr