Hold newly promoted tools off the wire until a cache-safe boundary #2315
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }} | |
| cancel-in-progress: ${{ github.event_name != 'push' }} | |
| jobs: | |
| # oxfmt, oxlint, and typecheck share one runner: one checkout and one | |
| # install instead of three of each. Dummy job names prettier and eslint | |
| # stay for protect-main. | |
| static-analysis: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| # Exact-key-only: a restore-keys prefix of bun- would hydrate | |
| # node_modules from a different lockfile. bun install then has to | |
| # reconcile a stale tree; missing that step leaves wrong deps. | |
| key: bun-${{ hashFiles('bun.lock') }} | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: oxfmt | |
| run: bunx oxfmt --check . | |
| - name: oxlint | |
| run: bunx oxlint . | |
| - name: Typecheck | |
| run: bun run typecheck | |
| # Build runs beside the suite instead of before it: tests import ./src | |
| # directly and never read ./dist, so serializing build ahead of test put | |
| # build time on the critical path for no dependency reason. | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: bun-${{ hashFiles('bun.lock') }} | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Build | |
| run: bun run build | |
| # The suite is sharded so the slowest slice, not the whole suite, sets the | |
| # wall clock. The old ./src leg (387 files, ~88s local) is split into three | |
| # path-disjoint shards measured at ~30s (src-a: tui, 146 files), ~46s | |
| # (src-b: agent + subagent, 90 files), and ~45s (src-c: everything else in | |
| # src, 153 files); the ./tests leg stays whole because ./evals ./scripts | |
| # finish in ~1s and a leg of their own would be all setup overhead. | |
| # Every shard still goes through check:projects-dir-guard: the | |
| # guard forwards these path filters to the suite it wraps, and the union of | |
| # the shards' filters is exactly ./src ./tests ./evals ./scripts, so the gate covers | |
| # the same tests as before, all of them sandboxed. | |
| test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| # A red shard must not cancel the others; every result is the signal. | |
| fail-fast: false | |
| matrix: | |
| shard: | |
| - name: src-a | |
| paths: ./src/tui | |
| - name: src-b | |
| paths: ./src/agent ./src/subagent | |
| - name: src-c | |
| paths: ./src/auth ./src/changelog ./src/config ./src/cost ./src/crash ./src/exec ./src/logging ./src/mcp ./src/perf ./src/permission ./src/plugins ./src/provider ./src/session ./src/shell ./src/telemetry ./src/tools ./src/trust ./src/upgrade ./src/util ./src/web ./src/config.test.ts ./src/context-compactor.test.ts ./src/director.test.ts ./src/inference-abort.test.ts ./src/inference-error-message.test.ts ./src/inference-gateway-error.test.ts ./src/list-dir.test.ts ./src/pricing-fetcher.test.ts ./src/pricing-metadata.test.ts ./src/profiles.test.ts ./src/prompts.test.ts ./src/renderer.test.ts ./src/settings.test.ts ./src/state.test.ts | |
| - name: tests-evals-and-scripts | |
| paths: ./tests ./evals ./scripts | |
| name: test (${{ matrix.shard.name }}) | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| # The runner image has no ripgrep, so the grep plugin silently exercised | |
| # its fallback walker and left the ripgrep path untested. | |
| - name: Install ripgrep | |
| run: sudo apt-get install -y ripgrep | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: bun-${{ hashFiles('bun.lock') }} | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| # The same script the local `bun run check` gate runs, with the shard's | |
| # path filters forwarded through the guard to the suite. The guard | |
| # routes a filtered run through test:paths, which carries the same | |
| # seeded flags as the `test` script; bun test filters are additive, so | |
| # appending filters to `bun run test` could not narrow it. Randomized | |
| # order catches tests that only pass in the default file order (shared | |
| # module-level state, an unrestored global mock, a leaked env var). | |
| # The seed stays 424242 in every shard rather than varying per shard: | |
| # the shards already run disjoint file sets, and a fixed seed keeps | |
| # any failure reproducible locally with the same | |
| # `bun run test:paths <paths>`. | |
| - name: Test | |
| run: bun run check:projects-dir-guard ${{ matrix.shard.paths }} | |
| # protect-main still requires the pre-restructure check names. These jobs | |
| # exist only to publish those contexts after the real work succeeds. | |
| prettier: | |
| needs: static-analysis | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: "true" | |
| eslint: | |
| needs: static-analysis | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: "true" | |
| typecheck: | |
| needs: static-analysis | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: "true" | |
| build-and-test: | |
| needs: [build, test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: "true" | |
| # The src-a/src-b/src-c matrix legs replaced the single test (src) leg, so | |
| # this publishes that context name once all legs pass. needs: test waits for | |
| # every matrix leg; a red leg skips this instead of greening it. | |
| test-src: | |
| name: test (src) | |
| needs: test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: "true" |