From b6e58019f29fbe8e2c0f912abd8f99d1a28caeb6 Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Wed, 5 Aug 2026 16:29:49 -0700 Subject: [PATCH] ci: split serialized tests into five shards (#10923) ## Thinking Path > - Paperclip uses CI to keep control-plane changes safe and mergeable. > - The PR workflow splits serialized server tests across isolated runners. > - A recent successful run spent 305 seconds in serialized shard 2/4. > - That job was the slowest check in the run. > - The four shards reported about 739 seconds of Vitest suite time. > - This pull request adds a fifth serialized shard and keeps release verification aligned. > - The benefit is a shorter PR critical path with no loss of test coverage. ## Linked Issues or Issue Description **What existing behavior does this improve?** The PR and release verification workflows run serialized server tests in four shards. **Current behavior** Successful PR run 30876682788 spent 305 seconds in `Verify serialized server suites (2/4)`. The test step used 256 seconds and made this job the slowest check. **Proposed behavior** Run the same serialized suite set in five complete and non-overlapping shards. **Reason and benefit** The measured suites reported about 739 seconds of total Vitest time. Five runners reduce the expected average suite time from about 185 seconds to about 148 seconds before setup overhead. **Breaking changes** None. The change only alters CI partition size. ## What Changed - Split serialized server tests into five shards in the PR workflow. - Apply the same five-shard layout to release verification. - Add a partition test that proves complete and non-overlapping serialized coverage. - Update release workflow coverage tests for five shards. ## Verification - `node --test scripts/__tests__/run-vitest-stable-shard.test.mjs scripts/__tests__/release-verify-workflow.test.mjs` - `git diff --check` ## Risks - Low risk. CI uses one additional runner for the serialized lane. - Round-robin partition weights can still vary as suite timings change. > This change does not overlap with planned core work in `ROADMAP.md`. Related PR #10663 optimized the separate general-server lane. ## Model Used - OpenAI Codex, GPT-5, agentic coding with reasoning, tool use, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com> --- .github/workflows/pr.yml | 23 ++++++++++++------- .github/workflows/release-verify.yml | 19 ++++++++------- .../release-verify-workflow.test.mjs | 4 ++-- .../run-vitest-stable-shard.test.mjs | 12 ++++++++++ 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5c9f483db5..de0a70a6f7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -270,18 +270,25 @@ jobs: fail-fast: false matrix: include: + # A successful PR run on 2026-08-04 (30876682788) spent 256s in + # serialized shard 2/4, making its 305s job the run's slowest check. + # Five shards reduce the measured 739s suite total to about 148s per + # runner before setup overhead. - shard_index: 0 - shard_count: 4 - shard_label: 1/4 + shard_count: 5 + shard_label: 1/5 - shard_index: 1 - shard_count: 4 - shard_label: 2/4 + shard_count: 5 + shard_label: 2/5 - shard_index: 2 - shard_count: 4 - shard_label: 3/4 + shard_count: 5 + shard_label: 3/5 - shard_index: 3 - shard_count: 4 - shard_label: 4/4 + shard_count: 5 + shard_label: 4/5 + - shard_index: 4 + shard_count: 5 + shard_label: 5/5 steps: - name: Checkout repository diff --git a/.github/workflows/release-verify.yml b/.github/workflows/release-verify.yml index 890dd4aea4..cac52d75b5 100644 --- a/.github/workflows/release-verify.yml +++ b/.github/workflows/release-verify.yml @@ -109,17 +109,20 @@ jobs: matrix: include: - shard_index: 0 - shard_count: 4 - shard_label: 1/4 + shard_count: 5 + shard_label: 1/5 - shard_index: 1 - shard_count: 4 - shard_label: 2/4 + shard_count: 5 + shard_label: 2/5 - shard_index: 2 - shard_count: 4 - shard_label: 3/4 + shard_count: 5 + shard_label: 3/5 - shard_index: 3 - shard_count: 4 - shard_label: 4/4 + shard_count: 5 + shard_label: 4/5 + - shard_index: 4 + shard_count: 5 + shard_label: 5/5 steps: - name: Checkout repository diff --git a/scripts/__tests__/release-verify-workflow.test.mjs b/scripts/__tests__/release-verify-workflow.test.mjs index 3bd0a616a8..b5911ab4e8 100644 --- a/scripts/__tests__/release-verify-workflow.test.mjs +++ b/scripts/__tests__/release-verify-workflow.test.mjs @@ -43,8 +43,8 @@ test("release verify workflow covers the same split test surface as stable PR ve ); } - for (const shardIndex of [0, 1, 2, 3]) { - assert.match(verifyWorkflow, new RegExp(`shard_index: ${shardIndex}[\\s\\S]*?shard_count: 4`)); + for (const shardIndex of [0, 1, 2, 3, 4]) { + assert.match(verifyWorkflow, new RegExp(`shard_index: ${shardIndex}[\\s\\S]*?shard_count: 5`)); } assert.match(verifyWorkflow, /pnpm test:run:general -- --group/); diff --git a/scripts/__tests__/run-vitest-stable-shard.test.mjs b/scripts/__tests__/run-vitest-stable-shard.test.mjs index 9b69130527..252629bc06 100644 --- a/scripts/__tests__/run-vitest-stable-shard.test.mjs +++ b/scripts/__tests__/run-vitest-stable-shard.test.mjs @@ -29,6 +29,18 @@ function dryRunJson(args) { } const SHARD_COUNT = 4; +const SERIALIZED_SHARD_COUNT = 5; + +test("the serialized shards form a complete, non-overlapping partition", () => { + const shards = Array.from({ length: SERIALIZED_SHARD_COUNT }, (_, index) => + dryRunJson(["--mode", "serialized", "--shard-index", String(index), "--shard-count", String(SERIALIZED_SHARD_COUNT)]), + ); + + const total = shards[0].serializedSuiteCount; + const selected = shards.flatMap((shard) => shard.selectedSerializedSuites); + assert.equal(selected.length, total, "every serialized suite must be selected exactly once"); + assert.equal(new Set(selected).size, total, "serialized shards must not overlap"); +}); test("the general-server shards form a complete, non-overlapping partition", () => { const shards = Array.from({ length: SHARD_COUNT }, (_, index) =>