Skip to content

test(plugin-detail): drop the unpublished @object-ui/fields deep subpath (#4325) - #4460

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4325-fields-subpath-surface
Aug 12, 2026
Merged

test(plugin-detail): drop the unpublished @object-ui/fields deep subpath (#4325)#4460
yinlianghui merged 1 commit into
mainfrom
claude/issue-4325-fields-subpath-surface

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Closes#4325.

The defect

packages/fields/package.json publishes exactly two entry points — . and
./style.css. packages/plugin-detail/src/__tests__/RelatedList.longFormColumns.test.tsx
side-effect-imported a third:

import '@object-ui/fields/widgets/MarkdownContent';

It resolved only because vitest.config.mts aliases the whole package name to
its SOURCE directory. Under Node's own resolution it is
ERR_PACKAGE_PATH_NOT_EXPORTED; under tsc with the standard paths: {}
template every wired-up package uses, it was TS2882. That is why
plugin-detail/tsconfig.test.json carried the repo's only source-tree
paths entry — restating the vitest alias so the file could be type-checked at
all.

The import was load-bearing, not decorative: the two "no column derived" cases
assert absence right after awaiting the row, and the markdown cell renderer sits
behind React.lazy, so without the preload a wrongly-derived column reads as
absent while its chunk is still in flight.

The ruling, and the fix

#4325 ruled the subpath out: @object-ui/fields' surface is its index, and
one test's preload does not justify minting permanent public API plus a matching
dist layout to maintain forever. So the import is deleted, paths is now {}
like every other wired-up package's, and the anti-race guarantee is rebuilt
rather than dropped.

The ruling's preferred option (a) — preload through the public index — was
measured and refuted. Importing @object-ui/fields only evaluates the module
that DECLARES React.lazy(() => import('./widgets/MarkdownContent')); the
factory never runs, so the chunk stays cold. A probe rendering
MarkdownCellRenderer straight after that import saw the Suspense fallback
synchronously and waited 321.8 ms for the chunk on an idle container.
AGENTS.md records first-import() latencies up to 976 ms under full parallel
load against RTL's 1000 ms default, so any findBy/waitFor on post-boundary
content is a coin flip decided by machine load.

Option (b) therefore lands: the race is removed instead of won. Every case
asserts through one documentCells() predicate, on a witness present in every
state of the lazy boundary — the Suspense fallback's raw value, the resolved
markup, and (for a normal non-fitContent column) the data-table cell wrapper's
title=, which is rendered outside the Suspense boundary. The fallback paths
option (c) was not needed.

The positive case moved to the same predicate too. It used to be
findByText('Heading'), which only ever passed because the deleted preload had
already resolved the chunk — leaving it would have converted the fixed flake
into a new one. One predicate reporting 1 there and 0 in the absence cases is
also what makes both directions provably non-vacuous.

Red-first — three stages, SKIP_TYPES scratch-broken back to the #4250 defect

stageshape under testresult
1new file, unmodifiedRED — 2 failed, on the header assertions
2header assertions neutralised, witness aloneREDexpected [ td, td ] to have a length of +0 but got 2
3the OLD assertions restored verbatim, no preloadGREEN — 5 passed

Stage 3 is the point of the card: with the derivation genuinely broken and the
preload gone, queryByText('Heading') and querySelector('td h1') both pass —
the green-for-wrong-reason trap #4325 predicted, reproduced and now pinned shut.
Stage 2 shows the replacement catches exactly that case. Both files were restored
from the commit afterwards and re-verified.

Green

pnpm --filter @object-ui/plugin-detail test → 78 files, 761 tests passed
pnpm --filter @object-ui/plugin-detail type-check → exit 0 (tsc --noEmit && tsc -p tsconfig.test.json)
pnpm --filter @object-ui/plugin-detail lint → 0 errors
node scripts/check-type-check-coverage.mjs → OK
node scripts/check-changeset-presence.mjs → OK
node scripts/check-changeset-no-major.mjs → OK
pnpm run check:control-bytes → OK

Zero behaviour change: no package source is touched, so the changeset declares an
empty frontmatter (releases nothing). The dispatch's default "patch" grade
assumed the other branch of the ruling — publishing ./widgets/* would have been
a real package change; ruling the subpath out leaves nothing to release.


Generated by Claude Code

…ath (#4325)
The pre-resolve import `@object-ui/fields/widgets/MarkdownContent` resolved
only through this repo's vitest source alias; the package's exports map
publishes `.` and `./style.css` only. #4325 ruled the subpath out rather than
publishing it, so the import is gone, tsconfig.test.json's paths is now {} like
every other wired-up package's, and the test's anti-race guarantee is rebuilt on
a witness present in every state of the lazy boundary.
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 12, 2026 9:37am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)24.7 KB350 KB
Entry fileindex-CdlZeCFp.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)9.56KB3.59KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)8.92KB3.41KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)489.32KB108.45KB
core (index.js)2.99KB1.14KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)153.42KB41.19KB
fields (index.js)228.99KB56.82KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)3.35KB1.38KB
i18n (pickLocalized.js)3.69KB1.73KB
i18n (provider.js)23.12KB7.62KB
i18n (useDisplayLocale.js)2.33KB1.20KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.98KB10.85KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)45.23KB12.45KB
plugin-charts (index.js)62.01KB17.63KB
plugin-chatbot (index.js)181.17KB43.03KB
plugin-dashboard (index.js)120.75KB31.38KB
plugin-designer (index.js)212.58KB42.83KB
plugin-detail (index.js)239.03KB59.77KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)114.58KB27.68KB
plugin-gantt (index.js)164.14KB39.98KB
plugin-grid (index.js)187.99KB49.92KB
plugin-kanban (index.js)48.62KB13.42KB
plugin-list (index.js)110.20KB26.79KB
plugin-map (index.js)18.16KB5.81KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.99KB10.74KB
plugin-timeline (index.js)26.21KB7.52KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.08KB20.55KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)23.73KB7.96KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.23KB0.66KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.05KB1.52KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT — step-7 复核 by PM session session_017Qqyix2QcnpUC9XeYVDzx3 (wind-down mode, focused review; comment delayed by the shared identity's API rate limit, review was completed at report time).

  • Probes re-run to completion and the dead agent's claims reproduced, not trusted: option (a) — preload via the public index — is refuted in principle (the lazy factory is a dynamic import inside the index; importing the index never runs it), option (b) landed, fallback (c) unneeded. The ruling ordered candidates by preference and asked for selection by measurement; this is compliance.
  • Three-stage red-first accepted as the decisive form: stage 3 — the OLD assertions restored verbatim against the scratch-broken derivation, GREEN with the chunk cold — reproduces the card's exact predicted vacuity, proving the replacement witness is load-bearing rather than cosmetic.
  • The inherited diff's latent flake (a positive findByText racing the lazy chunk once the preload is gone) caught and fixed with the same one-predicate witness — right call, and honestly reported as beyond the ruling's text.
  • Empty-frontmatter changeset deviation ACCEPTED: ruling the subpath OUT means no package source is touched (one test file + one test-only tsconfig), which is exactly the gate's documented exemption; the dispatch's default patch grade presumed the other branch of the ruling.
  • Surface stronger than asked: no ./widgets/* minted, deep import gone repo-wide, and the tsconfig now carries no source-tree path at all. Probes absent from history (tree hash verified). CI converged green.

Flipping ready + arming auto-merge. Slot NOT refilled per maintainer's wind-down instruction.


Generated by Claude Code


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 12, 2026 10:40
@yinlianghui
yinlianghui added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit 3ca5835Aug 12, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4325-fields-subpath-surface branch August 12, 2026 10:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@object-ui/fields deep subpaths resolve only through the repo vitest alias — its exports map publishes none of them

2 participants

@yinlianghui@claude