From 3403338ea05d231fc1b8d45c80d74e026d266a3f Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 14 Nov 2026 02:51:39 +0800 Subject: [PATCH] perf(ci): use four Vitest workers on hosted runners (#10399) Hosted CI jobs own a four-core runner but the suite ran on two workers. Other hosts keep the two-worker bound because they are shared. Co-authored-by: Claude Fable 5.2 --- docs/development/testing.md | 8 +++++--- vitest.config.ts | 10 ++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/development/testing.md b/docs/development/testing.md index c473e01c33..ecebe4a920 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -238,7 +238,8 @@ broad changes or diagnosing CI, rather than after every edit. Package/install changes additionally need package verification; provider changes need actual provider evidence. -Vitest projects use up to two workers, bounded by host parallelism. +Vitest projects use up to two workers, bounded by host parallelism. GitHub +Actions jobs own their runner and use up to four. `process-boundary` runs later with serial files because process-tree sampling shares host resources. Acceptance and process-global cases use isolated forks without serial scheduling. Pure domain/service, composition, conformance, @@ -283,7 +284,7 @@ coverage. Ordinary CI also exhausted the former 384 MiB budget after earlier files loaded the shared source/contract graph, while later command registration and schema/help rendering still needed transient allocation space. This is a measured capacity control, not a suite-wide speedup claim. The worker count -remains bounded at two, and spawned REA processes retain their own heap settings. +remains bounded, and spawned REA processes retain their own heap settings. Coverage enablement still controls instrumentation, test deadlines and coverage thresholds; explicitly disabling it or selecting only a provider does not enable instrumentation. @@ -1102,7 +1103,8 @@ is separate and grows when more shard jobs compete for runners. `process-boundary` stays serial because process-tree sampling shares the host. Raising the worker cap from two to four on this four-core host cut a six-file acceptance sample from 96–101 seconds to 71 seconds and did not remove the -single-file floor, so the cap stays two. Full qualification keeps six covered +single-file floor. Hosted CI jobs use four workers; other hosts stay at two +because they are shared with other checks. Full qualification keeps six covered shards; its longer instrumentation is outside this fast budget. The separate `CI timings` workflow batches recent completed CI and Release runs diff --git a/vitest.config.ts b/vitest.config.ts index 2da24fbea8..da60fe65ed 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -32,8 +32,14 @@ const { options: testOptions } = parseCLI( ); const COVERAGE_ENABLED = testOptions.coverage?.enabled === true; const COVERAGE_SHARD = testOptions.shard !== undefined; -// Keep local and CI concurrency bounded by the runner rather than command locks. -const MAX_TEST_WORKERS = Math.min(2, availableParallelism()); +// Bound concurrency by the runner rather than command locks. A hosted CI job +// owns its whole four-core runner, so it may use every core; other hosts stay +// at two workers because developers and agents share them with other checks. +const DEDICATED_CI_RUNNER = process.env.GITHUB_ACTIONS === "true"; +const MAX_TEST_WORKERS = Math.min( + DEDICATED_CI_RUNNER ? 4 : 2, + availableParallelism(), +); const TEST_PROJECTS = [...SOURCE_TEST_PROJECTS, ...BUILT_TEST_PROJECTS]; const PROJECT_POLICIES = {