test(repo): one vitest config, one verdict — delete the 17 per-package configs (#3240) - #7523
Conversation
…erdict (#3240) Direction A, ruled by the maintainer 2026-08-06 and reaffirmed by the delegated ruling of 2026-08-10: the root `vitest.config.mts` becomes the single entry, and a per-package run reaches it by path filter. What was there: 19 non-root vitest configs (the card's 17 under `packages/`, plus `apps/console` and `examples/schema-catalog`). Eleven of the seventeen were byte-identical standalone `defineConfig`s — `happy-dom` + `globals` + a one-line local setup and NO alias table, where the root config maps ~40 `@object-ui/*` specifiers at a sibling's `src/`. The other six re-exported or merged the root config, so they only ever restated it. What replaces them, per package: nothing. The root config already supplies every property they declared — `globals`, `happy-dom`, and jest-dom via `vitest.setup.dom-light.tsx` — plus the alias table they lacked. The one setup file that was not the shared one-liner, `plugin-map`'s, mocked `maplibre-gl`; `vitest.setup.base.ts` has mocked it globally all along, so that copy was a duplicate no canonical invocation ever loaded. Redefining the per-package run (the ruling's second clause): every `test` script is now `vitest run --root ../.. <pkgdir>/`, the shape PR #3869 landed for `packages/runner`. `pnpm --filter <pkg> test` and `turbo run test` were REFUSED by the invocation guard before this; they now run, against the same config CI uses, over exactly that package's files. Closing route 4 first, which the ruling did not anticipate: Vitest's config fallback does not stop at `vitest.config.*`. With none present it takes the directory's `vite.config.*`, and every `packages/*` has one — carrying a vestigial `test` block (`passWithNoTests: true`, a partial alias table, a setup the root config never loads). Deleting the 14 vitest configs whose packages also have a vite config would have moved them onto that route, widening the hole #5406 closed in the name of closing it. So each `packages/<pkg>/vite.config.ts` now calls the guard — gated on `process.env.VITEST`, which Vitest sets when it loads a config and `vite build` does not (measured both ways) — and the vestigial `test` blocks are deleted. turbo's `test` keeps `dependsOn: ["^build"]`, for a re-derived reason. The reason of record was resolution through `dist`, and that is now obsolete. But `^build` is the only edge putting a dependency's sources into a dependent's `test` cache key, and `helpers/vitest-config-program.ts` narrows its sweep by delegating to exactly that. Dropping it would replay stale greens; the note now says so. Evidence: `vitest list --filesOnly` from the repo root is byte-identical before and after — 2434 (project, file) pairs, empty diff. `vitest.config.mts` and all four root setup files are unchanged, so no file's project, environment or setup moved. #7291's `dist` project (`pnpm test:dist`: 1 file / 3 tests) and #7309's `isolate: false` invariant both still hold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…est-config-unification
`check-changeset-presence` counts three files under a released package's `src/`: comments in `plugin-calendar`, `plugin-map` and `plugin-timeline` test files, each naming a config or setup file this change deletes. No runtime or type surface moves, so the empty frontmatter is the declared answer the gate asks for, not a workaround. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…est-config-unification
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Merge-group red at 17:30Z — not this PR's, and the queue has already corrected itselfRecording this rather than leaving a red run hanging off this PR's number for the next reader to re-chase. The failure. Run 33783497752, Cause: #7473, not this change. That PR retags Why it is not this PR's, by file: this change touches no The queue handled it correctly and unaided: #7473 was dequeued, this PR was re-speculated alone on ⛔ No re-run spent, no commit pushed, no request made of the maintainer — there was nothing here to fix. Generated by Claude Code |
Second merge-group red (17:42Z) — the colorField ladder race, the one this PR's description already namedDequeued again ( What failed. Run 33784884273, Rung 2's second Why it is not this change's, mechanically rather than by appeal to that note:
Independent confirmation landed while this was in flight: #7527 diagnoses the same race from the other end and says outright that it "kicked whatever PR happened to be in the merge queue under load". Its causal chain — module-level ⛔ Not porting #7527's fix into this PRThe standing rule is to port an existing fix rather than wait, because the port no-ops once the base carries it. That justification does not hold here, so porting would be net-negative:
So: no commit pushed, and no re-run spent (the group is gone; re-running a discarded merge group measures nothing). What this PR actually needsNothing in the diff. Its own head is green on all 33 checks, Generated by Claude Code |
|
Follow-up to the comment above, which framed the ask conditionally: that condition is now met. #7527 landed on Re-verified just now against a So the PR needs exactly one re-queue and nothing else. Recording it once; I won't repeat it. Generated by Claude Code |
…est-config-unification # Conflicts: # packages/plugin-charts/package.json
Merge conflict against
|
| line | main |
this PR |
|---|---|---|
type-check |
→ tsc --noEmit && tsc -p tsconfig.test.json |
untouched |
test / test:watch |
untouched | → --root ../.. packages/plugin-charts/ |
Resolved as the union — nothing dropped from either side:
"test": "vitest run --root ../.. packages/plugin-charts/",
"test:watch": "vitest --root ../.. packages/plugin-charts/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",Keeping main's half is not optional: the same change added packages/plugin-charts/tsconfig.test.json, and that second tsc invocation is what type-checks it.
One interaction I checked rather than assumed. That new tsconfig.test.json could have listed the vitest.config.ts this PR deletes, which would have made main's new type-check red here for a reason nothing else would surface. It does not — it includes only src/**/*.test.ts{,x} and src/**/*.d.ts, and tsconfig.json includes only src. Neither program ever saw that file.
Merge, not rebase, per AGENTS.md §9 — no history rewritten, no force-push. The merge brought ~150 files from main; everything but the one hunk auto-merged.
Validation on the merged tree, before pushing
merged tree vs origin/main 106 files, +837 −904 (identical shape to pre-merge —
the merge added nothing of its own)
vs previous head 0a0f7def9 1 file, +1 −1 (only the type-check line)
unmerged entries 0
| check | result |
|---|---|
vitest run scripts/ (whole gate suite) |
100 files / 2943 tests passed |
| the 6 script-shape & guard pins¹ | 467 tests passed |
pnpm --filter @object-ui/plugin-charts test — the resolved line itself |
45 files / 410 tests passed, and it ran, so the --root ../.. shape is not refused by the guard |
turbo run type-check --filter=@object-ui/plugin-charts |
10/10 tasks successful, executing tsc --noEmit && tsc -p tsconfig.test.json |
Re-derived against the merged tree, because main landing new packages would have broken this PR's new walk: 0 vite/vitest configs added on main since the merge base; all 23 packages/*/vite.config.* still carry the guard with no test: block; 0 per-package vitest configs remain; 0 workspaces whose vitest test script lacks --root.
¹ vitest-invocation-guard, turbo-test-inputs, turbo-build-inputs, package-scripts-vitest-projects, vitest-config-alias-targets-3944, turbo-type-check-inputs.
The approval
This push resets the APPROVED review that was pinned to 0a0f7def9, so the governed-surface guard will hold the PR until it is re-pinned. That cost was unavoidable rather than chosen: the alternative was leaving the PR unmergeable. Nothing in the diff changed except the one type-check line — git diff 0a0f7def9 HEAD is 1 file, +1 −1.
Generated by Claude Code
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Fixes #3240
Direction A, ruled by the maintainer 2026-08-06 and reaffirmed by the delegated ruling of 2026-08-10: delete the per-package vitest configs, make the root
vitest.config.mtsthe single entry, and reach it from a package by path filter. The card BODY still says 「需要维护者先定」 — that sentence predates the ruling.What was there, re-measured rather than inherited
The card says 17, an August comment says 18 non-root. Measured on
main: 19 non-root + root = 20. The 17 underpackages/match the card's list exactly; the extras areapps/consoleandexamples/schema-catalog.git log --diff-filter=A); the August "18" was an arithmetic slip — that comment names 8 files in a bullet it labels 7. It has been 19 since May.defineConfig28840604c):happy-dom+globals+ a local setup, and no alias tablecore,react,typestwo-liners;components,fields,plugin-dashboardmerge a vite configpackages/examples/schema-catalog(root minusprojects);apps/console— kept, see belowWhich packages genuinely needed a local config: none
Per the card's own framing, for each deleted config:
environment/globals— the rootdom/dom-heavyprojects already declarehappy-dom, andglobals: trueis set at root level.setupFiles— 10 of the 11 were the same one-liner,import '@testing-library/jest-dom'.vitest.setup.dom-light.tsximports it plus RTLcleanup()and the base polyfills, so it strictly supersedes them.plugin-mapis the one exception, and it resolves the same way. Its setup was 78 lines mockingmaplibre-gl.vitest.setup.base.tshas mockedmaplibre-glglobally all along — the package copy was a duplicate that no canonical invocation ever loaded.src/. This is the divergence the card is about, and deleting is the direction that removes it.apps/console/vitest.config.tsis not deleted, and it is not one of the card's 17: the rootprojectsarray names it by absolute path, so it is a declared root project (@object-ui/console), andpackage-scripts-vitest-projects.test.tsasserts that project name resolves. Deleting it would drop 88 files.On the card's warning that a root-side green can itself be accidental:
plugin-calendar/src/registration.test.tsxruns indom-heavy, wherevitest.setup.dom.tsxdoes pre-import@object-ui/components. That warming is still there — but #3219 already converted the file's mock to spreadimportOriginal(), so the mock is a superset of the real module and the result no longer depends on evaluation order. Unification removes the second config; #3219 had already removed the sensitivity.Route 4 — closed first, because deleting without it made things worse
Vitest's config fallback does not stop at
vitest.config.*. With none present it takes the directory'svite.config.*, and every one of the 23 packages has one — each carrying a vestigialtestblock:passWithNoTests: true, a partial alias table, and a setup file the root config never loads. Measured onmain, in a package that already had no vitest config:14 of the 17 deleted configs belong to packages that also have a vite config. Deleting them alone would have moved those 14 onto that route — widening the hole #5406 had shut, in the name of shutting it. So:
packages/PKG/vite.config.tsnow callsassertCanonicalVitestInvocation, andtestblocks are deleted.The call is gated on
process.env.VITEST, because the same file is the package build config. Measured both directions at config-load time: undervitest runit is"true", undervite buildit isundefined. A test run is refused; a build never is —pnpm --filter '@object-ui/plugin-view^...' buildexits 0, andturbo run type-check(81/81) builds the whole graph.Verified after the change, from a package whose config was deleted:
Deliberately not covered:
examples/byo-backend-consoleandexamples/console-starter. They are templates a user copies out of the repo, so a../../scripts/import would break them where it matters most. Both were on route 4 before this change and still are — unchanged, not newly exposed.Redefining the per-package run (the ruling's second clause)
Every package
testscript becomesvitest run --root ../.. PKGDIR/— the shape PR #3869 landed forpackages/runner. 54 script entries across 38 manifests. Before this,pnpm --filter PKG testwas refused by the guard; now:31 is exactly what the collection baseline records for
plugin-view.plugin-aiandvscode-extensionkeep their explicit--passWithNoTests— they collect zero files, and the root config derivespassWithNoTestsfrom whether the CLI names paths, which the converted scripts do. That is the flip a 2026-08-25 comment on the card asked to be ready for.turbo
testand^build— re-derived, and keptThe card asks whether
dependsOn: ["^build"]is still needed. The stated reason is now obsolete; the dependency is not.The reason of record was resolution: per-package configs had no alias table, so a run reached its siblings through
dist. Every package now runs the root config, whose aliases point atsrc, and CI'spnpm testbuilds nothing at all. So nothing needs^buildto resolve, and dropping it looks like free speed.It is not.
^buildis the only edge that puts a dependency's sources into a dependent'stestcache key, andscripts/__tests__/helpers/vitest-config-program.tsnarrows its whole sweep by delegating to exactly that ("turbo'sdependsOn: ["^build"]and per-package$TURBO_DEFAULT$already answer source"). Drop it and a change underpackages/core/srcstops moving@object-ui/plugin-grid#test's key —turbo run testwould replay a stale green over code it never ran. The edge stays, for the second reason instead of the first; the re-derivation is recorded in that narrowing.turbo.json'sbuild.inputsgains$TURBO_ROOT$/scripts/vitest-invocation-guard.mjs, whichturbo-build-inputs.test.tsrequired as soon as the vite configs began importing it.Evidence
Collected test population — the load-bearing one. A deletion that stops collecting a package's files leaves everything green and tests nothing, so both sides are enumerated and diffed programmatically, never by eye:
Assertion counts, per package, both sides. Measured on a comparison worktree at the base commit, same command each side:
1341 is exactly the sum those 18 packages contribute to the collection baseline.
Repo-root suite, from the repo root, 16 shards (a quarter of the suite exceeds this container's foreground cap):
Executed 2459 == collected 2459. Structural corroboration:
vitest.config.mtsand all four root setup files are byte-identical to the base, so no file's project, environment or setup moved.Non-vacuity. A package silently dropped from collection is the defect, so it is proven still collected by making it red. In
plugin-map— the package whose config and whose unique setup were both deleted:#7291 and #7309, asserted at the final commit, not assumed. Both are newer than the ruling:
Gates (exit codes captured by redirect before any pipe):
The changeset is empty-frontmatter: the three files this touches under a released package's
src/are comments in test files naming a config or setup this change deletes. Nothing published moves.Two failures I chased and dismissed, recorded so a reviewer does not re-chase them:
check-readme-exports.test.tsreds in a partially built tree. It branches on build state; my worktree haddist/fromturbo run type-checkwhile CI runs unbuilt. Green in CI's state (87 tests). Reproduced and cleared twice.plugin-timeline/src/ObjectTimeline.colorFieldLadder-7243.test.tsx"rung 2" reds under some concurrency — on unmodifiedorigin/main, in the comparison worktree, and passes alone on both trees. Pre-existing order-dependence, not introduced here, and out of scope for this card.Also updated
scripts/vitest-invocation-guard.mjsdocstring (the four routes),scripts/__tests__/vitest-invocation-guard.test.ts(the old two-route topology pins, plus a new walk requiring the guard and the VITEST gate in every packages vite config, and notestblock there),turbo-test-inputs.test.ts(its two resolution pins were driven from the package scripts, which now all name--root ../..— they would have assertedvitest.config.mtsfor the trivial reason that the script said so), AGENTS.md, QUICK_REFERENCE.md, and three stale comments naming deleted files.⛔ Not folded in, per the 2026-08-06 ruling: the lint rule against whole-module
vi.mockof workspace packages. That is a separate card.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code