ci(test): a file-level slice builds its dependency closure in a passthrough-free run, so the sharded leg stops rebuilding it - #16868
Merged
Conversation
This was referenced Sep 8, 2026
baozhoutao
marked this pull request as ready for review
September 8, 2026 13:26
baozhoutao
enabled auto-merge
September 8, 2026 13:27
This was referenced Sep 8, 2026
Open
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.
Fixes #16395
Root cause, measured — the rebuild is real, and one half of it is removable
The card's lead was right and this PR removes the removable half of it.
turbofolds a run-level passthrough into the hash of every task in the run, notonly the task that receives it.
Run this shard's testsgives a file-level slice its owninvocation precisely so that
-- "--shard=k/n"reaches only that package's vitest — andthat passthrough then re-hashed the slice's entire
^buildclosure.Measured on turbo 2.10.10,
--filter=@objectstack/cli,turbo run test … --dry=json(60 tasks: 59
build+ 1test):-- --shard=1/2--shard=1/2vs--shard=2/2So the sliced leg could hit neither the main-seeded Turbo cache restored earlier in the
job nor the builds the shared leg had run seconds before, in the same job, on the same
disk. Shards 5/6 and 6/6 — the only two that carry a slice — built that closure twice per
job, unconditionally.
Live corroboration,
Test Core (5/6)of run34193080219(apackages/specPR, so theaffected set reaches
@objectstack/cli): the slice leg reportedCached: 2 cached, 58 totalandTime: 9m18.941s, while@objectstack/cli:testitselfmeasured
Duration 187.49s. ⇒ roughly six of those nine minutes were the duplicaterebuild, not tests. #16395's cancelled job died
mid-stream through @objectstack/cli's suite— that is this leg.The change
Two edits to
.github/workflows/ci.yml, both inside thetestjob:Build the sliced package's dependency closure, builds the slice'sclosure in a run that carries no passthrough —
turbo run build --filter=$PKG.Measured: 59 build tasks, all 59 hash-identical to the ones in the passthrough-free
test plan (0 differing, 0 extra, 0 missing against the test's own
^buildclosure), sothey replay from the restored cache / the shared leg instead of re-executing.
--filter=...^$PKGwas measured too and schedules 12 packages the closure does not need.--only, so the poisoned hashes reach exactly one task.Verified: the
--onlyplan is1 -> @objectstack/cli#test.It is its own step rather than a second guarded run inside the existing one because a
guarded site is the triple
(file, job, step):measure-stall-guard-headroomrefuses toreport a verdict when two guarded runs share one, and refusing is right — the two would be
judged against the worst reading of their union. That gate went red on the one-step shape
and green on this one; it named the fix and this is the fix it named.
A shard with no slice is behaviourally unchanged — the new step runs zero iterations
and the test step's shared leg is byte-identical.
How I convinced myself this is correct beyond "the gates passed"
CI configuration is not executed by the gates, so the evidence is separate:
The workflow parses and both step scripts are valid shell. The
testjob's YAML isparsed, the two
run:bodies extracted, and each passesbash -n. Step order isre-read from the parse: the attestation pair is still the job's last two steps (CI 聚合门禁把合并队列重建的
aggregate result: abandoned判成红 —— 在队 PR 零测试失败被踢出(ci.yml 两处白名单缺abandoned) #6082).An offline behavioural harness drives the extracted step bodies with a stub
run-with-stall-guard.mjsthat records argv, over four shard shapes:--onlysharded leg;test-core.logcarries exactly the 2 test legs, not the build logThe replay this PR depends on was executed, not assumed. Back-to-back invocations of
the exact new command: first
5 cached, 57 total/Time: 3m45.918s; second57 cached, 57 total/Time: 153ms >>> FULL TURBO. A passthrough-free build run in asecond turbo invocation replays completely — which is exactly what the poisoned leg
could not do.
The control fires. The hash instrument was run twice with identical arguments and
returned 60/60 identical hashes before it was used to claim 0/60.
Re-taken timing on current
main(the card's numbers are from 2026-09-06)timeout-minutesfor this job is 45, not 30 — #16173 raised it temporarily, with anexplicit revert condition tied to its own shard rebalance. This PR ⛔ does not touch that
line: raising the ceiling is #16395's fallback remedy, which is not being taken, and
lowering it is #16173's declared condition, not this PR's.
Test Core (5/6)on threepackages/spec-touching PRs, currentmain:Test Core (5/6)342150282033420591801834193080219⇒ the acute 30-minute blockage the card describes is gone; the defect it was a symptom of
was not, and is what this PR removes.
Acceptance notes
@objectstack/spec— spec is on shard 1/6. Shard 5/6 carries@objectstack/cli 1/2.So "spec's own heavy suite only runs on spec PRs" is not what makes shard 5 the outlier;
the slice leg is. Verified by running
select-shard-packages.sh+partition-test-shards.mjslocally over the full list.
Restore Turbo cachereporting success says nothing about a hit: on run34215028203the step took 2 s and on
34193080219it took 56 s, and the longer restoreproduced the longer job. Zone 3's distinction is real, and
Cached: N cached, M totalin the leg's own summary is the reading that answers it.
noted, not filed: the restore-key chain's last fallback,Linux-turbo-test-, matchesany shard's namespace, which partly undoes the shard scoping the step's comment
describes. Harmless (turbo entries are content-addressed) and not a defect — noted only
because the comment reads as if the scoping were total.
⚠ This PR's own CI run does NOT exercise the change
Measured, not assumed: with
TURBO_SCM_BASEat this branch's merge base,turbo ls --affectedreturns 0 packages for this diff (turbo.json'sglobalDependenciesaretsconfig.jsonandtsup.config.ts— not.github/**).So every shard on this PR takes the
No packages on this shard — nothing to testexit,the new step short-circuits with it, and a green
Test Corehere says nothing about thenew code path. That is why the evidence above is offline and local rather than "the PR went
green": the harness drives the real extracted step bodies, and the turbo readings were taken
in a real checkout. The first run that actually executes the new step is one whose affected
set reaches
@objectstack/cli— a merge-group run, or the nextpackages/specPR.🤖 Generated with Claude Code
https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Generated by Claude Code