From da91bbd4eb417405895ff2621b2798e1f598d30e Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 14 Nov 2026 03:20:11 +0800 Subject: [PATCH] test(ci): retire loader-ownership history seeding (#10404) * test(ci): retire loader-ownership history seeding Both timing tiers now publish javascriptLiteralLoaderOwnership.test.ts's own duration (fast 23.7s, full 26.6s), so the seeding returned its input unchanged. Remove it and its tests. * docs(ci): describe shard weighting without retired loader seeding --- docs/testing-lanes.md | 20 ++---- .../filesystem/ciTestSequencer.test.ts | 67 ------------------- tests/ciTestSequencer.ts | 67 +------------------ 3 files changed, 8 insertions(+), 146 deletions(-) diff --git a/docs/testing-lanes.md b/docs/testing-lanes.md index a3db828239..459b10a2fc 100644 --- a/docs/testing-lanes.md +++ b/docs/testing-lanes.md @@ -850,20 +850,12 @@ the sequencer's two-worker schedule balances every shard at about 404 seconds of file time. The same replay with the stale history weights leaves the slowest shard near 547 seconds and the fastest near 293. That spread is file-schedule time, not runner queue, which is why each tier keeps its own history. -CI assigns discovered files by historical duration, with a deterministic -fallback weight for new files, rather than dividing only by file count. -Each file keeps its full duration on one worker. Extra workers overlap other -files in the same sequence group, and `process-boundary` runs after that -group. History published before the competing-loader split still names only -`javascriptLiteralPercentPaths.test.ts`. Until an artifact records -`javascriptLiteralLoaderOwnership.test.ts`, the sequencer assigns that -recorded duration by the pre-split case counts: 54/192 stays with the -percent-path file and 138/192 goes to the consolidated loader file. History -that names the retired Early, Middle, and Late shards sums those durations -onto the consolidated file instead, and scales a partial set as three equal -shares. That weight is the old dual-surface cost. The demoted file runs every -loader once through the shared stdio MCP session, so the first schedule -overestimates it. Every discovered file still belongs to exactly one shard; +CI assigns discovered files by historical duration rather than dividing only +by file count. A file with no recorded duration, such as a newly added or +renamed test, is weighted at the median recorded duration until a published +artifact records it. Each file keeps its full duration on one worker. Extra +workers overlap other files in the same sequence group, and `process-boundary` +runs after that group. Every discovered file still belongs to exactly one shard; worker limits and project ordering remain unchanged. Fast qualification keeps eight built shards without coverage. The merge job rejects successful reports with missing, unexpected, or duplicate portable files. Coverage instrumentation diff --git a/tests/boundary/filesystem/ciTestSequencer.test.ts b/tests/boundary/filesystem/ciTestSequencer.test.ts index 7705d181d5..452bfae0e4 100644 --- a/tests/boundary/filesystem/ciTestSequencer.test.ts +++ b/tests/boundary/filesystem/ciTestSequencer.test.ts @@ -11,7 +11,6 @@ import { parseTestDurations, readTestDurations, scheduleWall, - seedLoaderOwnershipDurations, } from "../../ciTestSequencer.js"; describe("CI test history", () => { @@ -86,72 +85,6 @@ describe("CI test history", () => { }); }); -describe("loader ownership history", () => { - const source = - "tests/acceptance/applications/javascriptLiteralPercentPaths.test.ts"; - const consolidated = - "tests/acceptance/applications/javascriptLiteralLoaderOwnership.test.ts"; - const parts = [ - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipEarly.test.ts", - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipMiddle.test.ts", - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipLate.test.ts", - ]; - - it("assigns the pre-split loader share to the consolidated file", () => { - const seeded = seedLoaderOwnershipDurations( - new Map([ - [source, 192_000], - ["tests/other.test.ts", 10], - ]), - ); - expect(seeded.get(source)).toBe(54_000); - expect(seeded.get(consolidated)).toBe(138_000); - for (const path of parts) expect(seeded.has(path)).toBe(false); - expect(seeded.get("tests/other.test.ts")).toBe(10); - expect( - [...seeded.values()].reduce((sum, duration) => sum + duration, 0), - ).toBe(192_000 + 10); - }); - - it("sums retired shard durations onto the consolidated file", () => { - const seeded = seedLoaderOwnershipDurations( - new Map([ - [source, 20_000], - [parts[0]!, 30_000], - [parts[1]!, 40_000], - [parts[2]!, 50_000], - ["tests/other.test.ts", 10], - ]), - ); - expect(seeded.get(source)).toBe(20_000); - expect(seeded.get(consolidated)).toBe(120_000); - expect(seeded.get("tests/other.test.ts")).toBe(10); - }); - - it("scales a partial retired shard as three equal shares", () => { - const seeded = seedLoaderOwnershipDurations( - new Map([ - [source, 20_000], - [parts[0]!, 30_000], - ]), - ); - expect(seeded.get(source)).toBe(20_000); - expect(seeded.get(consolidated)).toBe(90_000); - }); - - it("keeps a published consolidated duration", () => { - const recorded = new Map([ - [source, 20_000], - [parts[0]!, 30_000], - [consolidated, 12_000], - ]); - expect(seedLoaderOwnershipDurations(recorded)).toEqual(recorded); - expect( - seedLoaderOwnershipDurations(new Map([["tests/other.test.ts", 10]])), - ).toEqual(new Map([["tests/other.test.ts", 10]])); - }); -}); - describe("duration shard allocation", () => { it.prop([ fc.uniqueArray(fc.integer({ min: 0, max: 10_000 }), { diff --git a/tests/ciTestSequencer.ts b/tests/ciTestSequencer.ts index 47d5eec9aa..ec80212b67 100644 --- a/tests/ciTestSequencer.ts +++ b/tests/ciTestSequencer.ts @@ -60,68 +60,6 @@ export async function readTestDurations( } } -/** - * Case counts from the competing-loader file before it was split: 54 cases - * stayed in the percent-path file and 46 ran in each of the three dual-surface - * shards. History that still names only the original path is that combined - * cost. The shards are retired; one file now runs every loader through stdio - * MCP plus a few compiled CLI parity cases. - */ -const LOADER_HISTORY_SOURCE = - "tests/acceptance/applications/javascriptLiteralPercentPaths.test.ts"; -const LOADER_HISTORY_PARTS = [ - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipEarly.test.ts", - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipMiddle.test.ts", - "tests/acceptance/applications/javascriptLiteralLoaderOwnershipLate.test.ts", -] as const; -const LOADER_HISTORY_CONSOLIDATED = - "tests/acceptance/applications/javascriptLiteralLoaderOwnership.test.ts"; -const LOADER_HISTORY_REMAINDER_CASES = 54; -const LOADER_HISTORY_PART_CASES = 46; -const LOADER_HISTORY_CASES = - LOADER_HISTORY_REMAINDER_CASES + - LOADER_HISTORY_PART_CASES * LOADER_HISTORY_PARTS.length; - -/** - * Charge the consolidated loader file until its own duration is published. - * A pre-split percent-path duration keeps 54/192 and gives 138/192 to the - * consolidated file. Recorded Early/Middle/Late durations are summed instead, - * and a partial set is scaled as three equal shares. That weight is the old - * dual-surface cost, so the first schedule overestimates the demoted file. - * A published consolidated duration stands, including beside retired shards. - */ -export function seedLoaderOwnershipDurations( - durations: ReadonlyMap, -): Map { - if (durations.has(LOADER_HISTORY_CONSOLIDATED)) return new Map(durations); - const recordedParts = LOADER_HISTORY_PARTS.flatMap((path) => { - const duration = durations.get(path); - return duration === undefined ? [] : [duration]; - }); - if (recordedParts.length > 0) { - const seeded = new Map(durations); - const sum = recordedParts.reduce((total, duration) => total + duration, 0); - seeded.set( - LOADER_HISTORY_CONSOLIDATED, - (sum * LOADER_HISTORY_PARTS.length) / recordedParts.length, - ); - return seeded; - } - const combined = durations.get(LOADER_HISTORY_SOURCE); - if (combined === undefined) return new Map(durations); - const seeded = new Map(durations); - const loaderCases = LOADER_HISTORY_CASES - LOADER_HISTORY_REMAINDER_CASES; - seeded.set( - LOADER_HISTORY_SOURCE, - (combined * LOADER_HISTORY_REMAINDER_CASES) / LOADER_HISTORY_CASES, - ); - seeded.set( - LOADER_HISTORY_CONSOLIDATED, - (combined * loaderCases) / LOADER_HISTORY_CASES, - ); - return seeded; -} - type TimedItem = { item: T; key: string; @@ -263,9 +201,8 @@ export class CiTestSequencer extends BaseSequencer { const shard = this.ctx.config.shard; if (!historyPath || !shard) return super.shard(files); const root = this.ctx.config.root; - const recorded = await readTestDurations(historyPath, root); - if (!recorded) return super.shard(files); - const durations = seedLoaderOwnershipDurations(recorded); + const durations = await readTestDurations(historyPath, root); + if (!durations) return super.shard(files); const weighted = files.map((item) => { const path = relative(root, resolve(root, item.moduleId))