ci(turbo): derive the test inputs guard from each package's Vitest config program (#4178) - #4188
Merged
Merged
Conversation
…nfig program (#4178) turbo's `test` task hashed nothing outside the package directory, so every root file that decides how a package's tests run — the shared `vitest.config.mts`, the four `vitest.setup.*`, the invocation guard the root config calls, and the console project config it pulls in — was invisible to the cache key. Measured on main: `@object-ui/core#test` frozen at 2e2087e2c30ef125 across all of them. Adds the six derived `$TURBO_ROOT$` entries, plus a guard that derives the requirement from each package's Vitest configuration program (config resolution incl. Vitest's upward search, transitive relative imports, and files designated through file-valued options) rather than restating it. The turbo-side plumbing shared with the #3514 guard moves to scripts/__tests__/helpers/turbo-inputs.ts so the two cannot drift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
yinlianghui
marked this pull request as ready for review
August 10, 2026 19:14
This was referenced Aug 10, 2026
Closed
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 #4178
PR #4176 (the #3514 sibling) merged as
eb5f8cea0before this started, so this branches offmainrather than stacking.Premise check first
The card's premise holds and its headline number reproduces exactly. On
mainateb5f8cea0,@object-ui/core#test:Two things the card got wrong, both found by measuring instead of trusting the filing — which is why this ships a derivation and not the filing's list.
The set is 10 files, not 5. Beyond the root config and the four
vitest.setup.*, the program also reachesscripts/vitest-invocation-guard.mjs(the root config callsassertCanonicalVitestInvocationat load — the thing that decides whether a run is refused at all),apps/console/vitest.config.ts(named in the root config'sprojects),apps/console/vite.config.ts, and the twoscripts/vite-*.tsplugins that one imports."Every per-package
vitest.config.tsliterally re-exports the root" is false. 16 of 39 packages derive zero out-of-package files —packages/plugin-grid/vitest.config.tsand friends are self-contained. Had the entries been pinned from the filing's prose, the guard would have asserted a shape the repo does not have.What ships
1.
scripts/__tests__/helpers/vitest-config-program.ts— the derivation, as its own module. A mechanism, not a policy, and a derivation only exercisable through the assertions it feeds is one nobody can probe. It assembles each package's Vitest configuration program:testscript, following the script's own--root/--configand then Vitest's upward search. That search is mirrored fromvitest/dist/chunks/constants.*.js(CONFIG_NAMESxCONFIG_EXTENSIONS) and theany()helper increateVitest— walk up from the root, first name in list order that exists wins. Verified against the real binary:packages/app-shellhas no config of its own andnpx vitest listthere is refused by the root config's guard, so the upward walk is real, not assumed;setupFiles,globalSetup,projects,workspace), themselves walked.That third step is load-bearing rather than decorative:
@object-ui/plugin-editorand@object-ui/plugin-markdownreach outside their directories only throughsetupFiles: ['../../vitest.setup.tsx']. A pure import-chain walk — which is what the card proposed — would have missed both.Narrowings are stated in the module's docblock rather than left implicit: the configuration program, not the alias closure of the tests (that closure is most of the repo, and is what
dependsOn: ["^build"]plus$TURBO_DEFAULT$already answer); designation is key-directed, so the ~45 concrete test paths indomTsTests/heavyDomTestsareincludeglobs and not program files. Both narrowings err toward requiring more and toward throwing on a shape not understood.2.
scripts/__tests__/turbo-test-inputs.test.ts— the policy over that result, mirroring #4176's assertion set: every out-of-package file must be matched by a$TURBO_ROOT$input; two assertions police the reverse direction; one pins the sweep's liveness. Two more pin the config-resolution premise the whole sweep rests on (the upward walk, andvitest.config.*winning overvite.config.*in the same directory) — without those, a future Vitest that stopped walking up would empty every configless package's derived set and this guard would go quiet with no red test.3.
scripts/__tests__/helpers/turbo-inputs.ts— the turbo-side plumbing (workspace discovery,$TURBO_ROOT$extraction, glob matching) lifted out of #4176's guard, now shared. Only the derivations differ between the two guards, and they differ in kind. A second hand-written copy of a glob matcher whose entire doctrine is "never approximate toward a match" is exactly the drift neither guard would survive. #4176's own test count is unchanged at 43, which is the evidence the extraction was behaviour-preserving.4.
turbo.json— the six entries the guard's first run named:Measured end to end
Subject:
@object-ui/runner, the one package whosetesttask both succeeds under turbo and runs through the root config (its script isvitest run --root ../.. packages/runner/, so the invocation guard passes). Defect planted: an unconditionalthrowappended to the rootvitest.setup.base.ts.Not merely a stale exit code — turbo replayed the previous run's captured output verbatim,
Start at 17:58:30and all, over a setup file that now throws on import.The verdict depended on cache state, not on the code. With the fix, same tree, same defect:
And each of the nine files behind the six entries now moves the hash independently (
@object-ui/core#test, baseline47146abf3e1fbf25):vitest.config.mtsto5c8802c4cf88c84f,vitest.setup.base.tsto070d14e80dc8a673,vitest.setup.dom.tsxto92265af76c689c45,vitest.setup.tsxtod809128224b22750,vitest.setup.dom-light.tsxto2edc9e09c9a8cdac,vitest-invocation-guard.mjsto083f717d6a07d292,vite-crypto-stub.tsto7a1265fb5e8730b5,apps/console/vitest.config.tstod3b74eda29c8b7e4,apps/console/vite.config.tstob751da4988a97a08.Reverse verification
Each new entry removed in turn, a phantom entry added, and — the one that matters most — a new root file introduced into a chain that previously had none:
[8] is the one that separates a derivation from a restatement: the case count goes 28 to 29 because
it.eachenumerates only packages with a non-empty set, so a package that reached nowhere before now has a case at all — and it names a file no entry, comment or fixture in this PR mentions.Sweep — the other cached tasks, measured
The card asked for a hash-freeze probe per task rather than a guess.
buildandlintare the two remaining cached tasks, and both declare noinputsat all. Both are frozen:Filed, not fixed here — this PR is scoped to
test: #4184 (lint, the root flat config that is the lint program) and #4185 (build, root tsconfigs plus the Vite plugins, and the highest-stakes of the three sincedist/is what every other task consumes).Gates
Changeset: none owed —
check-changeset-presence.mjsarbitrates: "No source of a released package changed in this range, so no changeset is owed." Noskip-changesetlabel per #3724.One thing worth flagging for triage, since it narrows this card's blast radius without touching its premise:
ci.ymldeliberately does not runturbo run test(it runs the canonical root Vitest once, sharded, with a comment explaining why). So the stale-replay this closes bites localturbo run testand any future CI use of it, rather than today's CI test job — unlike #4184 and #4185, wherelint.ymlandci.ymldo run the cached turbo task with.turbo/cachepersisted.Out of scope, noticed while measuring
@object-ui/plugin-editor'stesttask is already red onmain, independently of this change: itsvite.config.tsaliases@object-ui/fieldsto source but not@object-ui/providers, so the heavy setup graph fails to resolve (Failed to resolve import "@object-ui/providers" from "../fields/src/widgets/FileField.tsx"). Not filed as part of this card's sweep since it is a different mechanism; flagging it here for the PM to decide whether it wants its own issue.Generated by Claude Code