Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Status: Active Owner: Platform Architecture Phase: 1.D of docs/recommendations/DEV_VELOCITY_AND_ARCHITECTURE_PLAN_2026-04-22.md The unit-test suite is 1,657 test files / ~7,167 tests as of 2026-04-22, with a wall-clock cost of ~3 m 43 s unsharded (per reports/perf/baseline-2026-04-22.md). Vitest already runs files in parallel within a single process (pool: 'threads', maxWorkers: '50%' in CI — see vitest.config.ts), but a single CI runner still pays the full ~4-minute wall-clock. Sharding splits the suite across N parallel CI runners, each running 1/N of the files. Combined with Vitest’s per-file in-process parallelism, this can bring CI test wall-clock down to ~1 minute at N=4 (verified locally; see “Measurement” below).

Quick reference

# Local: run shard 2 of 4 (no env vars)
SKIP_SUPABASE_TESTS=true CI=true npx tsx scripts/ci/run-vitest-shard.ts --shard 2 --total 4

# Local: run the full suite (no sharding) via the wrapper
npm run test:unit:shard

# CI matrix (GitHub Actions sets the env vars for each matrix job)
VITEST_SHARD_INDEX=2 VITEST_SHARD_TOTAL=4 npm run test:unit:shard
CLI args win over env vars when both are set. Defaults to 1/1 (no sharding) which is safe to use locally. The wrapper writes a per-shard JUnit XML report at reports/test-shards/unit-${shard}-of-${total}.xml so each CI matrix job’s results can be uploaded as a separate artifact without clobbering siblings.

How Vitest sharding works

--shard=$INDEX/$TOTAL partitions the discovered test files (not individual tests) deterministically by hash. Equal TOTAL values across runs produce stable distributions, so a flake in shard 3 can be re-run as --shard=3/4 and pick up the same files. Reference: https://vitest.dev/guide/cli.html#shard
jobs:
  unit-tests:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: '22'
          cache: 'npm'
      - run: npm ci --legacy-peer-deps
      - name: Unit tests (shard ${{ matrix.shard }}/4)
        run: npm run test:unit:shard
        env:
          CI: true
          SKIP_SUPABASE_TESTS: 'true'
          VITEST_SHARD_INDEX: ${{ matrix.shard }}
          VITEST_SHARD_TOTAL: 4
      - name: Upload JUnit shard report
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: unit-shard-${{ matrix.shard }}
          path: reports/test-shards/unit-${{ matrix.shard }}-of-4.xml
          if-no-files-found: error
Switching the existing npm run test:coverage step to a sharded matrix is not done in this PR — it’s a CI-workflow change that should land in its own PR after a project owner can validate against billing budgets (matrix runs scale CI minutes linearly; 4 shards × ~1 min ≈ 4 CI minutes total vs 1 × ~4 min ≈ 4 CI minutes, but with 4× the wall-clock parallelism).

Coverage caveat

Coverage thresholds in vitest.config.ts (statements: 40, branches: 35, functions: 40, lines: 40) only make sense on the full suite, not per shard. If we wire sharding to the coverage step, we must also add a “merge coverage” job after the matrix that combines per-shard coverage/coverage-final.json files via nyc merge (or Vitest’s own merge tooling) before checking thresholds. For Phase 1.D, the recommendation is:
  • Use sharding for the fast-feedback unit-tests gate (test:unit:shard).
  • Keep the coverage step single-runner (test:coverage) until merge tooling is proven on this codebase.

Measurement on this branch

Captured on the Cursor Cloud VM (Linux, 4 vCPU / 16 GB RAM), filtering with SKIP_SUPABASE_TESTS=true CI=true:
ShardFilesTestsWall-clockNotes
1/100 (smoke)6482.78 sConfirms shard CLI works and JUnit emits correctly.
2/41361,68161 sOne quartile of the suite. Includes 5 pre-existing skips.
baseline (no sharding)5447,1673 m 43 sFrom reports/perf/baseline-2026-04-22.md.
Projected wall-clock at --total=4 (in parallel CI matrix): ~62 s — a ~3.6× speedup vs the unsharded baseline.

See also