Skip to content

test(repo): one vitest config, one verdict — delete the 17 per-package configs (#3240) - #7523

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-3240-vitest-config-unification
Sep 4, 2026
Merged

test(repo): one vitest config, one verdict — delete the 17 per-package configs (#3240)#7523
os-zhuang merged 5 commits into
mainfrom
claude/issue-3240-vitest-config-unification

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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.mts the 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 under packages/ match the card's list exactly; the extras are apps/console and examples/schema-catalog.

⚠️ One correction to the card's narrative: the population was not growing. The newest non-root config was added 2026-05-28 (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.

group count what it actually was
standalone defineConfig 11 byte-identical (28840604c): happy-dom + globals + a local setup, and no alias table
re-export / merge the root config 6 core, react, types two-liners; components, fields, plugin-dashboard merge a vite config
outside packages/ 2 examples/schema-catalog (root minus projects); apps/consolekept, see below

Which packages genuinely needed a local config: none

Per the card's own framing, for each deleted config:

  • environment / globals — the root dom/dom-heavy projects already declare happy-dom, and globals: true is set at root level.
  • setupFiles — 10 of the 11 were the same one-liner, import '@testing-library/jest-dom'. vitest.setup.dom-light.tsx imports it plus RTL cleanup() and the base polyfills, so it strictly supersedes them.
  • plugin-map is the one exception, and it resolves the same way. Its setup was 78 lines mocking maplibre-gl. vitest.setup.base.ts has mocked maplibre-gl globally all along — the package copy was a duplicate that no canonical invocation ever loaded.
  • alias/resolution — the standalone 11 had no alias table; the root config maps ~40 specifiers at a sibling's src/. This is the divergence the card is about, and deleting is the direction that removes it.
  • include/exclude — none of them narrowed collection; proven by the collected-set diff below.

apps/console/vitest.config.ts is not deleted, and it is not one of the card's 17: the root projects array names it by absolute path, so it is a declared root project (@object-ui/console), and package-scripts-vitest-projects.test.ts asserts 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.tsx runs in dom-heavy, where vitest.setup.dom.tsx does pre-import @object-ui/components. That warming is still there — but #3219 already converted the file's mock to spread importOriginal(), 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's vite.config.*, and every one of the 23 packages has one — each carrying a vestigial test block: passWithNoTests: true, a partial alias table, and a setup file the root config never loads. Measured on main, in a package that already had no vitest config:

cd packages/plugin-ai && pnpm exec vitest run
=> RUN v4.1.10 /…/packages/plugin-ai     <- root is the PACKAGE
   (no guard output at all)

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:

  • every packages/PKG/vite.config.ts now calls assertCanonicalVitestInvocation, and
  • the vestigial test blocks 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: under vitest run it is "true", under vite build it is undefined. A test run is refused; a build never is — pnpm --filter '@object-ui/plugin-view^...' build exits 0, and turbo run type-check (81/81) builds the whole graph.

Verified after the change, from a package whose config was deleted:

cd packages/plugin-detail && pnpm exec vitest run src/__tests__/detailColumns.test.ts
=> vitest 调用被拒绝:从包目录跑 vitest 会静默跑错测试集 (objectui#3378)     exit 1

Deliberately not covered: examples/byo-backend-console and examples/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 test script becomes vitest run --root ../.. PKGDIR/ — the shape PR #3869 landed for packages/runner. 54 script entries across 38 manifests. Before this, pnpm --filter PKG test was refused by the guard; now:

pnpm --filter @object-ui/plugin-view test
=> RUN  v4.1.10 /home/user/objectui-issue-3240      <- the REPO ROOT
   Test Files  31 passed (31)
        Tests  276 passed (276)

31 is exactly what the collection baseline records for plugin-view. plugin-ai and vscode-extension keep their explicit --passWithNoTests — they collect zero files, and the root config derives passWithNoTests from 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 test and ^build — re-derived, and kept

The 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 at src, and CI's pnpm test builds nothing at all. So nothing needs ^build to resolve, and dropping it looks like free speed.

It is not. ^build is the only edge that puts a dependency's sources into a dependent's test cache key, and scripts/__tests__/helpers/vitest-config-program.ts narrows its whole sweep by delegating to exactly that ("turbo's dependsOn: ["^build"] and per-package $TURBO_DEFAULT$ already answer source"). Drop it and a change under packages/core/src stops moving @object-ui/plugin-grid#test's key — turbo run test would 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's build.inputs gains $TURBO_ROOT$/scripts/vitest-invocation-guard.mjs, which turbo-build-inputs.test.ts required 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:

vitest list --filesOnly   (project-tagged, sorted, diffed)
  origin/main fe4e7a9e8 : 2460 (project, file) pairs
  0a0f7def9             : 2460 (project, file) pairs
  diff                  : EMPTY

Assertion counts, per package, both sides. Measured on a comparison worktree at the base commit, same command each side:

packages files tests base == head
calendar, charts, kanban, map, timeline, view 152 1274
detail, form 211 1985 (+8 skipped)
gantt, grid, list 236 2284
core, types, react, examples/schema-catalog 295 6618
components 229 2148
fields, plugin-dashboard 218 2960
all 17 + schema-catalog 1341 17269 (+8) identical

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):

2459 files passed | 31008 tests passed | 9 skipped | 0 failed

Executed 2459 == collected 2459. Structural corroboration: vitest.config.mts and 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:

mutation on disk : old text 0 occurrences, marker 1, blob 77104ba7 != HEAD e6d6ae38
run              : FAIL |dom| packages/plugin-map/src/index.registration.test.tsx
                   AssertionError: expected true to be false      exit 1
restore          : git checkout HEAD -- ABSOLUTE_PATH  (trap on EXIT INT TERM)
restore proven   : git diff HEAD empty; blob e6d6ae38 == HEAD e6d6ae38; marker 0
re-run           : 1 file / 1 test passed

#7291 and #7309, asserted at the final commit, not assumed. Both are newer than the ruling:

pnpm test:dist                          1 file / 3 tests passed        (dist project)
pnpm exec vitest run --project dist     exit 1, "OBJECTUI_DIST_PINS is not 1"  (its refusal half)
unit-registry-absence-collision.test.ts 15 tests passed                (isolate: false)
grep vitest.config.mts                  isolate: false present, dist project present

Gates (exit codes captured by redirect before any pipe):

turbo run lint          47/47 successful, 0 errors      (whole farm, not a narrowing)
turbo run type-check    81/81 successful
vitest run scripts/     100 files / 2906 tests passed
check:control-bytes · check:vi-mock-specifiers · check:vi-mock-inherit · check:phantom-deps
check:self-import · check:pre-install-import-graph · check:esm-specifiers · check:entry-guard
check:shell-escape-residue                                    all exit 0
check-changeset-presence  ✅ declares 1 changeset, EMPTY frontmatter
check-changeset-no-major  ✅

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.ts reds in a partially built tree. It branches on build state; my worktree had dist/ from turbo run type-check while 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 unmodified origin/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.mjs docstring (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 no test block there), turbo-test-inputs.test.ts (its two resolution pins were driven from the package scripts, which now all name --root ../.. — they would have asserted vitest.config.mts for 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.mock of workspace packages. That is a separate card.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…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
`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
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3181.9 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-nXe7vDDx.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 516.19KB 117.80KB
core (index.js) 6.12KB 2.42KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 242.42KB 61.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 48.12KB 13.34KB
plugin-charts (index.js) 70.87KB 19.72KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.82KB 34.64KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.07KB 64.12KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.50KB 41.08KB
plugin-grid (index.js) 209.25KB 56.71KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.33KB 27.60KB
plugin-map (index.js) 20.55KB 6.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.40KB 3.23KB
plugin-view (index.js) 85.22KB 20.93KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.63KB 1.70KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026

os-sales commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Merge-group red at 17:30Z — not this PR's, and the queue has already corrected itself

Recording this rather than leaving a red run hanging off this PR's number for the next reader to re-chase.

The failure. Run 33783497752, Test (shard 2/4), on merge group gh-readonly-queue/main/pr-7523-dd84b977… — this PR speculated behind #7473. 2 failed / 8031 passed, both in packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx.

Cause: #7473, not this change. That PR retags ```json```jsonc in skills/objectui/rules/protocol.md. The test lifts JSON blocks out of that guide at run time through /```json\n([\s\S]*?)```/g, which cannot match jsonc — the pattern requires the newline immediately after json — so the "bind": "customerNames" block it counter-probes for goes invisible. Full diagnosis and a proposed fix are on that PR: #7473 (comment).

Why it is not this PR's, by file: this change touches no skills/**, not that test file, and nothing under packages/components/src/__tests__/. Its own head 0a0f7def9 is green on all 33 checks, and the failing assertions are on markdown fence tags in a file it never opens.

The queue handled it correctly and unaided: #7473 was dequeued, this PR was re-speculated alone on main as gh-readonly-queue/main/pr-7523-78a3cc23… (merge commit 546095690), and that run is in flight.

⛔ No re-run spent, no commit pushed, no request made of the maintainer — there was nothing here to fix.


Generated by Claude Code

os-sales commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Second merge-group red (17:42Z) — the colorField ladder race, the one this PR's description already named

Dequeued again (CI_FAILURE). This time the group was this PR alone on main (gh-readonly-queue/main/pr-7523-78a3cc23…, merge commit 546095690), so #7473 is not in the picture and the failure had to be re-diagnosed from scratch.

What failed. Run 33784884273, Test (shard 3/4) — 1 failed / 8054 passed / 8 skipped. Shard 2, which was red in the previous group, passed here.

FAIL |dom| packages/plugin-timeline/src/ObjectTimeline.colorFieldLadder-7243.test.tsx
  > rung 2: rgb() and hsl() literals pass through
AssertionError: expected [ 'rgb(1, 2, 3)' ] to deeply equal [ 'hsl(1 2% 3%)' ]
  ❯ …colorFieldLadder-7243.test.tsx:114:77

Rung 2's second colorsFor call returned the first call's colour. That is the shared-lastItems race, verbatim — the failure mode recorded in this PR's description under "two failures I chased and dismissed", where it reproduced on unmodified origin/main in a comparison worktree and passed alone on both trees.

Why it is not this change's, mechanically rather than by appeal to that note:

  • Root vitest.config.mts and all four root setup files are byte-identical to the base, so no file's project, environment, setup or isolation moved.
  • The collected (project, file) set is byte-identical — 2460 pairs, empty diff — so --shard=3/4 puts the same files on the same runner, and this test's concurrency neighbours are unchanged.
  • This test has always run under the root config in CI. This PR deletes packages/plugin-timeline/vitest.config.ts, which CI never loaded; the only other plugin-timeline byte it touches is a comment naming a deleted setup file.

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 lastItems, two live ObjectTimelines inside one it because RTL cleanup runs in afterEach, and a length-only predicate that cannot say which one wrote — is exactly the shape observed here. It closes #7521, the finding filed from this lane earlier today.

⛔ Not porting #7527's fix into this PR

The 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 needs

Nothing in the diff. Its own head is green on all 33 checks, mergeable_state is clean, and the approval is still pinned. It needs one re-queue, after #7527 lands — and on a governed PR the enqueue is the maintainer's action, not a seat's, so I am not taking it.


Generated by Claude Code

os-sales commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to the comment above, which framed the ask conditionally: that condition is now met. #7527 landed on main as c2fc261f at 17:30:48Z, so the colorFieldLadder race that took the second merge group out is fixed at the base.

Re-verified just now against a main that has moved several commits further: mergeable_state clean, head still 0a0f7def9 (no commits pushed since the review), combined status success, and os-zhuang's APPROVED review still pinned to that exact head commit. Nothing here needs a push — the merge queue speculates against current main, so the fix is picked up on re-queue.

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
@os-sales
os-sales requested a review from os-zhuang September 4, 2026 01:49

os-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Merge conflict against main — resolved and pushed (0a0f7def90d17f2e64)

While this sat un-queued, main advanced to ccb3ad78a and the PR went dirty. A conflicted PR cannot be queued at all, so this one was mine and it is done. Re-requesting @os-zhuang because the push necessarily resets the pinned approval — see the last section.

The conflict was one file, one hunk. packages/plugin-charts/package.json, where both sides edited adjacent lines of the same scripts block for unrelated reasons:

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

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3182.4 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-B81JMUKB.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 516.21KB 117.82KB
core (index.js) 6.12KB 2.42KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.40KB 61.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 48.09KB 13.34KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.89KB 34.68KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 250.93KB 64.09KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.44KB 41.05KB
plugin-grid (index.js) 209.25KB 56.71KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.55KB 6.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.38KB 3.22KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit d06ca4a Sep 4, 2026
33 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-3240-vitest-config-unification branch September 4, 2026 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

17 个包各自带 vitest.config.ts 与根 vitest.config.mts 行为分叉:先回答「哪些包真的需要本地配置」

3 participants