Repository navigation
ci(test-shards): place Test Core bins by predicted wall, and cut the CLI into 4 slices on the refreshed dataset - #25968
Merged
Conversation
…ce 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 <noreply@anthropic.com>
Contributor
Author
席位核对Verdict: ACCEPT at head Checklist
Gate readings at head
Seat's own readings, in a detached worktree at
Deviations / follow-ups (named, not fixed here)
Generated by Claude Code |
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 2a728ba. Generated, never hand-edited.
…he 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPH84ZP1jWcst3tpRwfrJL
Contributor
Author
席位核对 (delta re-review)Verdict: ACCEPT at head The delta
Gate readings at head
Seat's own readings at
Still open on #25870 (named, not done here)
Generated by Claude Code |
os-zhuang
pushed a commit
that referenced
this pull request
Oct 26, 2026
…6-10-23 dataset (undo #25968) (#26077) <!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D5ZEhycrEz6EHUjW1whANXQ)_ Fixes #26074 Clause-②: no Metadata-protocol: none ## What This is a clean revert of f36f619 (#25968). It changes two files and nothing else: `scripts/partition-test-shards.mjs` goes back to plain-sum bin placement, and `scripts/test-shard-timings.json` goes back to the 2026-10-23 dataset with cli in 3 slices. Neither file has a later commit on `main`. No other file imports the exports this removes (`WHOLE_LEG_PARALLELISM`, `loadOf`, `wallOf`). ## Why Since the first queue build on the #25968 layout (08:16Z), full Test Core `merge_group` builds have gone red 5 times in 27. On the old layout, 05:00–08:10Z, they went red 0 times in 19. Every red ejects its PR and rebuilds every entry queued behind it, so throughput fell from 12–13 merges an hour to 6. The failing tests are all load-sensitive: - objectql `src/hook-run-as.test.ts` (cold `import('./index.js')`): Test timed out in 5000ms - plugin-security `src/objects/shipped-sets-identity-default-row-scope.test.ts`: Test timed out in 5000ms - cli `test/hook-timeout-override-refusal.test.ts` CONTROL: the `vitest list` child exits 1 The heads that failed passed when they went back in unchanged. The #25870 thread replayed `planRun()` on both trees. Before, five whole-package shards carried about 1866 s of four-wide work each, and plugin-security sat on a cli-slice shard with 899 s. After, four whole-package shards carry about 2141 s each, and objectql and plugin-security share one shard. The suite-duration ceiling annotations show the effect: plugin-security reads 605–611 s against a ceiling of 474, plugin-auth 617–928 s against 600, driver-sql 603–612 s against 451, and objectql 1102 s against 952. ## Cost and rollback The slowest spec-PR shard goes back to about 25–29 min, from about 20. #25870 stays open for a placement that takes contention into account. Rollback means re-landing f36f619 as it is. ## Verification - `node scripts/partition-test-shards.mjs --self-test`: OK. 75 measured packages, 77 items, 8 shards, max/mean 1.00x, bins about 1866 s, all within the 2899.02 s density cap, cli x3. - `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD`: pass. - The other gates are left to this PR's own CI run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SrpCjPfYv8N9P9EJvoDyN9 Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Elon · project thread
Part of #25870. This PR carries two steps: (1) wall-based bin placement, and (2) the refreshed dataset with the CLI cut into 4 slices. #25870 stays open, because its acceptance is measured on 3 consecutive spec-touching PRs after landing. The prediction below is not that measurement.
Supersedes #25960. It carries the shard-timings-refresh lane's commit 92343f5 (
chore(ci): refresh the Test Core shard-timings dataset) unchanged. That refresh cannot land alone, because pin 3c refuses it at 3 slices (step 2).Diagnosis — which of the three, with run ids
The long shards are always the three that carry a
@objectstack/clifile-level slice. Read fromgh api …/runs/N/jobsstep timings on 56pull_requestruns (2026-10-25 18:54Z – 2026-10-26 05:44Z), and from the slice leg's ownDuration/ turboTime:lines on the 18 slice-shard jobs of sixpackages/spec-touching runs.The slowest shard on a spec-touching PR breaks down like this (minutes):
Time:)(whole-package leg =
Run this shard's testsminus the slice leg's turboTime:.)Three causes, all real, in this order of size:
BaseSequencer.shard: sha1 of the path, then equal-count chunks), so the three slices are not equal: across the six runs they measured 518–1047 s, the heaviest ~1.27× the mean (~820 s). Total cli 2406–2542 s against a pinned 2899 s (0.83–0.88×).@objectstack/cliis in ci.yml'sDTS_READERS, so every slice shard builds the whole cli closure WITH.d.ts, and a PR run reads no remote cache, so that build is cold (ci.yml: local cold closure 5m59s with declarations, 1m24s without).partition()balanced bins by their SUM of predicted seconds. A slice runs serially after the four-wide whole leg, so the step is ~ wholeSum / 2.9 + slice (the ci(test-shards): widen Test Core from 6 to 8 shards once@objectstack/specis file-sharded — the "6 is enough" arithmetic predates the cli slices (full-battery slowest shard ≈ 1937 s → ≈ 1452 s) #25376 wall model). Balancing sums filled every slice shard up to the mean with ~900 s of whole packages: predicted step 1277 s on the three slice shards against 643 s on the other five (committed dataset, full list).Stale weights are not the cause: on the refreshed dataset (#25960, the shard-timings-refresh lane's 2026-10-26 output) the sum-placed split still predicts 1107 s on the slice shards against 518 s elsewhere — the same 2.1× gap.
Step 1: wall-based placement (cause 3)
In
scripts/partition-test-shards.mjs, with no workflow or package edit:partition()places bins by load — the wall model in whole-package seconds, a slice countingWHOLE_LEG_PARALLELISM(2.9, the ci(test-shards): widen Test Core from 6 to 8 shards once@objectstack/specis file-sharded — the "6 is enough" arithmetic predates the cli slices (full-battery slowest shard ≈ 1937 s → ≈ 1452 s) #25376 constant, reused rather than re-derived) times its weight. A list with no slice is placed by exactly the same additions and comparisons as before, so every non-sliced run (and the nightly, which slices every package on every shard) gets a byte-identical split.totalstays the plain sum, so the density cap and the drift report mean what they meant.refineToCap()keepsloadin step withtotal.balanceOf()also reports the walls; pin 2 grades the wall ratio (with a slice in the split the sums are unequal by design); new pin 2b is a fixture where sum-placement and wall-placement differ.Predicted test steps, full list (
turbo ls, dogfood excluded), viaplanRun():Measured-based expectation for the six spec runs above: removing the whole-package leg takes the slowest shard from 25.7–28.8 min to ~22.3–25.1 min. The other five shards absorb the moved packages (their slowest was 17.5 min; +~2.5 min of four-wide work each).
Step 2: the refreshed dataset, and the CLI at 4 slices
The refresh (92343f5, 14 runs, newest 40272142430) measures
@objectstack/cliat 2697.45s. Pin 3c (sliceCountProblems, the serial-floor derivation of #22075) then refuses 3 slices:@objectstack/metadata-protocol670.12s; spec and runtime run as two tasks)So the change is
FILE_SHARDED_PACKAGEScli 3 → 4, withPREVIOUS_FILE_SHARDED_PACKAGESset to the outgoing{cli: 3}per the map's own rule, so the refresh lane still decodes the 3-slice summaries retained from before the change. The docblocks carry the new derivation table. The bin-sum floor and the wall model agree on this dataset, and no ratio or bound moved. Predicted test-step walls on the full list are 738s on all 8 shards (max/mean 1.00x), and every bin is within the density cap.Predicted from measurement, not from the model. vitest's
BaseSequencer.shard(sha1 of the path relative to the package root, sorted, equal-count chunks) reproduced locally overvitest list --filesOnly(506 files) matches run 40274911131's three slice jobs file for file: 169/169/168 files on jobs 121415631670/…679/…708. From those jobs' per-file times, with the wall taken as 0.39 × the per-file sum (vitest'sDurationover itstestssum on the same jobs: 0.403/0.381/0.390):Duration)The heaviest slice falls 27%. Applied to the six spec runs (slowest = setup ~1.5 + that run's cold DTS closure + ~1.1 min of whole leg, the wall model's 738 − 674 s, + 0.727 × that run's heaviest slice):
That gives 18.5–20.9 min, with three of six at or under 20. The cost: a fourth shard now builds the CLI's DTS closure (~5.8 min cold). The 738s walls above count test time only, not that build. The per-file read of 5/8 lacks 11 files that print before the 5,000-line log tail; their 172.2s (vitest
testsminus the captured sum) is spread evenly over them.If the readings stay above ~20 min
What is left on a slice shard is the cold DTS closure (5.5–6.3 min) plus the heaviest slice. The next steps, neither taken here:
.d.ts. With the split now reproducible, a slice holding none of them could build DTS-off (~1 min instead of ~5.8). This editsci.yml, so it is Tier H.Acceptance notes
.github/workflows/ci.yml(Tier H, untouched): the Test Core header still says each slice is "placed at its PLAIN weight so every bin keeps the whole-package split's density", and its "3 slices" wording (the drift-step comment, "a shard carrying one of its 3 slices is predicted a third of that"; the timeout history) is now stale. Correcting either makes a PR Tier H, so both are left for whichever step edits ci.yml. The partitioner's own headers carry the corrected statements.Tests
node scripts/partition-test-shards.mjs --self-testat b323410 →self-test OK (75 measured packages -> 78 shard items, 8 shards, predicted test-step walls 738/738/738/738/738/738/738/739s, max/mean 1.00x <= 1.3x, …, file-level slices: @objectstack/cli x4).scripts/ablation-replace.mjs, bin choice back tototal, on the committed dataset): the self-test reds withbalance: at 8 shards the slowest predicted test step is 1.45x the mean (1277s vs 881s). Restored blob == HEAD, andgit diff HEADis empty.@objectstack/cli: sliced 3 ways at 2697.45s, one slice is 899s -- past 1.3x the heaviest other serial task (670s, so 871s) … Raise it to 4.(the same red as chore(ci): refresh the Test Core shard-timings dataset #25960's job 121437007857).scripts/test-shard-timings.jsonis byte-identical to 92343f5 (git diff 92343f5b b3234103 -- scripts/test-shard-timings.jsonis empty).measure-test-shard-timings(decodes through both maps),check-test-completeness,ci/select-shard-timings-run,nightly-tiers,check-test-suite-ceilings,ci/select-shard-packages.selftest.sh.Gates
node scripts/pm/dispatch-gates.mjs --commands, merge-base origin/main cdd4eea): all 35 families exit 0. The--ranreconciliation reads35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED. Step 1 alone (8ca31e0) ran its 33 families the same way.Clause-②: no
Metadata-protocol: none
🤖 Generated with Claude Code
https://claude.ai/code/session_01NPH84ZP1jWcst3tpRwfrJL
Generated by Claude Code