Uh oh!
There was an error while loading. Please reload this page.
fix(tests): close the vitest invocation guard's package-cwd hole in the 11 standalone package configs - #5412
Merged
os-support-ai merged 2 commits intoAug 20, 2026
Conversation
`assertCanonicalVitestInvocation` refuses a package-cwd vitest run because such a run uses a different config than CI does and can therefore pass a suite CI would fail. Its docstring claimed every per-package config re-exports the root one, so no package-level path could skip it. Measured, by running `pnpm exec vitest run` from every directory carrying a config: 8 package configs import the root config and were refused, 2 packages carry no config and resolve upward and were refused — and 11 standalone configs (plugin-calendar/-charts/-detail/-form/-gantt/-grid/ -kanban/-list/-map/-timeline/-view) never mention the root file, so the guard never ran. From packages/plugin-grid, one such run printed `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0, under a config with no `@object-ui/*` alias table at all. - the 11 standalone configs now call the guard themselves, via a new `repoRootFrom(import.meta.url)` landmark search rather than a hand-counted `../..` (which fails silently when the count is wrong) - the guard's docstring and the root config's call-site comment now state the three routes a config can take, instead of asserting one of them - the claim is enforced, not restated: the guard's own test walks every `vitest.config.*` in the repo and fails on any taking neither route Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
…one (#5406) AGENTS.md, QUICK_REFERENCE.md and the project-setup skill guide each stated that the guard lives "in `vitest.config.mts`". After #5406 that is one of twelve call sites, and stating only the root one is what let the hole go unnoticed. Concrete paths rather than a `packages/plugin-*/…` glob, because `quick-reference-commands-4149.test.ts` requires every path in a code span to resolve. Also adds the empty-frontmatter changeset declaring that this publishes nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 20, 2026
os-support-ai
marked this pull request as ready for review
August 20, 2026 15:38
Uh oh!
There was an error while loading. Please reload this page.
os-support-ai
deleted the
claude/issue-5406-vitest-invocation-guard-coverage
branch
August 20, 2026 15:38
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 freeto 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#5406
The enumeration, measured rather than inherited
I ran
pnpm exec vitest runfrom every directory carrying a vitest config on pristineorigin/main(fd227ea), and classified each config by whether it leads back to the root one. 19 config files exist: the rootvitest.config.mtsplus 18 others.packages/app-shell,packages/mobile, ~30 moremergeConfig, or strip-one-key)packages/core,react,types,components,fields,plugin-dashboard,apps/console,examples/schema-catalog— 8plugin-calendar,-charts,-detail,-form,-gantt,-grid,-kanban,-list,-map,-timeline,-view— 11Importing the root config executes its module scope, and the guard call sits there — so route 2 is covered as a side effect of the import, not by anything the package config says. Route 3 never imports the guard module at all, so it never runs.
The card's numbers do not survive measurement
packages/*/vitest.config.tsfiles (6 root-importing + 11 standalone), not the uncovered ones.packages/coreandpackages/componentsas uncovered, "none of which re-export the root config". Both do import it and both were measured refused before any change;packages/core/vitest.config.tsis literally two lines re-exporting it.plugin-grid, the third package it names, is genuinely uncovered.The uncovered set is 11 — exactly what #5313 measured a day earlier, from the same starting point. See the PM note at the bottom.
Why these 11 are the worst place for the hole
They are not near-copies of the root config. Each declares
happy-dom+globals+ a localvitest.setup.ts(one line:import '@testing-library/jest-dom') and no alias table at all, where the root config maps roughly 40@object-ui/*specifiers at a sibling package'ssrc/. So a run launched there both collects the package's own files and resolves them differently from CI — the divergent-config false green this guard exists to refuse, arriving through the one door it was documented to have locked.This also settles the question #5313 left open: a bare run in one of the 11 does not collect console's 22 foreign files. Their configs have no
projectsarray, so vitest falls back to the default**/*.{test,spec}.?(c|m)[jt]s?(x)relative to the package root and collects the package's own suite. That is worse than #3378's shape, not better — the count looks right and the files look right.Reverse verification — predicted, then observed
Predictions written before running:
plugin-gridaccepted before / refused after;packages/corerefused in both (it imports the root config);packages/app-shellunknown, since whether vitest walks up for a config had to be measured, not assumed.Before — from
packages/plugin-grid:After — the identical command:
Observed matched predicted on all three, including the unknown:
app-shellandmobilewere already refused, so the docstring's upward-resolution claim is the one part of it that was true. Sweeping all 21 directories again after the change: 21/21 refused, 0 accepted.Ablation on the new enforcement test, to show it can actually fail — predicted plain RED (the config texts are its only input, and removing a call can only add a finding):
Restored with
git checkout HEAD -- ...(the fix was committed first, so the restore leg has a real restore point);git statusclean afterwards and 30/30 green again. No build artifact sits between any edit and the thing under test on either leg: the enforcement test reads the config files as text from disk and vitest transpiles the test per run — nothing on either path resolves through adist/.The fix
repoRootFrom(import.meta.url)in the guard module, instead of eleven hand-writtenpath.resolve(__dirname, '../..'). That literal fails silently when the count is wrong: the resolved directory exists, the comparison still runs, and the guard keeps issuing verdicts computed against the wrong root. A landmark search either finds the directory holdingvitest.config.mtsor throws. It reads the config'simport.meta.url, not the guard module's, so it holds whether Vite hands the config to Node's native ESM loader or bundles it to atimestamp-*.mjswritten alongside — and it adds nothing to the__dirnamedebt tracked in 28 个 vite/vitest config 用__dirname,Vite 8 已警告它在未来 major 默认的configLoader: 'native'下不受支持 #3592.vitest.config.*in the repo and fails on any taking neither route, with a message that spells out the fix and says not to add an exemption. Liveness is pinned too (the walk reaches the root file, both subdirectories and both routes are represented), so it cannot go vacuously green.One near-miss worth flagging, because it is the same defect class as the bug: the classifier's first spelling was
text.includes('vitest.config.mts'), and all 11 standalone configs name the root file in the comment explaining why they do not import it. Every one classified as route 2 and the check went green over exactly the configs it exists to catch. It now matches the import specifier, and that distinction is pinned as its own assertion.Nothing was exempted, and nothing legitimate broke
The card said to stop and report rather than carve an exemption. Nothing needed one:
pnpm testfrom the repo root only (ci.yml:435explicitly rejectsturbo run test). No workflow and no root script invokesturbo run testorpnpm --filter PKG test.projectsarray names onlyapps/console.testscripts in those 11 packages now fail loudly, exactly like the other ~30 packages already did. That is the guard's documented stance ("until that is decided they fail loudly instead of lying"); their fate is 17 个包各自带 vitest.config.ts 与根 vitest.config.mts 行为分叉:先回答「哪些包真的需要本地配置」 #3240's question, untouched here.OBJECTUI_VITEST_GUARD=offescape hatch still stands down in a newly-guarded package (verified).src/is touched, no config's test semantics changed, no assertion weakened anywhere, and no skip list added.Verification, all from the repo root, at 33fc791
pnpm exec vitest run scripts/pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/pnpm exec vitest run scripts/__tests__/vitest-invocation-guard.test.tspnpm type-check:scripts.tstesteslintover all 14 changed files'path' is defined but never usedwarnings, present onmain, left alone)check:control-bytes/check:self-import/check:phantom-deps/check:skills-pathscheck-changeset-presence/check-changeset-no-majorcheck:published-dist(full 275s build)Measured that
packages/*/vitest.config.tssits in no tsc program (tsc --listFileson bothtsconfig.jsonandtsconfig.test.json: 0 hits), which is why the new import carries no@ts-expect-error— one there would be dead weight today and a TS2578 hazard the moment the file joins anallowJsprogram.Files touched
scripts/vitest-invocation-guard.mjs,scripts/__tests__/vitest-invocation-guard.test.ts,vitest.config.mts(comment only), the 11packages/plugin-*/vitest.config.ts, and one changeset.Three doc surfaces each stated the guard lives "in
vitest.config.mts" —AGENTS.md,QUICK_REFERENCE.md,skills/objectui/guides/project-setup.md. That is now one of twelve call sites, and stating only the root one is the habit that let the hole go unnoticed, so each got a one-clause correction. They use concrete paths rather than apackages/plugin-*glob becausequick-reference-commands-4149.test.tsrequires every path in a code span to resolve — it caught the glob, which is why the docs namepackages/plugin-grid/vitest.config.tsand its ten siblings..github/workflows/ci.ymlis untouched (#5403 owns it).For the PM
assertCanonicalVitestInvocationdoes not cover the 11 packages that own avitest.config.ts— measured: a package-cwd run inplugin-gridis NOT refused #5313 records this same defect, with the correct count of 11 and the sameplugin-gridmeasurement, and it prescribed precisely the route taken here. It ispm:queueand unassigned. I have deliberately used no closing keyword on it — disposition is yours, not mine.Generated by Claude Code
Generated by Claude Code