From 8ca31e085cfefbcd0e6f4bdb9d46b20905a9d8e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 06:26:36 +0000 Subject: [PATCH 1/3] ci(test-shards): place Test Core bins by predicted wall, so a CLI slice shard carries no whole-package leg ahead of its serial slice A shard runs its whole packages four at a time and then each file-level slice alone, so its tests step reads ~wholeSum / 2.9 + slice (the #25376 wall model). partition() balanced plain sums, which filled every slice shard up to the mean with whole packages that ran before the slice. It now places by load (a slice counts 2.9x its weight); a split with no slice is placed byte-identically. Claude-Session: https://claude.ai/code/session_01NPH84ZP1jWcst3tpRwfrJL Co-authored-by: Claude --- scripts/partition-test-shards.mjs | 149 +++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 15 deletions(-) diff --git a/scripts/partition-test-shards.mjs b/scripts/partition-test-shards.mjs index d2ecd156af6..855dc43a895 100644 --- a/scripts/partition-test-shards.mjs +++ b/scripts/partition-test-shards.mjs @@ -157,6 +157,12 @@ const TIMINGS_PATH = path.join(REPO_ROOT, 'scripts', 'test-shard-timings.json'); // beside them. That last figure is the floor this count cannot move: a CLI // slice (~960-1010s measured) is serial, so past eight the slowest shard is // the slice and more shards only add runners. +// +// ⚠ The bins above were placed by their SUM. Since #25870 partition() places +// them by that same wall model (WHOLE_LEG_PARALLELISM), so a slice shard no +// longer carries a whole-package leg ahead of its slice; on the committed +// dataset's full list the predicted steps move from 1277s on the three slice +// shards (643s elsewhere) to 966s on the slice shards and 830s elsewhere. export const SHARD_COUNT = 8; // The acceptance bound #10472 set: the slowest shard within ~1.3x the mean. @@ -881,21 +887,58 @@ function readManifest(dir) { } } -// LPT greedy: heaviest package into the currently lightest bin. Deterministic: -// input order never matters because both the package sort and the bin choice -// break ties explicitly (by name / by lowest bin index). +// ── A SLICE IS A SERIAL LEG: BINS ARE PLACED BY WALL, NOT BY SUM (#25870) ──── +// +// A shard's "Run this shard's tests" step runs its whole packages four at a +// time (`--concurrency=4`) and THEN each file-level slice as a turbo run of its +// own, alone on the runner (ci.yml). So a slice's seconds reach the wall one for +// one while a whole package's are shared about 2.9 ways -- the wall model the +// SHARD_COUNT header calibrated (#25376): step ~ wholeSum / 2.9 + slice. This +// constant is that 2.9, not a new measurement. +// +// Placing bins by their SUM of seconds treated a slice second like a whole +// second, so every slice shard was filled up to the mean with whole packages and +// then ran its slice after them. On the committed dataset's full list that put +// ~900s of whole packages in front of each 966s CLI slice: predicted step 1277s +// on the three slice shards against 643s on the other five, and measured the +// same way on the pull_request runs #25870 tabulates -- 3/8-5/8 at 25-29 min, +// the rest at 7-17, the gap being each slice shard's whole-package leg +// (`Run this shard's tests` minus the slice leg's own turbo `Time:`). +// +// So partition() places by LOAD -- a bin's predicted wall, kept in whole-package +// seconds (a slice counts WHOLE_LEG_PARALLELISM times its weight) so that a list +// with no slice is placed by exactly the same additions and comparisons as the +// plain-sum LPT it replaced, byte for byte. `total` stays the plain sum of +// predicted test windows: the density cap and the drift report are statements +// about that quantity, and neither changes meaning here. +export const WHOLE_LEG_PARALLELISM = 2.9; + +// One item's share of a bin's load, in whole-package seconds. +export function loadOf(item) { + return item.slice ? item.weight * WHOLE_LEG_PARALLELISM : item.weight; +} + +// A bin's predicted `Run this shard's tests` step under the wall model. +export function wallOf(bin) { + return bin.load / WHOLE_LEG_PARALLELISM; +} + +// LPT greedy on load (above): heaviest item into the currently lightest bin. +// Deterministic: input order never matters because both the item sort and the +// bin choice break ties explicitly (by name / by lowest bin index). export function partition(items, shardCount) { const sorted = [...items].sort( - (a, b) => b.weight - a.weight || a.name.localeCompare(b.name, 'en') + (a, b) => loadOf(b) - loadOf(a) || a.name.localeCompare(b.name, 'en') ); - const bins = Array.from({ length: shardCount }, () => ({ total: 0, names: [] })); + const bins = Array.from({ length: shardCount }, () => ({ total: 0, load: 0, names: [] })); for (const it of sorted) { let best = 0; for (let i = 1; i < bins.length; i++) { - if (bins[i].total < bins[best].total) best = i; + if (bins[i].load < bins[best].load) best = i; } bins[best].names.push(it.name); bins[best].total += it.weight; + bins[best].load += loadOf(it); } return bins; } @@ -904,13 +947,34 @@ export function partition(items, shardCount) { // `floor` is the heaviest single package: sharding is BY PACKAGE, so no split // at any shard count can put max below it, and comparing it to the mean says // whether the bound is even reachable before anyone tries to reach it. +// +// `walls` / `wallRatio` are the same statistic on what the bins were placed by +// (wallOf, #25870). With no slice in the split they are the totals scaled by +// one constant, so the two ratios agree; with a slice, only the wall ratio says +// whether the shards finish together. A bin built by hand (no `load`) is read +// as all whole packages. export function balanceOf(bins, items = null) { const totals = bins.map((b) => b.total); const sum = totals.reduce((a, b) => a + b, 0); const mean = sum / bins.length; const max = Math.max(...totals); const floor = items ? Math.max(0, ...items.map((i) => i.weight)) : null; - return { totals, sum, mean, max, min: Math.min(...totals), ratio: mean === 0 ? 1 : max / mean, floor }; + const walls = bins.map((b) => wallOf({ load: b.load ?? b.total })); + const wallMean = walls.reduce((a, b) => a + b, 0) / bins.length; + const wallMax = Math.max(...walls); + return { + totals, + sum, + mean, + max, + min: Math.min(...totals), + ratio: mean === 0 ? 1 : max / mean, + floor, + walls, + wallMean, + wallMax, + wallRatio: wallMean === 0 ? 1 : wallMax / wallMean, + }; } // ── THE DENSITY CAP: NO SHARD CARRIES MORE THAN THE SPLIT PROVEN GREEN (#22075) ─ @@ -970,7 +1034,7 @@ export function refineToCap(bins, items, cap) { const weightOf = new Map(items.map((i) => [i.name, i.weight])); const slices = new Set(items.filter((i) => i.slice).map((i) => i.name)); const movable = (name) => !slices.has(name); - const out = bins.map((b) => ({ total: b.total, names: [...b.names] })); + const out = bins.map((b) => ({ total: b.total, load: b.load, names: [...b.names] })); const EPSILON = 1e-9; for (let step = 0; step < 10_000; step++) { let h = 0; @@ -997,15 +1061,21 @@ export function refineToCap(bins, items, cap) { } if (best === null) break; const { to, give, take } = best; + // Only whole packages move, and a whole package's load is its weight, so + // `load` follows `total` move for move. out[h].names.splice(out[h].names.indexOf(give), 1); out[h].total -= weightOf.get(give); + out[h].load -= weightOf.get(give); out[to].names.push(give); out[to].total += weightOf.get(give); + out[to].load += weightOf.get(give); if (take !== null) { out[to].names.splice(out[to].names.indexOf(take), 1); out[to].total -= weightOf.get(take); + out[to].load -= weightOf.get(take); out[h].names.push(take); out[h].total += weightOf.get(take); + out[h].load += weightOf.get(take); } } return out; @@ -1483,7 +1553,9 @@ const SELF_TEST_BATTERIES = Object.freeze({ // 8 of these 25 are pins 3b and 3c, the file-level slicing's (#16173), and // every one of them runs unconditionally: 3c's fixtures and 3b's cut of the // heaviest package do not depend on what the live map slices. - 'the balancing pins (#10472)': 25, + // 2 of these 27 are pin 2b, the wall placement's (#25870): a fixture, so it + // runs whatever the dataset holds. + 'the balancing pins (#10472)': 27, 'predicted-vs-measured drift (#16173)': 9, 'drift warning tier under the red (#16465)': 12, 'file-level slice items (#16173)': 20, @@ -1730,15 +1802,54 @@ function selfTest() { }); const real = datasetPlan.bins; const balance = balanceOf(real, datasetItems); + // Graded on predicted WALL, the quantity the bins are placed by (#25870): with + // a slice in the split, the bin SUMS are deliberately unequal -- a slice shard + // carries its serial slice and little else -- and only the walls say whether + // the shards finish together. With no slice the two ratios are the same number. + check(() => { + if (balance.wallRatio > MAX_SHARD_OVER_MEAN) { + throw new Error( + `balance: at ${SHARD_COUNT} shards the slowest predicted test step is ${balance.wallRatio.toFixed(2)}x the mean ` + + `(${balance.wallMax.toFixed(0)}s vs ${balance.wallMean.toFixed(0)}s), past the ${MAX_SHARD_OVER_MEAN}x bound. ` + + 'Walls: ' + balance.walls.map((t) => t.toFixed(0)).join('/') + 's; bins: ' + + balance.totals.map((t) => t.toFixed(0)).join('/') + 's.' + ); + } + }); + + // 2b. A SLICE IS PLACED AS THE SERIAL LEG IT IS (#25870). Three shards, a + // package cut in two (100s a slice) and three whole packages (100s, 50s, + // 50s). Placed by SUM -- the LPT this replaced -- each slice shard also + // takes a 50s whole package, which runs four-wide BEFORE the slice and + // lengthens that shard's step to ~117s. Placed by wall, the slices run + // alone and the three whole packages share the third shard (~69s). The + // two placements differ, so a revert to the sum reds here. + const serial = partition( + [ + { name: 's 1/2', weight: 100, slice: { index: 1, count: 2 } }, + { name: 's 2/2', weight: 100, slice: { index: 2, count: 2 } }, + mk('x', 100), + mk('y', 50), + mk('z', 50), + ], + 3 + ); check(() => { - if (balance.ratio > MAX_SHARD_OVER_MEAN) { + const crowded = serial.filter((bin) => bin.names.some((n) => n.startsWith('s ')) && bin.names.length > 1); + if (crowded.length > 0) { throw new Error( - `balance: at ${SHARD_COUNT} shards the heaviest bin is ${balance.ratio.toFixed(2)}x the mean ` + - `(${balance.max.toFixed(0)}s vs ${balance.mean.toFixed(0)}s), past the ${MAX_SHARD_OVER_MEAN}x bound. ` + - 'Bins: ' + balance.totals.map((t) => t.toFixed(0)).join('/') + 's.' + `slice placement: a slice shard also carries whole packages (${crowded.map((b) => b.names.join('+')).join('; ')}) ` + + 'that run before its serial slice -- are the bins placed by their plain SUM again?' ); } }); + check(() => { + const walls = serial.map(wallOf); + const expected = [100, 100, 200 / WHOLE_LEG_PARALLELISM]; + if (walls.some((w, i) => Math.abs(w - expected[i]) > 1e-9)) { + throw new Error(`slice placement: predicted walls ${walls.map((w) => w.toFixed(2)).join('/')}s, expected ${expected.map((w) => w.toFixed(2)).join('/')}s`); + } + }); // 3. The bound is REACHABLE at this shard count -- the arithmetic #10472 // asked about when it floated 6 -> 8. No split can put the heaviest bin @@ -3013,7 +3124,8 @@ function selfTest() { console.log( `partition-test-shards: self-test OK (${datasetPackages.length} measured packages ` + `-> ${datasetItems.length} shard items, ${SHARD_COUNT} shards, ` + - `max/mean ${balance.ratio.toFixed(2)}x <= ${MAX_SHARD_OVER_MEAN}x, floor ${balance.floor.toFixed(0)}s, ` + + `predicted test-step walls ${balance.walls.map((t) => t.toFixed(0)).join('/')}s, ` + + `max/mean ${balance.wallRatio.toFixed(2)}x <= ${MAX_SHARD_OVER_MEAN}x, floor ${balance.floor.toFixed(0)}s, ` + `bins ${balance.totals.map((t) => t.toFixed(2)).join('/')}s within the ${datasetCap.toFixed(2)}s density cap, ` + `file-level slices: ` + `${datasetPlan.decisions.filter((d) => d.count > 1).map((d) => `${d.name} x${d.count}`).join(', ') || 'none'})` @@ -3136,7 +3248,7 @@ function main() { { excluded } ); const mine = bins[shardIndex - 1]; - const { max, mean, ratio } = balanceOf(bins); + const { max, mean, ratio, walls, wallRatio } = balanceOf(bins); // Printed on every shard, not just the imbalanced one, and printed as the // RATIO the acceptance bound is written in: the per-shard numbers alone never // said whether the split was balanced, which is why #10472's imbalance had to @@ -3150,6 +3262,13 @@ function main() { `density cap ${cap.toFixed(2)}s; ` + `${packages - estimated} measured, ${estimated} estimated from test-file count)` ); + // What the bins were placed by (wallOf, #25870): the test step each shard is + // predicted to take, a slice counted as the serial leg it runs as. + console.error( + ` predicted test-step walls (whole / ${WHOLE_LEG_PARALLELISM} + slice): ` + + `${walls.map((w) => w.toFixed(0)).join('/')}s, this shard ${wallOf(mine).toFixed(0)}s; ` + + `max/mean ${wallRatio.toFixed(2)}x` + ); // Only a document that states its own slicing prints this, so a Test Core // shard's log is unchanged; the nightly's says where its slice count came from. if (acrossShards !== null) { From b389ba08b3e4c71033157ae0294044ea67c4b3cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 05:47:33 +0000 Subject: [PATCH 2/3] chore(ci): refresh the Test Core shard-timings dataset Regenerated by .github/workflows/shard-timings-refresh.yml from the test-core-run-summary artifacts of 14 accumulated run(s) (40272142430 40266979145 40262173089 40257470166 40242835329 40238189261 40233481335 40228747755 40223991179 40219252911 40214410982 40209651657 40204927516 40200279306), newest 40272142430 at 2a728ba2acf52c13d1918c2afca7b79bf391e1f5. Generated, never hand-edited. --- scripts/test-shard-timings.json | 650 +++++++++++++++++++------------- 1 file changed, 394 insertions(+), 256 deletions(-) diff --git a/scripts/test-shard-timings.json b/scripts/test-shard-timings.json index 946dd83387d..e68f9b230d5 100644 --- a/scripts/test-shard-timings.json +++ b/scripts/test-shard-timings.json @@ -1,192 +1,291 @@ { "note": "GENERATED by scripts/measure-test-shard-timings.mjs -- do not hand-edit. Per-package `turbo run test` durations in seconds, the balancing input for the Test Core shard split (scripts/partition-test-shards.mjs). See `provenance.refresh` to regenerate. Every weight is a real measurement: the ones in `carriedOver` were measured by an earlier refresh and re-confirmed unchanged by a turbo cache HIT in this one. A weight is the median of the executed runs that measured it; the ones in `provisional` rest on fewer than `provenance.minimumRuns` of them -- fit to balance on, NOT a baseline to set a ceiling from. `ceilings` is each package's suite-duration ceiling, graded by scripts/check-test-suite-ceilings.mjs: its slowest executed run in the refresh window that first set it, times `provenance.ceilingHeadroom`, then held by every later refresh -- raised only by a ruling, never by a refresh. `uncapped` names the packages with none, and why. `density` is the window's density reading: each run's count of executed packages (cache replays excluded) and each measured package's median share of its runs' executed seconds -- what the next refresh's diff judges a rise against.", "provenance": { - "measuredAt": "2026-10-23", + "measuredAt": "2026-10-26", "summaries": [ - "3L5pkkPHrqIIhRQweIZni3Jx7aO.json", - "3L5pK0YguKIvCY56IGYC68ubcnZ.json", - "3L5r7HrLBpJw06Kn9XvmCJKAk2I.json", - "3L5qPyNpntYIVqHv5kttg7ouTyb.json", - "3L5orAUkQaLbM91Eomq4Lp41E2I.json", - "3L5or8L4X8hDTrlB8fFYvDl14Nr.json", - "3L5p97SK55GLiCOyNWcQ0KPPYny.json", - "3L5pYfbQZQq7z6GuC4x4NEro8nt.json", - "3L5pYgQhfn9F1YfZWcZs3m75wLJ.json", - "3L5if5AHrl7wQgsMo3ih11gEmqi.json", - "3L5hSfDHmwBh5GMQNMGeg3mgpBD.json", - "3L5jEVrlkZ9AUR9obG1RxghGaNO.json", - "3L5jKNwhp3zxrgEkAdOEMu7HJiz.json", - "3L5ix9jJ5LfgjPDCVM23vrGANPf.json", - "3L5hYzIHzYbTnqZthz4E8Acoey6.json", - "3L5iwScsRQslaZqoufX9q49Ca1Y.json", - "3L5kHHjXj3jd2ikEXJkYAr2qoNg.json", - "3L5iMqqYvLtphnuVU2790j4BcOs.json", - "3L5aYyVM6hDZDXM2wUgLAzVn1lK.json", - "3L5b8ackq0B6vLxB1gJvGHvPwm5.json", - "3L5cwiNtOq0V3rj8Y3t0cpp099W.json", - "3L5a8o5LwuAyjA4N4ZHocot55Y2.json", - "3L5bCbVihHlFDbW27FNwV44BRAx.json", - "3L5dT9aCViSVhEDNf1glxKoUaPK.json", - "3L5aFXABy8dYjmenqLEja1JVybr.json", - "3L5dJx1cAfph7mWxor3zJT2UYI1.json", - "3L5bG4aQQV63WVrO3IpmJSur08r.json", - "3L5V0fYBXrPNFbEUIfP6XZ35QDi.json", - "3L5VJjjOdLIiHZSNqsrkIyYU9xA.json", - "3L5TjnxBup2JEvkC531UdO5iWTj.json", - "3L5VB2bblXMBlcCEImC68dliybU.json", - "3L5WEzoPTRwkpRiFoQ32OVzqO1V.json", - "3L5TzKPzELsixHpzSjlTIHpoU6f.json", - "3L5V6en0ePK8VGrYZov45WkLSaL.json", - "3L5U2MRXW2XALPTb0VzKo6dNFJg.json", - "3L5W57i8V6y1S6QzwcVd0yPwR0G.json", - "3L5NBqXWi7RH6wc1MHy717jo4b0.json", - "3L5P0U39kjIKstpb6wjyOjtmH5I.json", - "3L5ND5kM4oQJOYTfUJpDjFQKWsx.json", - "3L5NPoU7qJwhN7znV0srql9bw4Q.json", - "3L5NAdhsrWpMHqrza9YUkmWhbPd.json", - "3L5PSHkSEL8Qg8Njjjsdw2pz0hN.json", - "3L5NuylkQZ95nfitetQsSE4hvkk.json", - "3L5PkJwVZ43AQr28FfYYXyszhwu.json", - "3L5NeKyqy7O5jwElRRKlNYSJQv5.json", - "3L5FxoGDDW0aAns2yrHnVBLzzHa.json", - "3L5GofvOX3G4fWKovi2RjHNWRaB.json", - "3L5F4WWgME9vI6HK7iu0GBEJ0U9.json", - "3L5GDreVG7PT584aKMhIoEKLJFY.json", - "3L5FFQAu9qmm3VTgQbMZlSVvMY9.json", - "3L5HUQ5lfq5ym4mVY13SNNnFZ74.json", - "3L5Fd18OtpYfA89sDRpunV8qEgS.json", - "3L5ExCnfNxu0b2zJMAlU1NcsUqE.json", - "3L5GFbyUvTxjQxh2EUvxBQMxhbp.json", - "3L58EQzDDayMHI3g6DkmeodxKy9.json", - "3L57uQhzTpHqcPBN54RDFEET4Uz.json", - "3L59kg4N8hqgKoxz9cma89rnsGs.json", - "3L58xrCgLhQ3w6qiwBcLJsL4Gvc.json", - "3L57ykdu32hsG0CEyrpXurC33qg.json", - "3L5AHpdhkBPonHmH3q7y1nRYPZf.json", - "3L58JpBeQnSkG3glDgcCgs1Xgdd.json", - "3L59O1y0GZpYg2ai5VcbUHzqtVI.json", - "3L57nHTeD92ORte7cpJ1OIMS1rD.json", - "3L51mYL41RY1q8Er1eXMCmPjT7Z.json", - "3L52WQJ80eyu4PyyCTF9xfXd151.json", - "3L50glJH53t1JTZIQkxQFF0QpGB.json", - "3L51gYDjx9oKOgRqr3VI7MNogrX.json", - "3L52xWsBkNA851yAh8q1iograbK.json", - "3L50i4R33lk0o6XZ82F7WzPeQt1.json", - "3L51qEQLz6BJ1d9zlYXzqvlpo0O.json", - "3L50ngZJEiGlvN8YX4FuuAENKoF.json", - "3L52nHJeQYh3sAmrzVfKtD1vZqj.json" + "3LDaSZoydWFxKiH8lVKLKVxGaMJ.json", + "3LDaPIV2dIpgEvVGKFxYd6HvYkT.json", + "3LDaO15zRDYZSNL1mB4enxuV3pa.json", + "3LDaOA8UT8QuAmkEjdJ2ih9ciUl.json", + "3LDaO4ahLYajwSYg8Iy4e79uMZL.json", + "3LDaQTO1pa0xrf6skNVq1PTxAYj.json", + "3LDaQbbOFROA2if4SYKnPJnY8o6.json", + "3LDaNRvMdnlmDnRZo3E35ef7ks1.json", + "3LDaOeEKoicr9j7n9bhhFslV63z.json", + "3LDaRBIewlANXPlK3igFDETMWOI.json", + "3LDaRBTVuCafN7r6ZM5ZfFqjXx8.json", + "3LDUYCGqjs1sRzM1Hb1WD6PIwhN.json", + "3LDTsfHrAX3sOq0c6nnMHcuPx6Y.json", + "3LDTG0QWuTkpMMqTfVkFtgLyW0D.json", + "3LDTCt9nMnjs1OzDOslA4UsgCvu.json", + "3LDTD0OxpF7fQ5DdY8H1NtGZvFQ.json", + "3LDTbtbIiyRWaCz6X3v7kGwNVWQ.json", + "3LDVEm7r4dtDMAyjwMuVV0zmXc0.json", + "3LDTT8XKvxfOaZRj53OyH8xWSBF.json", + "3LDTFqIBMVBKsY28virwghMkFiL.json", + "3LDTKBXlf47dz3hfnKZMHkaTx9s.json", + "3LDTKLgrAjBCoXlvkdXVlk5d06g.json", + "3LDN6nIRzg0SK0Bfe5cFQYDpSGm.json", + "3LDMrn1JsFnStMbyMsWsjRCIppa.json", + "3LDM8vCv7eT8HJFiv6i1ba8F5C4.json", + "3LDM6YchNjUNnEX1mmCus3d5Lto.json", + "3LDM6XTlK0HzBh35UmBt3P9iTBG.json", + "3LDM5kcy0SUDC9jEeQmcPEihAji.json", + "3LDM5lyTXtiyMEprJRWvtNQhV6f.json", + "3LDMTBfery6kKunHMlt7Kd0szBw.json", + "3LDMAG5HG3glYUEjSdFbueF6BPE.json", + "3LDM7rqhvMfLuIe1brpwhqWirHV.json", + "3LDM7wRFUaB4zc3VwgshO1mBSCD.json", + "3LDGKabmuOiTpeutCfpQ8MCG3JL.json", + "3LDG0vuLgEd5aQwcBTlS0YBgnQp.json", + "3LDFJsWivFuJiiGHDEwILPwlcz4.json", + "3LDF8vkb02p6SNpuS4vKirjVwQ9.json", + "3LDF8xIQIUHmWeT9toL0o4jREsW.json", + "3LDFAqSn0y9wDS0lmJ1mYwamSPH.json", + "3LDFAs6AkJW4lWJOl3Pixh9EB4q.json", + "3LDFfH4jECaxoW15YV8XBjfpCwh.json", + "3LDFENmkz0oxlagjizS7tCB1mkd.json", + "3LDFGb6shtRRGKZE87sDkAOwhXb.json", + "3LDFGlhe5FUP6cAlO6a2rNhZ8Dv.json", + "3LCtwDL1RvwV3LsCCj5Pj7yN6Aj.json", + "3LCsVp9yNcuFTd4W1h2Uxny5Jpq.json", + "3LCsW62HFSmQutLoRc531TTmPmo.json", + "3LCv0vMFXsWiaOcjshFuUh0rweo.json", + "3LCtBosMSnAnSxYZxeaKp4puEo9.json", + "3LCsnLxYRS4BfVzGWACILMH0QlK.json", + "3LCtoOMEqIQZClJRjfIjl2WzvTV.json", + "3LCsTzeKKUrcWhmkhp5rXbFrmzq.json", + "3LCsSJDnOPW4vmOIKa580tpvczO.json", + "3LCv5DaXWaRqna0YqJzWSndYgGG.json", + "3LCsxEGt7TyrgdzwLBdNQnCj7ce.json", + "3LCmV3rMTs7wtFpTipqfnE0OTwk.json", + "3LClu2fJ5CL0KMrIF9NZxMsdHRA.json", + "3LClA8CyF7dJLIUAwrWJPMuolIP.json", + "3LCl9j2pcY0fUVyK6Mgwnt3B7v7.json", + "3LCl9deGxEkkmULZ5DvEM7f2dtP.json", + "3LClB22Q5pGTJNT4GhEhOfWBdNX.json", + "3LClB3eWfhAQUjsqZK8uibbrpyE.json", + "3LCl9LJwdmCIaWVwiKLHP7jtqMN.json", + "3LClHHXv8z3FYmPyqk1mKFNIXAg.json", + "3LCl8eYIdHlVT0ZItbe0UlbtmKO.json", + "3LCl8g0miILcr770UuUyGNaqfDw.json", + "3LCekjGcB6kfA0LBQVOtvZdyJwz.json", + "3LCeMJJubkzKlSLBUV4mhAw567n.json", + "3LCdsRub0h5xiZQTkx3HNCT1eqi.json", + "3LCdsCwP2A23ygiG4D3V2kJ9kAJ.json", + "3LCds6jOAUoZwUPIHDRnnmLkrBB.json", + "3LCdrA94CAIwysH3gLzmUkgtRah.json", + "3LCdrCFQYpMAaK0ABwdKmzlAQmt.json", + "3LCdteoI6PV9ApQpI9YjSyfPZQu.json", + "3LCdyf3bmXHqc8h0UPBDUd1LBem.json", + "3LCdw436KV0AeGSejShfHD60VZC.json", + "3LCdw6a1kqW9GzNKyNVvZI9rklg.json", + "3LCWX5J71I4rp7iKfQtsV5bOLx5.json", + "3LCWWsjoYHuNYKsj1F8SjA4HC9J.json", + "3LCWXBsBFOQkOVzU7fDlF49yEdg.json", + "3LCWX7QRGj9b82MbiyFv381x07W.json", + "3LCWXIsNky4qPD33mw15CaLU36t.json", + "3LCWYRaXK6Ypls3igfLhLxVp1hb.json", + "3LCWYKzowb5ktvobYfcml0W935G.json", + "3LCWYhVNJBuCoBVbca4apYB7XL8.json", + "3LCWY1xSWspaDuQAydMknIyP11C.json", + "3LCWW4w8notGugVkPyzBE8fDOUk.json", + "3LCWVzeqeINeG89tTqRZC0kEERu.json", + "3LCPFGjCJGDv96aKr0AsNKJS1ak.json", + "3LCPFUFYq2fszNDtFZn933fjTe8.json", + "3LCPFikJ4vlA9FrvABcM2utNGsq.json", + "3LCPFVLm15Tf6OeOwvceW9TLKA2.json", + "3LCPFPVv5NuabV7YPH5ZngI20ul.json", + "3LCPGsKYGTTKFwHqlRiNZtXDMxi.json", + "3LCPGv1syvP1CH86XNr33ZqPgpl.json", + "3LCPJvzLONBV1x8BfgA1YaLAVhw.json", + "3LCPFUCIBvdSaZ4qsQapAUMvKzF.json", + "3LCPEMxwnLKkKwpClLlXqmdrdiM.json", + "3LCPEO2ZWEPaxLnB4YUoHsdCUEx.json", + "3LCI0yuSEEwpDcv3mxTAabYsT74.json", + "3LCHzEibBmoaUQnasJ8dlmBXNAz.json", + "3LCI0Hyl8qxTnjCNL6h7ZVMlCeL.json", + "3LCHywitYKodFp5sSHabAXsUErB.json", + "3LCHz02JdnDgSLfiKj219achauU.json", + "3LCI1wF1e6DFIxxoIDxkGCMhjPm.json", + "3LCI225OECHtfdYnPG1stRhkS4A.json", + "3LCI1QxNYHbdkEVlUG1NauyzzdI.json", + "3LCI1euhgYjFncNbXEAZqLCJjbn.json", + "3LCI3DFmgnMsN5G68yfkOfWSZ0t.json", + "3LCI3CxQgQCISZCrV0Nn8ueHY5o.json", + "3LCC3rxx7LgNW7HKq0TrE4lZ3hI.json", + "3LCBbGH6vf6UKso8eF0icozaNYO.json", + "3LCAf6zNl797qrzrnaGUkv4tL7e.json", + "3LCBMyYBtLCSRYvfGYybeEZIkvw.json", + "3LCDD7DDnNUzbunqOjvBzfYzp5L.json", + "3LCAg06xKPufsgt8nyvIEo8DxlJ.json", + "3LCAg1BKIQRtwwOy5WWNJJ0UzVT.json", + "3LCAfNaWjSREo9QdnPSj2XBUUi9.json", + "3LCAftw6pqPiGQ2vIJPMyo1cofh.json", + "3LCAgTku9pHMsbPzXARi2V7Gx4E.json", + "3LCAgMRb0ZKJgXVwYr6hlUW0hi2.json", + "3LC4G3pPljtgnqvbMI7wHYweqU4.json", + "3LC42H4n3ghFeZGKSeBoXNEXeAC.json", + "3LC3LNHU6fXoQqP1Zrl9yWP3RvB.json", + "3LC409r9udvJ7ZqAjAe3rp1jfju.json", + "3LC5oojMAwIlwjm0nucDrW7IKOv.json", + "3LC3MIwksYBlx1BxAdaTMHrWb7E.json", + "3LC3MHdka4d4VTGwYcyF33J6EmL.json", + "3LC3LTCF4rRUyftjSLNmjjC3Bbd.json", + "3LC3M3tOMzsRJCJjXKKiXECiv29.json", + "3LC3KdDX3FsPzb5JXfKWmn49OuU.json", + "3LC3KgCkjiACXvOmaveS1ramgZH.json", + "3LBxXKoHflSvnVUxOGzmkfYAhxw.json", + "3LBxZjd6LkCX0hm3VHitN0bFbxh.json", + "3LBxQYi4KdBwoLjrfAW3Qc6sI9f.json", + "3LBxu3ApUnTrZtNTGTOrAYpgDgd.json", + "3LBwZxgStewMCWZB0DbJM5yDC97.json", + "3LByUkS0cMbQX3tuPekDh7yMrWX.json", + "3LBweyM37YiWR5BITbl8heAJ85y.json", + "3LBx52yfzG5egICSnFplmlhcos4.json", + "3LBxCCnd9t5oBDHOLDcuJPNgBsO.json", + "3LBwcOsrluJGy92YNKAfy93bM3N.json", + "3LByvd9eelZsmzimdQKpMx14Pjy.json", + "3LBpfnSHdoQPrufWbCIA3oYWMhQ.json", + "3LBpyRLEXr3FNvGPsV4K3NQi8ie.json", + "3LBq9Ly9kxM8spEL5Qpym9zUQqm.json", + "3LBrh12BGhRd1AdSpFfqp8sw995.json", + "3LBpdiEdj7OblBJgcg4Y4ZDIvCZ.json", + "3LBqeJD8XmRkGDPd746sMQZygSS.json", + "3LBpCjeKQnOUkJalK9527767wmB.json", + "3LBqAkoUaOrYcpfoMo2FCqzt9vK.json", + "3LBqehpRQHGFC6hD0t1tYbKjBAO.json", + "3LBpMqAuVndlJG2KRDjQN4Bv2WV.json", + "3LBrf9R97F4LvQJXKdxaKYDuLKM.json" ], "runs": [ - "39943147947", - "39936055900", - "39928834751", - "39921744055", - "39915751898", - "39909025131", - "39903431405", - "39898100438" + "40272142430", + "40266979145", + "40262173089", + "40257470166", + "40242835329", + "40238189261", + "40233481335", + "40228747755", + "40223991179", + "40219252911", + "40214410982", + "40209651657", + "40204927516", + "40200279306" ], "mergeRule": "median across runs; a file-sharded package is summed from its slices WITHIN one run first, and a run that cannot assemble every slice contributes no sample for it; a package not measured in this pass keeps its previous weight ONLY when a turbo cache HIT witnesses that its inputs are unchanged, and every such package is named in `carriedOver`", - "refresh": "node scripts/measure-test-shard-timings.mjs [--run ] ... --merge-into scripts/test-shard-timings.json --out scripts/test-shard-timings.json (summaries: the `test-core-run-summary--of-6` artifacts of any green run, or a local `pnpm exec turbo run test --concurrency=4 --summarize`. ⚠ ONE RUN COVERS ONLY 2-52 of ~71 packages depending on cache warmth, so feed SEVERAL runs -- a `--run ` before each run's summaries is REQUIRED, so a sliced package is assembled per run and then medianed like every other package -- and `--merge-into` to carry the cache-hit remainder. A package measured by fewer than `minimumRuns` executed runs is named in `provisional`, so feed runs until that list is empty. `.github/workflows/shard-timings-refresh.yml` does all of this weekly.)", + "refresh": "node scripts/measure-test-shard-timings.mjs [--run ] ... --merge-into scripts/test-shard-timings.json --out scripts/test-shard-timings.json (summaries: the `test-core-run-summary--of-` artifacts of any green run, or a local `pnpm exec turbo run test --concurrency=4 --summarize`. ⚠ ONE RUN COVERS ONLY 2-52 of ~71 packages depending on cache warmth, so feed SEVERAL runs -- a `--run ` before each run's summaries is REQUIRED, so a sliced package is assembled per run and then medianed like every other package -- and `--merge-into` to carry the cache-hit remainder. A package measured by fewer than `minimumRuns` executed runs is named in `provisional`, so feed runs until that list is empty. `.github/workflows/shard-timings-refresh.yml` does all of this weekly.)", "minimumRuns": 3, "ceilingHeadroom": 1.25 }, - "secondsPerTestFileFallback": 3.244, + "secondsPerTestFileFallback": 2.114, "packages": { - "@objectstack/cli": 2899.02, - "@objectstack/client": 147.21, - "@objectstack/client-react": 12.44, - "@objectstack/cloud-connection": 268.04, - "@objectstack/connector-imap": 3.67, - "@objectstack/connector-mcp": 43.15, - "@objectstack/connector-openapi": 33.96, - "@objectstack/connector-rest": 34.78, - "@objectstack/connector-slack": 34.99, - "@objectstack/core": 164.54, - "@objectstack/downstream-contract": 16.85, - "@objectstack/driver-memory": 141.51, - "@objectstack/driver-mongodb": 100, - "@objectstack/driver-sql": 384.28, - "@objectstack/driver-sqlite-wasm": 85.63, - "@objectstack/driver-turso": 189.07, - "@objectstack/embedder-openai": 5.56, - "@objectstack/example-crm": 41.32, - "@objectstack/example-embed-objectql": 28.22, - "@objectstack/example-showcase": 130.48, - "@objectstack/example-todo": 58.58, - "@objectstack/formula": 39.65, - "@objectstack/hono": 41.55, - "@objectstack/http-conformance": 125.37, - "@objectstack/knowledge-memory": 3.94, - "@objectstack/knowledge-ragflow": 4.23, - "@objectstack/lint": 200.52, - "@objectstack/mcp": 112.36, - "@objectstack/metadata": 119.96, - "@objectstack/metadata-core": 25.61, - "@objectstack/metadata-fs": 37.42, - "@objectstack/metadata-protocol": 629.62, - "@objectstack/objectql": 970.12, - "@objectstack/observability": 2.89, - "@objectstack/organizations": 73.2, - "@objectstack/platform-objects": 123.56, - "@objectstack/plugin-approvals": 360.06, - "@objectstack/plugin-audit": 249.24, - "@objectstack/plugin-auth": 405.25, - "@objectstack/plugin-dev": 50.78, - "@objectstack/plugin-email": 119.76, - "@objectstack/plugin-hono-server": 77.51, - "@objectstack/plugin-pinyin-search": 37.94, - "@objectstack/plugin-security": 360.02, - "@objectstack/plugin-sharing": 209.26, - "@objectstack/plugin-webhooks": 94.12, - "@objectstack/rest": 876.56, - "@objectstack/runtime": 848.96, - "@objectstack/sdui-parser": 4.23, - "@objectstack/service-analytics": 412.58, - "@objectstack/service-automation": 450.63, - "@objectstack/service-cache": 1.64, - "@objectstack/service-cluster": 21.59, - "@objectstack/service-cluster-redis": 22.41, - "@objectstack/service-datasource": 128.92, - "@objectstack/service-i18n": 17.69, - "@objectstack/service-job": 31.65, - "@objectstack/service-knowledge": 28.49, - "@objectstack/service-mailbox": 57.26, - "@objectstack/service-messaging": 158.74, - "@objectstack/service-package": 51.92, - "@objectstack/service-print": 32.82, - "@objectstack/service-queue": 36.08, - "@objectstack/service-realtime": 9.31, - "@objectstack/service-settings": 149.93, - "@objectstack/service-sms": 22.13, - "@objectstack/service-storage": 223.46, - "@objectstack/spec": 1606.55, - "@objectstack/trigger-api": 14.65, - "@objectstack/trigger-record-change": 81.69, - "@objectstack/trigger-schedule": 43.22, - "@objectstack/types": 23.67, - "@objectstack/verify": 220.52, - "@objectstack/vitest-filter-preflight": 28.34, - "create-objectstack": 26.36 + "@objectstack/cli": 2697.45, + "@objectstack/client": 81.49, + "@objectstack/client-react": 8.1, + "@objectstack/cloud-connection": 115.93, + "@objectstack/connector-imap": 3.55, + "@objectstack/connector-mcp": 29.9, + "@objectstack/connector-openapi": 24.54, + "@objectstack/connector-rest": 28.39, + "@objectstack/connector-slack": 23.67, + "@objectstack/core": 103.58, + "@objectstack/downstream-contract": 6.8, + "@objectstack/driver-memory": 103.22, + "@objectstack/driver-mongodb": 54.92, + "@objectstack/driver-sql": 241.07, + "@objectstack/driver-sqlite-wasm": 63.75, + "@objectstack/driver-turso": 129.29, + "@objectstack/embedder-openai": 5.22, + "@objectstack/example-crm": 23.11, + "@objectstack/example-embed-objectql": 7.86, + "@objectstack/example-showcase": 48.49, + "@objectstack/example-todo": 27.48, + "@objectstack/formula": 39.47, + "@objectstack/hono": 30.38, + "@objectstack/http-conformance": 155.47, + "@objectstack/knowledge-memory": 2.57, + "@objectstack/knowledge-ragflow": 4.29, + "@objectstack/lint": 141.56, + "@objectstack/mcp": 68.77, + "@objectstack/metadata": 142.75, + "@objectstack/metadata-core": 15.7, + "@objectstack/metadata-fs": 21.58, + "@objectstack/metadata-protocol": 670.12, + "@objectstack/objectql": 450.03, + "@objectstack/observability": 3.06, + "@objectstack/organizations": 99.66, + "@objectstack/platform-objects": 80.46, + "@objectstack/plugin-approvals": 254.27, + "@objectstack/plugin-audit": 255.86, + "@objectstack/plugin-auth": 283.83, + "@objectstack/plugin-dev": 48.97, + "@objectstack/plugin-email": 77.36, + "@objectstack/plugin-hono-server": 68.19, + "@objectstack/plugin-pinyin-search": 53.27, + "@objectstack/plugin-security": 245.56, + "@objectstack/plugin-sharing": 150.16, + "@objectstack/plugin-webhooks": 49.65, + "@objectstack/rest": 569.96, + "@objectstack/runtime": 793.7, + "@objectstack/sdui-parser": 3.68, + "@objectstack/service-analytics": 406.18, + "@objectstack/service-automation": 522.93, + "@objectstack/service-cache": 2.22, + "@objectstack/service-cluster": 11.41, + "@objectstack/service-cluster-redis": 12.79, + "@objectstack/service-datasource": 90.24, + "@objectstack/service-i18n": 7.81, + "@objectstack/service-job": 14.25, + "@objectstack/service-knowledge": 34.06, + "@objectstack/service-mailbox": 67.92, + "@objectstack/service-messaging": 120.65, + "@objectstack/service-package": 32.97, + "@objectstack/service-print": 29.92, + "@objectstack/service-queue": 33.57, + "@objectstack/service-realtime": 8.09, + "@objectstack/service-settings": 147.44, + "@objectstack/service-sms": 8.95, + "@objectstack/service-storage": 150.72, + "@objectstack/spec": 1168.98, + "@objectstack/trigger-api": 10.92, + "@objectstack/trigger-record-change": 56.43, + "@objectstack/trigger-schedule": 33.51, + "@objectstack/types": 19.54, + "@objectstack/verify": 376.32, + "@objectstack/vitest-filter-preflight": 13.41, + "create-objectstack": 13.77 }, "skippedAsCached": [ "@objectstack/cli", + "@objectstack/client", + "@objectstack/client-react", "@objectstack/cloud-connection", "@objectstack/connector-mcp", + "@objectstack/connector-openapi", "@objectstack/connector-rest", + "@objectstack/connector-slack", "@objectstack/core", "@objectstack/downstream-contract", + "@objectstack/driver-memory", + "@objectstack/driver-mongodb", + "@objectstack/driver-sql", "@objectstack/driver-sqlite-wasm", "@objectstack/driver-turso", + "@objectstack/embedder-openai", "@objectstack/example-crm", "@objectstack/example-embed-objectql", + "@objectstack/example-showcase", + "@objectstack/example-todo", "@objectstack/formula", + "@objectstack/hono", "@objectstack/http-conformance", + "@objectstack/knowledge-memory", "@objectstack/knowledge-ragflow", "@objectstack/lint", "@objectstack/mcp", "@objectstack/metadata", - "@objectstack/metadata-core", "@objectstack/metadata-fs", "@objectstack/metadata-protocol", "@objectstack/objectql", @@ -195,6 +294,7 @@ "@objectstack/platform-objects", "@objectstack/plugin-approvals", "@objectstack/plugin-audit", + "@objectstack/plugin-auth", "@objectstack/plugin-dev", "@objectstack/plugin-email", "@objectstack/plugin-hono-server", @@ -204,31 +304,45 @@ "@objectstack/plugin-webhooks", "@objectstack/rest", "@objectstack/runtime", + "@objectstack/sdui-parser", "@objectstack/service-analytics", "@objectstack/service-automation", "@objectstack/service-cache", - "@objectstack/service-cluster-redis", + "@objectstack/service-datasource", + "@objectstack/service-i18n", "@objectstack/service-job", "@objectstack/service-knowledge", "@objectstack/service-mailbox", "@objectstack/service-messaging", "@objectstack/service-package", + "@objectstack/service-print", + "@objectstack/service-queue", + "@objectstack/service-realtime", "@objectstack/service-settings", - "@objectstack/service-sms", "@objectstack/service-storage", "@objectstack/spec", "@objectstack/trigger-api", "@objectstack/trigger-record-change", "@objectstack/trigger-schedule", - "@objectstack/types", - "@objectstack/vitest-filter-preflight", - "create-objectstack" + "@objectstack/verify", + "@objectstack/vitest-filter-preflight" ], "skippedIncompleteSlices": [ - "@objectstack/cli in run 39943147947 (missing 2/3, 3/3)" + "@objectstack/cli in run 40209651657 (missing 1/3, 2/3)", + "@objectstack/cli in run 40214410982 (missing 1/3, 2/3)", + "@objectstack/cli in run 40266979145 (missing 2/3, 3/3)" ], "carriedOver": [], - "provisional": [], + "provisional": [ + "@objectstack/metadata-protocol", + "@objectstack/plugin-audit", + "@objectstack/plugin-sharing", + "@objectstack/rest", + "@objectstack/runtime", + "@objectstack/service-analytics", + "@objectstack/service-automation", + "@objectstack/verify" + ], "ceilings": { "@objectstack/cli": 2868.12, "@objectstack/client": 179.74, @@ -310,114 +424,138 @@ "density": { "runs": [ { - "run": "39943147947", - "executed": 49 + "run": "40272142430", + "executed": 20 }, { - "run": "39936055900", - "executed": 54 + "run": "40266979145", + "executed": 59 }, { - "run": "39928834751", - "executed": 43 + "run": "40262173089", + "executed": 58 }, { - "run": "39921744055", - "executed": 75 + "run": "40257470166", + "executed": 58 + }, + { + "run": "40242835329", + "executed": 32 + }, + { + "run": "40238189261", + "executed": 33 + }, + { + "run": "40233481335", + "executed": 29 + }, + { + "run": "40228747755", + "executed": 20 + }, + { + "run": "40223991179", + "executed": 20 + }, + { + "run": "40219252911", + "executed": 20 }, { - "run": "39915751898", - "executed": 69 + "run": "40214410982", + "executed": 40 }, { - "run": "39909025131", - "executed": 70 + "run": "40209651657", + "executed": 40 }, { - "run": "39903431405", + "run": "40204927516", "executed": 75 }, { - "run": "39898100438", - "executed": 72 + "run": "40200279306", + "executed": 73 } ], "shares": { - "@objectstack/cli": 0.2164, - "@objectstack/client": 0.01174, - "@objectstack/client-react": 0.0009241, - "@objectstack/cloud-connection": 0.01921, - "@objectstack/connector-imap": 0.0002767, - "@objectstack/connector-mcp": 0.002915, - "@objectstack/connector-openapi": 0.0026, - "@objectstack/connector-rest": 0.002878, - "@objectstack/connector-slack": 0.002672, - "@objectstack/core": 0.01031, - "@objectstack/downstream-contract": 0.001177, - "@objectstack/driver-memory": 0.01114, - "@objectstack/driver-mongodb": 0.007783, - "@objectstack/driver-sql": 0.03059, - "@objectstack/driver-sqlite-wasm": 0.007015, - "@objectstack/driver-turso": 0.01846, - "@objectstack/embedder-openai": 0.0004014, - "@objectstack/example-crm": 0.003057, - "@objectstack/example-embed-objectql": 0.002133, - "@objectstack/example-showcase": 0.01, - "@objectstack/example-todo": 0.004714, - "@objectstack/formula": 0.003693, - "@objectstack/hono": 0.003341, - "@objectstack/http-conformance": 0.0101, - "@objectstack/knowledge-memory": 0.0003478, - "@objectstack/knowledge-ragflow": 0.000354, - "@objectstack/lint": 0.01451, - "@objectstack/mcp": 0.008144, - "@objectstack/metadata": 0.01135, - "@objectstack/metadata-core": 0.001921, - "@objectstack/metadata-fs": 0.003133, - "@objectstack/metadata-protocol": 0.05059, - "@objectstack/objectql": 0.07109, - "@objectstack/observability": 0.0002716, - "@objectstack/organizations": 0.00686, - "@objectstack/platform-objects": 0.009257, - "@objectstack/plugin-approvals": 0.02386, - "@objectstack/plugin-audit": 0.01921, - "@objectstack/plugin-auth": 0.03314, - "@objectstack/plugin-dev": 0.00361, - "@objectstack/plugin-email": 0.009403, - "@objectstack/plugin-hono-server": 0.005618, - "@objectstack/plugin-pinyin-search": 0.002891, - "@objectstack/plugin-security": 0.03053, - "@objectstack/plugin-sharing": 0.01499, - "@objectstack/plugin-webhooks": 0.006986, - "@objectstack/rest": 0.06441, - "@objectstack/runtime": 0.06859, - "@objectstack/sdui-parser": 0.0003479, - "@objectstack/service-analytics": 0.03143, - "@objectstack/service-automation": 0.04294, - "@objectstack/service-cache": 0.000132, - "@objectstack/service-cluster": 0.001845, - "@objectstack/service-cluster-redis": 0.00155, - "@objectstack/service-datasource": 0.009756, - "@objectstack/service-i18n": 0.001535, - "@objectstack/service-job": 0.002369, - "@objectstack/service-knowledge": 0.002017, - "@objectstack/service-mailbox": 0.004452, - "@objectstack/service-messaging": 0.01318, - "@objectstack/service-package": 0.004403, - "@objectstack/service-print": 0.002653, - "@objectstack/service-queue": 0.003507, - "@objectstack/service-realtime": 0.00076, - "@objectstack/service-settings": 0.01091, - "@objectstack/service-sms": 0.001988, - "@objectstack/service-storage": 0.01645, - "@objectstack/spec": 0.09904, - "@objectstack/trigger-api": 0.001217, - "@objectstack/trigger-record-change": 0.006333, - "@objectstack/trigger-schedule": 0.003499, - "@objectstack/types": 0.001908, - "@objectstack/verify": 0.01721, - "@objectstack/vitest-filter-preflight": 0.00254, - "create-objectstack": 0.001995 + "@objectstack/cli": 0.2175, + "@objectstack/client": 0.01878, + "@objectstack/client-react": 0.002868, + "@objectstack/cloud-connection": 0.03284, + "@objectstack/connector-imap": 0.001097, + "@objectstack/connector-mcp": 0.007916, + "@objectstack/connector-openapi": 0.004374, + "@objectstack/connector-rest": 0.006797, + "@objectstack/connector-slack": 0.003776, + "@objectstack/core": 0.017, + "@objectstack/downstream-contract": 0.003234, + "@objectstack/driver-memory": 0.01712, + "@objectstack/driver-mongodb": 0.01244, + "@objectstack/driver-sql": 0.02573, + "@objectstack/driver-sqlite-wasm": 0.0106, + "@objectstack/driver-turso": 0.02147, + "@objectstack/embedder-openai": 0.001983, + "@objectstack/example-crm": 0.004883, + "@objectstack/example-embed-objectql": 0.003669, + "@objectstack/example-showcase": 0.01117, + "@objectstack/example-todo": 0.004618, + "@objectstack/formula": 0.01122, + "@objectstack/hono": 0.004841, + "@objectstack/http-conformance": 0.01206, + "@objectstack/knowledge-memory": 0.000795, + "@objectstack/knowledge-ragflow": 0.001635, + "@objectstack/lint": 0.0241, + "@objectstack/mcp": 0.011, + "@objectstack/metadata": 0.03279, + "@objectstack/metadata-core": 0.00486, + "@objectstack/metadata-fs": 0.003597, + "@objectstack/metadata-protocol": 0.05121, + "@objectstack/objectql": 0.1187, + "@objectstack/observability": 0.001056, + "@objectstack/organizations": 0.01782, + "@objectstack/platform-objects": 0.01368, + "@objectstack/plugin-approvals": 0.01971, + "@objectstack/plugin-audit": 0.01955, + "@objectstack/plugin-auth": 0.04246, + "@objectstack/plugin-dev": 0.008368, + "@objectstack/plugin-email": 0.02333, + "@objectstack/plugin-hono-server": 0.01322, + "@objectstack/plugin-pinyin-search": 0.009586, + "@objectstack/plugin-security": 0.04182, + "@objectstack/plugin-sharing": 0.01147, + "@objectstack/plugin-webhooks": 0.007615, + "@objectstack/rest": 0.04352, + "@objectstack/runtime": 0.06059, + "@objectstack/sdui-parser": 0.001406, + "@objectstack/service-analytics": 0.03101, + "@objectstack/service-automation": 0.03996, + "@objectstack/service-cache": 0.0006902, + "@objectstack/service-cluster": 0.003502, + "@objectstack/service-cluster-redis": 0.003888, + "@objectstack/service-datasource": 0.01368, + "@objectstack/service-i18n": 0.002411, + "@objectstack/service-job": 0.003756, + "@objectstack/service-knowledge": 0.005797, + "@objectstack/service-mailbox": 0.01569, + "@objectstack/service-messaging": 0.01734, + "@objectstack/service-package": 0.005475, + "@objectstack/service-print": 0.006634, + "@objectstack/service-queue": 0.006008, + "@objectstack/service-realtime": 0.002314, + "@objectstack/service-settings": 0.04004, + "@objectstack/service-sms": 0.003692, + "@objectstack/service-storage": 0.03974, + "@objectstack/spec": 0.2647, + "@objectstack/trigger-api": 0.00402, + "@objectstack/trigger-record-change": 0.009983, + "@objectstack/trigger-schedule": 0.007522, + "@objectstack/types": 0.005565, + "@objectstack/verify": 0.02873, + "@objectstack/vitest-filter-preflight": 0.005587, + "create-objectstack": 0.005706 } } } From b32341039f093b50bbe98b7f38ae7e568921cb07 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 07:27:23 +0000 Subject: [PATCH 3/3] ci(test-shards): cut the CLI into 4 slices, re-derived by pin 3c on the 2026-10-26 refresh The refreshed dataset measures @objectstack/cli at 2697.45s. At 3 slices one slice is 899s, past 1.3x the heaviest other serial task (@objectstack/metadata-protocol, 670.12s, so 871s), and pin 3c refuses the refresh. At 4 a slice is 674s and meets; 3 does not, so 4 is the smallest. PREVIOUS_FILE_SHARDED_PACKAGES carries the outgoing {cli: 3}, so the refresh lane still decodes the 3-slice summaries retained from before this change. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01NPH84ZP1jWcst3tpRwfrJL --- scripts/partition-test-shards.mjs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/scripts/partition-test-shards.mjs b/scripts/partition-test-shards.mjs index a79085754b9..3c2f37e3764 100644 --- a/scripts/partition-test-shards.mjs +++ b/scripts/partition-test-shards.mjs @@ -162,7 +162,9 @@ const TIMINGS_PATH = path.join(REPO_ROOT, 'scripts', 'test-shard-timings.json'); // them by that same wall model (WHOLE_LEG_PARALLELISM), so a slice shard no // longer carries a whole-package leg ahead of its slice; on the committed // dataset's full list the predicted steps move from 1277s on the three slice -// shards (643s elsewhere) to 966s on the slice shards and 830s elsewhere. +// shards (643s elsewhere) to 966s on the slice shards and 830s elsewhere. On +// the 2026-10-26 refresh, with the CLI cut into 4 slices (pin 3c, below), all +// eight read ~738s. export const SHARD_COUNT = 8; // The acceptance bound #10472 set: the slowest shard within ~1.3x the mean. @@ -360,10 +362,23 @@ export function timingModeNote(event) { // n = 2 869.44s 1.52x still the floor // n = 3 579.63s 1.01x meets -- spec's task is the floor // -// So n = 3, and pin 3c holds it there. Slices are placed at their PLAIN weight -// (partition() is unchanged), so every bin keeps the density the whole-package +// So n = 3, and pin 3c held it there. Slices were placed at their PLAIN weight +// (partition() unchanged), so every bin kept the density the whole-package // split had -- see densityCap() for the hard limit on that and its pin. // +// RE-DERIVED AT 4 (#25870), on the 14-run refresh of 2026-10-26 (newest run +// 40272142430): the CLI at 2697.45s. `@objectstack/spec` (1168.98s) and +// `@objectstack/runtime` (793.70s) each run as two tasks, so the heaviest +// other serial task is `@objectstack/metadata-protocol`'s 670.12s, and a slice +// must sit within 1.3 x 670.12 = 871.16s. +// +// n = 3 899.15s 1.34x still the floor -- pin 3c refused 3 +// n = 4 674.36s 1.01x meets -- metadata-protocol is the floor +// +// Since #25870 partition() places a slice at its wall share rather than its +// plain weight (WHOLE_LEG_PARALLELISM); `total`, and so the density cap, still +// reads plain sums. +// // ⛔ THE COUNT IS CONFIGURED; WHETHER A RUN SLICES IS DECIDED BY THAT RUN. A // pull_request or merge_group run splits its own affected set, so planShards() // asks, of the run's own list, whether a configured package is that run's @@ -384,7 +399,7 @@ export function timingModeNote(event) { // a change to what that generator can DECODE, which is why the map it replaced // is kept below. export const FILE_SHARDED_PACKAGES = Object.freeze({ - '@objectstack/cli': 3, + '@objectstack/cli': 4, }); // THE MAP AS IT STOOD BEFORE ITS LAST CHANGE, read only by the generator's @@ -412,8 +427,10 @@ export const FILE_SHARDED_PACKAGES = Object.freeze({ // `test-core-run-summary-*`, #25771), so four days after that PR lands no // retained summary predates the change and this map decodes nothing. // The outgoing map of #22075's third round was empty -- the runs before it -// carried no slice -- so this one is empty too. -export const PREVIOUS_FILE_SHARDED_PACKAGES = Object.freeze({}); +// carried no slice. #25870 raised the CLI from 3 slices to 4, so this holds 3. +export const PREVIOUS_FILE_SHARDED_PACKAGES = Object.freeze({ + '@objectstack/cli': 3, +}); // The item grammar. A shard item is a package (`@objectstack/cli`) or a SLICE // of one (`@objectstack/cli 1/2`), and this pair of functions is the only place