test(vi-mock): inherit the real @object-ui/plugin-designer surface in 10 vi.mock factories - #8233
Conversation
… 10 factories
All ten `vi.mock('@object-ui/plugin-designer', ...)` factories in the tree
hand-listed three of the barrel's 29 exports, so any export a file's import
graph reads at module scope resolves to `undefined` and the file dies during
COLLECTION with zero failed assertions (objectui#6892).
Each factory now obtains the real module through the typed generic and spreads
it FIRST, keeping every hand-written stub after it. Both consumers declare the
edge (`packages/app-shell` dev + peer, `apps/console` dev), so the typed
spelling is licensed.
Two of the ten also gain a module-scope `import '@object-ui/plugin-designer'`.
`AppContent` reaches `CreateAppPage` only through
`React.lazy(() => import('@object-ui/plugin-designer'))`, so the now-awaiting
factory first runs inside those files' `findByTestId` budget; under a saturated
transform pipeline the barrel's 619-module graph does not fit in it and the
file goes red on LOAD rather than on behaviour. Moving the cost into the import
phase is the repo's documented repair for that class. No assertion changed.
Part of #6892 (slice 11: @object-ui/plugin-designer)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
The sweep in the previous commit takes this specifier to zero frozen, which is the gate's documented precondition for joining COVERED_SPECIFIERS. Fifteen members become sixteen; the header gains the slice-11 per-specifier record in the shape slices 5-10 used, and the remaining-population paragraph is refreshed from this run's census (50 -> 40 frozen, @object-ui/app-shell still PARKED under objectui#8173). Gate on this tree: exit 0, 602 call sites judged over sixteen specifiers, 602 inherit, 0 frozen for @object-ui/plugin-designer. Part of #6892 (slice 11: @object-ui/plugin-designer) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ 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
|
|
Standing down on That check is informational (not in the Generated by Claude Code |
|
Flip deferred, not withheld — CI on Generated by Claude Code |
|
Armed —
Landing stroke follows the merge: content probe on re-fetched Generated by Claude Code |
|
LANDED — Merged at 2026-09-07T03:28:55Z as Slice 12 ( Generated by Claude Code |
Part of #6892 (slice 11:
@object-ui/plugin-designer)Eleventh slice of the per-specifier
vi.mocksweep. All ten frozen factories on@object-ui/plugin-designernow inherit the real module's export surface, and the specifier joinsCOVERED_SPECIFIERSin this same PR — the gate's documented precondition (zero frozen, then admit).Base:
ecf8e726e(carries PR #8207, slice 10). Head at the time of every reading below:89fb451e2.STEP 0 — PROCEED
Method. The static import graph was walked from
packages/plugin-designer/src/index.tsxwith the TypeScript parser, every module-scope statement of every reached module classified, TS ESM'./x.js'resolved to./x.ts/.tsx, and the alias table read out ofvitest.config.mtsitself. It was then corroborated empirically by importing the real barrel under both vitest projects the ten sites run in.Graph. 619 modules, 6,825 module-scope statements (2,850 import/export, 1,456 fn/class declarations, 1,099 bindings, 968 type declarations, 452 other) over fourteen workspace packages:
components206,core95,fields77,react64,plugin-grid31,plugin-designer29,plugin-form28,i18n27,types18,mobile16,permissions10,sdui-parser8,data-objectstack5,providers5. 98 bare side-effect imports (the@object-ui/componentsrenderer cascade) and one CSS import, inert because the root config declares nocssoption.Registrations — NOT inert, and NOT bare. 130 module-scope
ComponentRegistry.register(...)calls, zero of them bare:ui89,plugin-designer10,element10,page7,action5,plugin-form4,plugin-grid2,view2,record1. The five rows inpackages/components/src/renderers/layout/page.tsxthat read as bare carrynamespace: 'ui'through the spreadpageMetaconstant (page.tsx:663), soregister()'s deprecationconsole.warncannot fire. The namespace lives inregister()'s third argument, never in the registered key — slice 10's correction, re-confirmed here. Beyond registration the only effects are the benign class slice 7 already measured inpackages/fields: fiveregisterFieldRenderer(...)map writes, onesetCellRendererResolver(...)assignment,registerAllFields(), onesubscribeDataChangeslistener add and twoforEachregistration loops. Zero timers, globals, storage,fetchor connections.Empirical import, per project. Zero
console.warnand zeroconsole.errorin both, 29 exports in both.dom(light setup; the nine app-shell files)apps/consoleproject (merged config, HEAVYvitest.setup.dom.tsx; the console file)The order-of-magnitude gap is slice 10's finding repeated: the console project's setup has already loaded 391 registry keys before the barrel is asked for. Neither number decides anything.
Marginal cost, in the files that pay it (three runs each,
Durationmedians):packages/app-shell/.../AppContent.bootRedirectCoverage.test.tsx(app-shell,dom)apps/console/src/__tests__/AppContent.systemHubRoutes.test.tsx(console project)packages/app-shell/.../AppContent.noAppsCta.test.tsx(the file that actually renders a designer route)packages/app-shell/.../AppContent.pseudoRouteSegments.test.tsx(ditto)Aggregate — the number CI pays, and the one that decides. The ten files as ONE invocation, including two legs taken as an interleaved A/B against a committed tree so machine drift could not decide it:
+3.6 s (~+10%) for the whole slice, 103/103 tests green in both states — the same order as slice 10's +2.7 s, and nowhere near the retired objectui#6580's ~10 s x 22 files STOP band. Verdict: PROCEED.
Mechanism, measured rather than argued. Every one of the ten files already holds 590 of the barrel's 619 modules at module scope (with the mocked specifiers excluded from the walk), so inheriting adds exactly the 29 modules of
packages/plugin-designer/srcitself. That is why the per-file marginal is a third of a second against a 6.9 s cold import.Two of the ten went red on load timing, not on behaviour — and only in the ten-file invocation, never alone.
AppContentreachesCreateAppPageonly throughReact.lazy(() => import('@object-ui/plugin-designer'))(AppContent.tsx:73-75), so an inheriting factory first runs when that lazy boundary resolves — i.e. inside the RTLfindByTestIdbudget of the two files that assert on the rendered designer page. Alone they pass; with ten files in flight the transform pipeline is saturated, the 29 added modules do not fit in 1000 ms, and the assertion fails while the shell is still booting (Unable to find an element by data-testid=create-app-page, 1021 ms, with the boot screen still on the page).The repair is the one AGENTS.md prescribes for an unbounded module load counted against a bounded window: a module-scope
import '@object-ui/plugin-designer';in exactly those two files, which moves the cost into the import phase where no timeout applies. The specifier is byte-identical to the component's own, so ESM hands the lazy factory the already-resolved module. No assertion, and no test's behaviour, was touched; the timeout was NOT raised. Five subsequent aggregate runs are green. This is declared as a deviation from the dispatch's "the ten factories and nothing else" surface, because it is two extra lines in two of those same ten files.Census, through the gate's own exported
scan()Re-derived on
ecf8e726ewithcoveredwidened in memory to the 22 workspace specifiers anyvi.mockcall site names; the constant on disk was never widened-and-reverted.@object-ui/plugin-designerOwning packages: 9 under
packages/app-shell, 1 underapps/console— the first slice on this worklist to reach outsidepackages/. All ten are one syntactic shape (the zero-parameter object-literal arrow), each hand-listing the same three of the barrel's 29 exports, so the repair is the same factory shape ten times: shape count = 1.Population: 659 judged, 609 inherit, 50 frozen before -> 40 frozen after, no site moving the other way. Gate verdict line on the final head:
602 call site(s) on ... 16 specifiers ... judged (602 inherit, 0 auto-mocked); 4403 tracked source file(s), 2677 test-named; 606 carry a mock.Neighbour reading
Every other
vi.mockfactory in all ten files was classified through the gate before editing, then judged against the walked graph. Zero neighbouring repairs owed, predicted in advance:@object-ui/auth@object-ui/i18n@object-ui/react@object-ui/plugin-formSlice 6's collection-death class needs a frozen neighbour that the newly-real graph reaches. There is not a single third-party factory in this family at all, so the frozen
lucide-reactneighbour that killed 15 files in slice 6 cannot exist here even though this barrel's graph readslucide-react106 times. No file among the ten mocks@object-ui/app-shell— re-derived on this base, the parked specifier (objectui#8173) stays parked.Dependency-edge reading
packages/app-shell/package.jsondeclares@object-ui/plugin-designeras aworkspace:^peer (:99) and aworkspace:*dev dependency (:116);apps/console/package.jsondeclares it as aworkspace:*devDependency (:76) — a refinement on the dispatch, which named the line but not the section. Both edges are declared, so the typed genericimportOriginal<typeof import('@object-ui/plugin-designer')>()is licensed in all ten files, andcheck:phantom-depsexits 0 on the final head.Positive control
Two legs from the committed tree, each under a
trapwith absolute paths, onpackages/app-shell/src/console/__tests__/AppContent.bootRedirectCoverage.test.tsx. Every mutation is proven on disk by an anchored count and a changed blob before the gate result is read; every restore is proven by STATE —git hash-objectequalling the HEAD blob andgit diff HEADnaming zero paths — never by a checkout exit code. The on-disk guard is anchored on the designer spread line specifically, because this file carries three other spreads (its already-inheriting auth, i18n and react neighbours) and a file-wide count would never fall to zero. No dist preflight is owed: the gate is a source-text scanner and both configs alias the specifier to source.3426082ebfa89448a5fde77036df045e96461b4f50cbc4da3102f15904c97b3b87f23b2de6fe6977AppContent.bootRedirectCoverage.test.tsx:37with reason the factory obtains the real module but never spreads it9d07165133ee3e8d8281ea26ea89660f9b36c6d8Leg 1 is the shape objectui#8183 names as the only valid control: a spread that merely mentions the obtained module reads as inheriting and would be a void reading. Leg 2's blob is byte-identical to this file's base blob, so the leg provably reconstructed the exact frozen form the gate must reject. Both restores: hash equal to HEAD,
git diff HEADempty, finalgit statusclean.Covered-set diff — 15 -> 16
Exactly one line added to
COVERED_SPECIFIERS('@object-ui/plugin-designer'), the header's count wordfifteen->sixteen, the slice-11 per-specifier record added in the shape slices 5-10 used, and the remaining-population paragraph refreshed 50 -> 40 with@object-ui/app-shellstill annotated PARKED under objectui#8173. The record was written without the shape objectui#8117's pin rejects (a backticked test filename followed by a comma or a bracket): a mechanical scan of the whole gate source for that pattern returns 0 matches, and the pin itself needed no edit — confirmed by running it, since it still derives its names fromCOVERED_SPECIFIERS.Gates — every exit code captured by redirect-then-capture, pinned to
89fb451e2node scripts/check-vi-mock-inherit.mjs@object-ui/plugin-designervitest run scripts/__tests__/check-vi-mock-inherit.test.tsvitest run scripts/__tests__/(whole directory)vitest run packages/app-shell/(4 shards)vitest run apps/console/pnpm --filter @object-ui/app-shell type-check--listFiles— all nine edited app-shell files in a 4,429-file programpnpm --filter @object-ui/console type-check--listFiles— the edited console file in a 3,539-file programpnpm type-check:scriptspnpm lint:rootpnpm --filter @object-ui/app-shell lintpnpm --filter @object-ui/console linteslint --no-inline-config --format jsonover the 11 non-changeset pathspnpm check:phantom-depspnpm check:control-bytes+grep -naPself-scan of every changed pathnode scripts/check-changeset-presence.mjsapps/consolepublishes as@object-ui/console, so one was owed)node scripts/check-changeset-no-major.mjsnode scripts/check-governed-queue-guard.mjs --test(12 paths)pnpm check:entry-guardpnpm check:vi-mock-specifiersThe dependency closure was built first (
pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' --filter '@object-ui/console^...' build, exit 0) — the flag goes BEFORE the filter.Live E2E (informational)is base-red on every branch today for an upstream reason (#7990 / objectstack#16186) and is not this PR's.Files changed (12)
AppContent.*.test.tsxsiblings carrying a frozen@object-ui/plugin-designerfactory — nine underpackages/app-shell/src/console/__tests__/, one underapps/console/src/__tests__/. Two of them additionally gain the module-scope import described above.scripts/check-vi-mock-inherit.mjs—COVERED_SPECIFIERSgains exactly'@object-ui/plugin-designer'; slice-11 header record; fifteen -> sixteen; remaining population 50 -> 40..changeset/vi-mock-inherit-slice11-plugin-designer.md— EMPTY frontmatter.No product source, no
package.json, nothing underpackages/plugin-designer/src/**, and no assertion was touched.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code