Skip to content

test(vi-mock): inherit the real @object-ui/plugin-form surface in 32 vi.mock factories - #8116

Merged
baozhoutao merged 4 commits into
mainfrom
claude/issue-6892-vi-mock-inherit-slice5-plugin-form
Sep 6, 2026
Merged

test(vi-mock): inherit the real @object-ui/plugin-form surface in 32 vi.mock factories#8116
baozhoutao merged 4 commits into
mainfrom
claude/issue-6892-vi-mock-inherit-slice5-plugin-form

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Part of #6892 — slice 5 of the per-specifier vi.mock sweep worklist: @object-ui/plugin-form.

Every frozen vi.mock('@object-ui/plugin-form', ...) factory in the tree now obtains the real module and spreads it FIRST, with the file's own stubs applied as overrides afterwards, and @object-ui/plugin-form joins COVERED_SPECIFIERS in scripts/check-vi-mock-inherit.mjs in the same pull request. The gate reads 0 frozen for the specifier on the tree this branch ships, so the constant line is paid for by the sweep rather than by judgement.

This is the first slice whose sites span three owning packages, and the first to meet a third syntactic shape.

Step 0 — what does inheriting actually RUN?

Mandatory before any conversion, and this is the slice that was flagged as most likely to fail it. The static import graph was walked from packages/plugin-form/src/index.tsx with the TypeScript parser and every top-level statement of every reached module classified.

28 modules reached, 75 module-scope statements. Verdict: PROCEED, with a named, benign side effect — not the "inert" verdict slices 3 and 4 got.

Six of the 75 are module-scope registrations in the barrel itself:

line registration
index.tsx:184 ComponentRegistry.register('object-form', ...) namespace plugin-form
index.tsx:243 ComponentRegistry.register('form', ...) namespace view, skipFallback
index.tsx:295 ComponentRegistry.register('embeddable-form', ...) namespace plugin-form
index.tsx:316 ComponentRegistry.register('form-analytics', ...) namespace plugin-form
index.tsx:360 ComponentRegistry.register('object-master-detail-form', ...) namespace plugin-form
index.tsx:457 ComponentRegistry.register('line_items', ...) namespace record, skipFallback

register() was read rather than assumed: it is a Map.set into an in-memory registry plus a console.warn on the un-namespaced spelling, and all six pass a namespace, so not even the warning fires. This is the same effect slice 2 absorbed for plugin-charts and plugin-dashboard.

The other 69 statements are pure allocation — Sets of literal strings (autoLayout.ts, sanitize.ts, deriveMasterDetail.ts), style and threshold literals, one regular expression at submitRedirect.ts:96, four createSafeTranslation(...) factory calls (occSave.tsx:49, WizardForm.tsx:186, DrawerForm.tsx:59, ModalForm.tsx:60, measured pure by slice 3) and four elementDataSourceBlock(...) marks, which add the renderer to a module-level Set in @object-ui/core.

Absent: zero timers, globals, storage access, fetch, connections; zero side-effect-only imports. Two column-anchored greps over all 28 modules agree with the parser walk exactly — the globals and timers grep matches nothing (exit 1) and the registration grep returns exactly those six lines and nothing else.

The ruling's failure class was checked on the consuming side too. Of the 32 converted files exactly one names ComponentRegistry at all: ObjectView.dataSourceContextFallback-7842.test.tsx READS one entry (get('object-view')) rather than asserting emptiness or a registration count. No converted assertion can be broken by the six new entries. The only console spy in the 32 is a console.error spy, and register() only ever writes to console.warn.

The free confirmation slice 4 lacked exists here. packages/app-shell/src/views/studio-design/StudioDesignSurface.formFields.test.tsx already inherited the real barrel on main and passed, so the real module was known to load in that test environment before anything was converted.

Step 1 — the derived census

Re-derived on the merge base with the FIXED classifier, through the gate's exported scan() with covered set to every workspace specifier any call site in the tree names, so COVERED_SPECIFIERS was never widened-and-reverted.

judged inheriting frozen
@object-ui/plugin-form on the merge base d5c1f527e 32 1 31
after this sweep 32 32 0
on the shipped head 52019690d (after merging main) 33 33 0

All-specifier population: 178 to 147 frozen over 643 judged on the original base, and 148 over 654 on the shipped head. Direction check: every one of the other 21 rows is byte-identical between the two runs — a diff of the per-specifier tables with the plugin-form row removed is empty. Zero sites moved the other way.

Site distribution, exactly matching the dispatch's own grep: 20 under packages/plugin-view, 8 under packages/app-shell (7 frozen plus the 1 already inheriting), 4 under packages/plugin-designer.

