test(vi-mock): inherit the real @object-ui/permissions surface in 24 vi.mock factories - #8163
Conversation
…vi.mock factories
Every `vi.mock('@object-ui/permissions', ...)` factory that hand-listed the
package's exports now obtains the real module and spreads it FIRST, and the
specifier joins `COVERED_SPECIFIERS` in `scripts/check-vi-mock-inherit.mjs` in
the same commit -- the gate's documented widening precondition.
Census re-derived through the gate's own exported `scan()` on 571b487:
48 judged / 24 inheriting / 24 frozen -> 0 frozen, 48 inheriting. The
all-specifier population moves 98 -> 74 frozen over 659 judged, with every
other one of the 21 specifier rows byte-identical between the two runs.
STEP 0 measured the barrel before anything was converted: 27 modules and 422
module-scope statements reached from `packages/permissions/src/index.ts`, of
which 18 execute anything and every one is allocation. The only effect leaving
a module is `Symbol.for('objectui.inflightGet')` -- global symbol registry,
idempotent, storing no value and putting nothing on `globalThis`. Zero
registrations, timers, storage, fetch, connections, side-effect imports or CSS.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…mock-inherit-slice8-permissions
✅ 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 |
|
Armed —
Landing stroke follows the merge: content probe on re-fetched Generated by Claude Code |
|
LANDED — Merged at 2026-09-06T22:21:16Z as Slice 9 ( Generated by Claude Code |
Part of #6892 (slice 8: @object-ui/permissions)
Every
vi.mock('@object-ui/permissions', ...)factory that hand-listed the package's exports now obtains the real module and spreads it FIRST, and the specifier joinsCOVERED_SPECIFIERSinscripts/check-vi-mock-inherit.mjsin the same PR — the gate's own documented widening precondition.Base:
571b4870d(carries PR #8146, slice 7, and PR #8147).origin/mainmoved toa4611b3e2mid-slice and was merged in; every reading below is pinned to the shipped headb7d9fd085.STEP 0 — the barrel, measured before anything was converted
Verdict: PROCEED. INERT — the first swept barrel with zero registrations of any kind.
Method: the static import graph walked from
packages/permissions/src/index.tswith the TypeScript parser, every module-scope statement of every reached module classified; corroborated by two column-anchored greps over the same modules and by an empirical import of the real barrel under happy-dom in the lightdomproject, where all 48 sites run.A verdict on a re-export-only barrel is not free — it inherits whatever the re-exported modules do at module scope — so the 43-line barrel was the entry point, not the answer.
ComponentRegistry.registercall sitesfetch/ connectionsThe graph is the package itself plus
@object-ui/types, and nothing else in the workspace; the only external specifiers arereact,@objectstack/specand two of its subpaths.The CLASS of every executing statement, since that is what the ruling asks for rather than inertness by assertion:
createDiscardProofCache()(PermissionProvider5,MePermissionsProvider5) — read at the definition rather than assumed:new WeakMap()plus a returned closure. Pure allocation.createContext(null)plusPermCtx.displayName = 'PermissionContext'— allocation and a property assignment on the object just allocated.new Setof literals (SYSTEM_MANAGED_FIELD_NAMES,AUDIT_FIELD_NAMES,TRANSIENT_STATUS), 1 ×Object.freezeof a literal, 1 ×new WeakMap— pure allocation.Symbol.for('objectui.inflightGet')— the ONLY effect that leaves its module, and it is the global symbol registry, not a global property: interning is idempotent, stores no value, and the empirical probe's ownObject.getOwnPropertySymbols(globalThis)does not contain it.The remaining 404 statements are imports/exports (144), type declarations (182), function/class declarations (45) and literal or arrow-function bindings (33) — none of which executes anything.
Grep/AST agreement: exact, with no disagreement to record this time. The column-anchored timers/globals/storage/network/DOM grep and the
IDENT.method(grep over all 27 modules both return nothing (grepexit 1), matching the walk.Empirical leg, a throwaway probe under happy-dom in the
domproject, run and then deleted withgit statusverified at 0 lines before any conversion began: 7 runtime exports, 0console.warn, 0console.error, ~467ms cold import.⛔ No change to
packages/permissions/src/**was needed or made.STEP 1 — the census, through the gate's own exported
scan()Derived with
scan()imported andcoveredset to all 22 workspace specifiers anyvi.mockcall site in the tree names —COVERED_SPECIFIERSwas never widened-and-reverted.packages/app-shellpackages/plugin-gridpackages/plugin-listpackages/plugin-detailpackages/plugin-calendarpackages/plugin-dashboardpackages/plugin-formpackages/plugin-ganttpackages/plugin-kanbanpackages/plugin-mappackages/plugin-timelinepackages/plugin-treepackages/plugin-viewPopulation over all 22 specifiers: 98 → 74 frozen over 659 judged. Direction check: a diff of the two per-specifier tables with the
@object-ui/permissionsrow removed is empty — every other one of the 21 rows is byte-identical, and zero sites moved the other way.The dispatch's grep was exact on the total (48) and on the per-package file split; it could not separate 24 inheriting from 24 frozen, which is what
scan()adds.Shapes: two. 23 zero-parameter object-literal arrows, and one that was ALREADY
asyncand still frozen (plugin-detail/src/renderers/__tests__/record-details.test.tsx) — it awaitedreact, not the module under mock, which is exactly the shape a name-matching gate waves through and this one does not.Projects (A5, answered): all 48 sites are
.test.tsxand none is inheavyDomTests, so all 48 run in the single lightdomproject. One environment is the whole answer, and the empirical STEP 0 import was taken in it.⭐ The free confirmation is back, and it is the strongest this worklist has had: 24 of the 48 sites — exactly half, spread over twelve packages — already inherited the real barrel on
mainand passed, so the real module was known to load in that environment before anything was converted.Neighbour reading (ruling 4)
Every OTHER
vi.mockfactory in all 24 touched files was read and classified before editing:@object-ui/auth@object-ui/plugin-view@object-ui/collaboration@object-ui/i18n@object-ui/components@object-ui/react@object-ui/plugin-chartsreact-router-domsonner@object-ui/plugin-list⭐ Slice 6's collection-death class did NOT fire, and the neighbour reading says why IN ADVANCE rather than after the fact. These files carry 15 frozen neighbours — the exact shape that killed 15 files in slice 6 — but this barrel's graph reaches neither
sonnernor@object-ui/plugin-list, because it reaches nothing beyond@object-ui/types,reactand@objectstack/spec. Zero neighbouring factories were repaired. The carry-forward: a frozen neighbour is dangerous only when the newly-real module's graph reaches it — walk the graph first, then read the neighbours against it.No test turned red under the real module, so there is no behavioural finding to report and no test's assertions were changed.
Covered set: 12 → 13
'@object-ui/permissions'is appended toCOVERED_SPECIFIERS(exact string match, grow-only), the header's count word moves twelve → thirteen, the slice-8 per-specifier record is added in the shape slices 5–7 used, and the remaining-population paragraph is refreshed to 74.scripts/__tests__/check-vi-mock-inherit.test.tsneeded no edit — it derives the names fromCOVERED_SPECIFIERSrather than copying them, and slice 7 had already moved its neighbour fixture to a specifier no package publishes. 67/67 green, run rather than assumed.Positive control (ruling 6)
Three legs from the committed tree, each under a
trapwith absolute paths, each mutation proven on disk by anchored marker counts before the result is read, each restore proven by STATE —git hash-objectequal to the HEAD blob andgit diff HEADnaming zero paths — never by an exit code.plugin-grid/src/__tests__/bulkDeleteVisibleWhen.test.tsx:47827316231…plugin-detail/src/renderers/__tests__/record-details.test.tsx:5022240a3e0…app-shell/src/views/ObjectView.chartRelay-7823.test.tsx:93dbe1f8eb8…Gate exit 0 after each restore. Legs 1 and 2 exercise the gate's obtain-without-spread rule specifically, since
importOriginalis still named on the line above. Nodistpreflight is needed: the root Vitest config aliases the specifier topackages/permissions/srcand the gate is a source-text scanner.Gates, pinned to the shipped head
b7d9fd085node scripts/check-vi-mock-inherit.mjs@object-ui/permissionsvitest run scripts/__tests__/check-vi-mock-inherit.test.tsvitest run scripts/__tests__/(whole directory)vitest run packages/app-shell/vitest run packages/plugin-grid/ packages/plugin-detail/ packages/plugin-form/type-checkof the four touched packagespnpm type-check:scriptspnpm lint:rootlintof the four touched packages (eslint .)eslint --format jsonover all 25 non-changeset pathspnpm check:control-bytesgrep -naPself-scan of all 26 changed paths (grep exit 1, no match)node scripts/check-changeset-presence.mjspnpm changeset:checknode scripts/check-governed-queue-guard.mjs --test <26 paths>pnpm check:entry-guardpnpm check:vi-mock-specifiersDeclared narrowing.
turbo ls --affectednames 16 packages; the four owning ones were run whole and the other twelve were not. That narrowing is measured rather than assumed: no changed file is imported anywhere in the tree. Agit grepfor each changed test file's stem across*.ts,tsx,mts,mjs,jsreturns nothing outside the file itself (exit 1), andscripts/check-vi-mock-inherit.mjsis named only fromscripts/,.github/and prose changesets. Test files are leaves, so a dependent package has no path to reach this diff. CI runs the full farm regardless.Live E2E (informational)is red on every branch today for an upstream reason (#7990 / objectstack#16186) — not this PR.Growth warning
origin/mainmoved from571b4870dtoa4611b3e2(PR #8154) mid-slice and was merged in. The merge brought no newvi.mockcall site on any specifier: the census on the merged head is identical at 659 judged / 74 frozen / 585 inheriting, and the gate's verdict line is byte-identical apart from the tracked-file count (4386 → 4390). That is the third consecutive quiet slice; the constant was still widened only against a gate run on the tree that ships.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code