The growth warning fired for the third consecutive slice. Merging origin/main mid-slice brought in a thirty-second frozen site on this same specifier — packages/plugin-view/src/__tests__/ObjectView.expandFls-7429.test.tsx, landed by #8089 while this branch was being verified. It is swept the same way in this pull request, so the constant is widened against a gate run on the tree that ships and not against the derivation taken on the base.

The three shapes

  1. Single-line object literal (16 sites, all plugin-view). vi.mock('@object-ui/plugin-form', () => ({ ObjectForm: ... })) becomes an async factory that spreads importOriginal of Record-of-string-to-unknown first.
  2. Multi-line object literal (11 sites, 7 app-shell and 4 plugin-view). Same conversion, one added spread line; every existing override body is untouched.
  3. Dynamic-import delegation (4 sites, all plugin-designer) — new on this slice. vi.mock('@object-ui/plugin-form', () => import('./__mocks__/plugin-form')) handed the entire factory to a shared manual mock module that exports only ModalForm and DrawerForm, and so inherited nothing at all. These become an async factory that spreads the real barrel FIRST and the manual mock module SECOND, so the two hand-written components still win and every other export is now the real one. That the override ordering is right is not asserted, it is measured: those four suites assert on data-testid="mock-modal-form" and stay green.

No site was converted blindly and none deliberately freezes to prove a module is not loaded, so there is no residual and the constant line ships in full.

Residuals and exclusions

Residuals: none. Both exclusions were verified rather than assumed, and both cost nothing:

Ablation — one leg per shape, blob-hash proven

Each leg mutates the committed tree, proves the mutation reached disk by anchored text counts BEFORE the result is read, runs the gate, then restores under a trap on EXIT INT TERM and proves the restore by git hash-object equalling the HEAD blob and by git diff HEAD naming zero paths — never by a checkout exit code.

leg shape file mutation proof gate
A single-line ObjectView.sortSink.test.tsx spread marker 2 to 1, frozen head form 0 to 1 exit 1, names :75 only
B multi-line RecordFormPage.i18n.test.tsx spread marker 2 to 1, frozen head form 0 to 1 exit 1, names :83 only
C delegation ObjectManager.test.tsx spread marker 1 to 0, frozen head form 0 to 1 exit 1, names :16 only

Legs A and B are load-bearing rather than tautological: both files carry TWO inheriting factories on different specifiers, the mutation removes exactly one, and the gate names exactly the one site. Every leg returns the gate to exit 0 after the restore.

No dist preflight is needed: the root Vitest config aliases @object-ui/plugin-form to packages/plugin-form/src, and the gate is a source-text scanner.

Nothing died during COLLECTION

The 32 converted files were run at this head and again with their merge-base blobs restored over exactly those 32 paths under a trap:

tree result
this head 52019690d 32 passed, 288 tests
the same 32 paths at 59a3a233d 32 passed, 288 tests

Identical, so no test was lost or gained and no file died before it ran. The base restore is proven on disk — async-factory markers across the 32 paths drop from 42 to 10, the frozen object-literal head reappears in 28 files and the frozen delegation head in 4 — and the restore leaves git diff HEAD naming zero paths.

Gates

All pinned to the final head 52019690d, exit codes captured by redirect-then-capture, never through a pipe.

gate result
pnpm check:vi-mock-inherit pass, exit 0 — 437 call sites on ten covered specifiers judged, 437 inherit, 0 frozen; plugin-form 0 frozen
pnpm exec vitest run scripts/__tests__/check-vi-mock-inherit.test.ts pass, exit 0, 69 of 69, case count unchanged, pin not edited
the 32 converted files pass, exit 0, 32 files, 288 tests
pnpm exec vitest run packages/plugin-view/ packages/plugin-designer/ pass, exit 0, 53 files, 433 tests
pnpm exec vitest run packages/app-shell/ pass, exit 0 on four disjoint shards: 159 plus 159 plus 158 plus 158 equals 634 files, the whole package; 6099 passed plus 1 skipped
pnpm check:vi-mock-specifiers pass, exit 0
pnpm type-check:scripts pass, exit 0
pnpm check:control-bytes pass, exit 0, 6496 text files; plus a grep -naP self-scan of all 34 changed paths, no match
node scripts/check-changeset-presence.mjs pass, exit 0 — 32 published-source files, one EMPTY-frontmatter changeset
pnpm changeset:check pass, exit 0
node scripts/check-governed-queue-guard.mjs --test NOT GOVERNED, exit 0, 34 paths against 5 governed surfaces
type-check of the three owning packages pass, exit 0 each; non-vacuous — --listFiles names 21 of 21, 4 of 4 and 7 of 7 edited files in programs of 1636, 655 and 4424 files
lint of the three owning packages pass, exit 0 each; non-vacuous — --format json lists 1191 files and all 32 edited files, and intersecting every message against this branch's own 106 added line numbers gives 0

The whole packages/app-shell suite ran as four disjoint shards rather than one invocation because a single run exceeds this container's foreground cap. This is not a narrowing: the four shards partition the same file set, their union is the package's 634 test files, each ran in the foreground to completion, and all four exit 0.

Live E2E (informational) is red on every branch today for an upstream reason (#7990) and is not this branch's reading.


Generated by Claude Code

…vi.mock factories

Part of #6892 (slice 5).

Every frozen `vi.mock('@object-ui/plugin-form', ...)` factory in the tree now
obtains the real module and spreads it FIRST, with the file's own stubs applied
as overrides afterwards, and `@object-ui/plugin-form` joins the
`check-vi-mock-inherit` gate's `COVERED_SPECIFIERS` in the same commit.

Three syntactic shapes, across three owning packages: 16 single-line object
literals and 11 multi-line ones under `packages/plugin-view` and
`packages/app-shell`, plus 4 `packages/plugin-designer` sites that delegated the
whole factory to a shared manual mock module and so inherited nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…lice

Part of #6892 (slice 5).

Merging `origin/main` brought in one more frozen factory on this specifier —
`ObjectView.expandFls-7429.test.tsx`, landed by objectui#7429 while this slice
was being verified. It is swept the same way, so the gate reads zero frozen for
`@object-ui/plugin-form` on the tree this pull request actually ships.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3186.4 KB 3191.4 KB
Main entry chunk (gzip) 143.5 KB 350 KB
Entry file index-YAcvZiUJ.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) 498.00KB 113.96KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 182.08KB 50.62KB
fields (index.js) 242.43KB 61.25KB
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) 6.57KB 2.76KB
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.84KB 10.94KB
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.99KB 0.87KB
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 (useSpecGesture.js) 4.39KB 1.66KB
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.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.35KB 19.68KB
plugin-chatbot (index.js) 193.54KB 46.04KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.59KB 63.48KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.56KB 56.63KB
plugin-kanban (index.js) 52.46KB 14.57KB
plugin-list (index.js) 113.34KB 27.72KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.48KB 20.81KB
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 (expression.js) 0.20KB 0.18KB
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.73KB 2.28KB
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

Copy link
Copy Markdown
Contributor

Standing down on Live E2E (informational) for this PR, read 2026-09-06T17:12Z.

Every other check on 52019690d is judged on its own; this PR flips to ready once the required set converges, then goes to the merge queue by squash.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 17:23
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026

Copy link
Copy Markdown
Contributor

Armed — squash auto-merge enabled and the PR is in the merge queue, domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

  • Head 52019690d: 31 non-informational checks completed green at 17:22Z (the ci-wait reading); Live E2E (informational) red per the standing-down note above.
  • ready_for_review at 17:23:12Z; the post-flip Governed Surface Queue Guard run started after that timestamp and completed success at 17:23:28Z.
  • added_to_merge_queue at 17:23:36Z on the REST timeline (read 17:23:42Z). PR docs(guide): make three drifted counts on ci-cd-pipeline.md self-maintaining #8121 is ahead of it in the queue.

On MERGED: content probe on re-fetched origin/main ('@object-ui/plugin-form' in COVERED_SPECIFIERS, importOriginal in a converted plugin-view test, the empty-frontmatter changeset present; control: '@object-ui/collaboration' still in the constant and an untouched plugin-grid factory unchanged), then the LANDED note here and on #6892 (which stays open, Part of). #7883 becomes dispatchable after the landing.


Generated by Claude Code

Merged via the queue into main with commit e920343 Sep 6, 2026
33 of 34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-6892-vi-mock-inherit-slice5-plugin-form branch September 6, 2026 17:38

Copy link
Copy Markdown
Contributor

LANDED — merged at 2026-09-06T17:38:50Z as e920343de (squash, via the merge queue), domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

Content probe on re-fetched origin/main (e920343de, read 17:39:21Z):

  • '@object-ui/plugin-form' is the tenth member of COVERED_SPECIFIERS in scripts/check-vi-mock-inherit.mjs (line 354).
  • importOriginal appears 4 times in packages/plugin-view/src/__tests__/ObjectView.sortSink.test.tsx (the converted file the dev's ablation leg 1 used).
  • .changeset/vi-mock-inherit-slice5.md is present.
  • Controls: '@object-ui/collaboration' still in the constant (1 hit) and the Object.freeze declaration unchanged.

#6892 stays open (Part of); the LANDED note goes there in the same stroke. #7883 (retire deJsxClosingTags in the same gate) is now dispatchable.


Generated by Claude Code

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.

2 participants