Skip to content

fix(core): a registration that declines the bare key no longer clears its stub - #9851

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-9839-register-skipfallback-preserves-stub
Sep 18, 2026
Merged

os-tesla merged 2 commits into
mainfrom
claude/issue-9839-register-skipfallback-preserves-stub

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #9839

Clause-②: no — no exported symbol is added, no key on a published payload moves, and no accept set is relaxed. Which registrations the registry ACCEPTS is unchanged; what changes is which bare key survives a registration that declined to take it.

The defect

Registry.register's bare stub-clearing lines sat OUTSIDE its meta?.namespace && !meta?.skipFallback branch. A registration that explicitly DECLINED the bare key therefore skipped the claim and deleted another declaration's bare lazy stub anyway — and the bare key ended up claimed by nobody at all: the stub that would have loaded the chunk was gone, and the registration that removed it had refused to replace it. Nothing warned, because from the registry's point of view no one was contesting anything.

Line numbers, re-taken (the card's were taken before objectui#9838 landed)

Measured on a freshly fetched origin/main 6c3944c30 at 2026-09-18T09:59:54Z. packages/core/src/registry/Registry.ts is byte-identical between that ref and this branch's base 276d17459 (git diff --quiet exit 0), so both readings describe the same file.

what card said (pre-objectui#9838) re-taken
if (meta?.namespace && !meta?.skipFallback) { :492 :505
this.components.set(type, { — the bare-key claim, INSIDE :506 :547
branch ends :511 :552
this.lazyEntries.delete(fullType); :515 :556
this.lazyEntries.delete(type); — outside, unconditional :516 :557
unregister(...) :530 :571
the components ownership guard inside it :534-:536 :577
the unguarded lazy pair inside it :538-:539 :579 / :580
docblock saying "plus any matching lazy stub" :521-:523 :561-:570, phrase on :564

The repair

The bare stub-clearing now sits inside that branch, under the same predicate that takes the key — the predicate is shared, not duplicated. this.lazyEntries.delete(fullType) stays below and unconditional, which is what keeps a no-namespace registration clearing the bare stub: its full type IS the bare key.

⛔ It does not touch which registrations are accepted. No components write moved. Stop condition 1 not reached.

The unregister phrase, ruled

The docblock promised "the bare-name fallback (when the fallback still points at this registration), plus any matching lazy stub". Ruled here as "a stub that belongs to this registration", from the code's own behaviour rather than from the wording:

unregister already answers that exact question for the bare components key, and answers it "ownership" — it will not drop bare dashboard while it resolves to plugin-dashboard:dashboard. Reading the stub line as unconditional makes the same call, on the same bare key, with the same owner, decide the opposite way purely because the owner happens to be sitting in the other table at that instant — and which table it is sitting in is exactly the "has the chunk loaded yet" race that objectui#9533 and objectui#9821 are about. A policy that flips on chunk-load timing is not a policy.

⭐ It is also not hypothetical: with register repaired and unregister left alone, the identical victim survives one function down — unregister('dashboard', 'view') after a declined registration still takes bare dashboard off plugin-dashboard. The pin unregister leaves a bare stub that belongs to a DIFFERENT full type is that case.

⚠️ The BARE form, unregister(type) with no namespace, is deliberately unchanged and still unconditional on both tables. That is the force form teardown sites pair with the namespaced call (html-page-lazy-blocks.test.tsx calls both), and the components half has the same asymmetry. Pinned by the BARE unregister form stays unconditional on both tables.

⭐ The teaching sweep — triage's escalation clause asks about the SOURCE

Triage asked whether any doc or example teaches skipFallback: true as the way to avoid affecting another declaration, because "那句话正在制造受害者". It does — eight places. Swept at origin/main 6c3944c30, 2026-09-18T09:59:32Z:

file line the teaching
skills/objectui/guides/plugin-development.md 425 skipFallback: true, // Don't overwrite other 'form' registrations — the literal prescription
skills/objectui/guides/plugin-development.md 163 "Use skipFallback: true when multiple plugins register the same base type"
skills/objectui/guides/plugin-development.md 72 the key table: "Don't register non-namespaced fallback (prevents overwrites)"
skills/objectui/evals/plugin-development.json 25 the graded expected answer to "my plugin registers 'form' but it conflicts" IS skipFallback: true
packages/core/src/registry/Registry.ts 504 // Skip this if skipFallback is true to avoid overwriting other components — sitting on the very branch whose delete ran anyway
packages/core/src/registry/Registry.ts 179-181 the skipFallback JSDoc: "This avoids conflicts with other components that share the same base name"
packages/plugin-grid/README.md 68 / content/docs/plugins/plugin-grid.mdx 203 "Bare grid is deliberately not ours. skipFallback: true ... keeps this plugin from claiming it"
packages/plugin-form/README.md 57 / content/docs/plugins/plugin-form.mdx 356 "bare line_items is left to whoever else claims it"

Every one of those promised something the code did not deliver whenever the other declaration's claim was a pending lazy stub. ⚠️ Whether that escalates the grade is triage's call, ⛔ not this PR's and ⛔ not the seat's. The fix is the same either way, and after it every sentence in that table is true. ⛔ None of those files is edited here — they are outside this card's declared file surface, and they no longer need editing for truth.

The one existing green test this changed, named

⚠️ Making the delete conditional turned exactly one existing test red, and it is the named probe stop condition 2 exempts — measured before any test or string was touched: skipFallback on the EAGER door does not preserve the stub's bare claim. Everything else in packages/core stayed green (155 files / 3292 tests). It is rewritten to assert the repair and renamed to skipFallback on the EAGER door preserves the stub's bare claim.

⚠️ A second existing test was rewritten, and it did NOT go red on its own — reporting it by name because that is the interesting case: each door prescribes the remedy that is true for it. The code change leaves it green while falsifying its premise. That pin asserts the eager door's collision warning says skipFallback: true "does not settle this one, because registering ... clears the bare stub either way" — a reason this change removes. Its own comment says it is "⛔ Not a wording preference: the eager door must not prescribe an opt-out that the test above shows does not settle the contest", and "the test above" is the probe that now shows the opposite. So the warning text is repaired (a false console warning shipped by the PR that falsifies it is the worst option), and that pin inverts with it — both doors now prescribe the opt-out, and it asserts the stale disclaimer is gone.

Tests, and proof they can fail

Run at repo root per AGENTS.md; heavy runs serialized through ../objectstack/scripts/pm/os-verify-lock.sh.

  • pnpm exec vitest run packages/core/ — 155 files / 3292 tests, exit 0
  • pnpm test --shard=1/4 — 805 passed, 1 skipped (806 files), exit 0, 801s
  • the 47-file at-risk population — 1557 tests, exit 0. That set is every test that can observe this change, derived mechanically: a test must create a lazy stub (registerLazy), call .unregister(, or assert on the collision warning text. pnpm check:registry-bare-names independently reports the production blast radius: 425 registration claims, 237 bare keys claimed, 59 declined with skipFallback, 1 contested and already in its ledger.
  • pnpm --filter @object-ui/core type-check exit 0 (after pnpm --filter '@object-ui/core^...' build; the changed test file is genuinely covered — tsc -p tsconfig.test.json --listFiles names it)
  • pnpm --filter @object-ui/core lint exit 0 — 258 files, 0 errors
  • gates: check:control-bytes, check:new-line-citations (0 new), check:registry-bare-names, check:doc-types, check:pending-changeset-literals, check:handler-key-reads, check:changeset-presence — all exit 0. check:sdui-registration-pins exits 2 = prerequisite not met (wants a console build); NOT MEASURED locally, left to CI. Shards 2-4 left to CI.

Ablation — four legs, each mutation proven on disk and each restore proven against HEAD

Run through ablation-replace.mjs, which refuses a mutation whose anchor misses and asserts the blob moved; each leg restored with blob == HEAD and an empty git diff HEAD. Run from the committed fix, so the restore leg has a real restore point.

leg mutation red, exactly as predicted
1 put lazyEntries.delete(type) back below the branch (the defect) skipFallback on the EAGER door preserves the stub's bare claim + unregister leaves a bare stub that belongs to a DIFFERENT full type
2 drop unregister's ownership guard unregister leaves a bare stub that belongs to a DIFFERENT full type only — the two bare-form pins stayed green, so the guard is precisely targeted
3 delete the in-branch clear entirely a registration that DOES claim the bare key still clears the stub + the ordinary stub-then-real lifecycle still clears BOTH stub keys
4 restore the pre-fix warning remedy text each door prescribes the remedy that is true for it

Leg 3 is the one that matters for the opposite-direction case the card asks for: it proves those assertions are not vacuously true. ⛔ No ablation artefact is left in the tree — git status is clean and every restore printed its blob equality.

Seat note — the fence was amended, and this section replaces a now-false one

⚠️ Edited by the domain:ui#2 execution seat (session_018HrVaotisyhgmot9o2MLRq), 2026-09-18T10:16Z. The dev writes a PR body once and ⛔ never patches it; the body is the seat's to correct, and this section is the correction. It previously said .changeset/9821-registerlazy-cross-table-collision-warning.md was deliberately NOT edited. That is no longer true, and leaving it would be the exact defect this round exists to close — a false sentence sitting where people read it.

What changed and why. That pending changeset (from objectui#9838, same release) ended by saying the eager door cannot prescribe skipFallback: true 「because it clears the bare stub whether or not the fallback is taken」. This PR removes precisely that reason, so the sentence would have published false into @object-ui/core's CHANGELOG. ⭐ The dev flagged it and refused to widen its own surface, which is correct; the seat then amended the fence by exactly that one file and one sentence. It now puts the old reason in past tense, names objectui#9839 as what removed it, and states what is true: declining the bare key leaves it with its owner, and both doors prescribe the opt-out.

⚠️ This lane's most repeated defect is a false sentence that publishes — three instances in one shift (objectui#9710's changeset stated a mechanism measured false; its next round left 「packages/types is untouched」 standing while the diff moved 62 lines there; this is the third). ⛔ Not a fourth.

Gates on the final diff, measured before this note was written. check-changeset-presence exit 0 · check-changeset-claims exit 0 · check-changeset-overwrite exit 0 in its default report-only mode (exit 1 only under OS_CHANGESET_OVERWRITE_ENFORCE=1). Its finding names M .changeset/9821-… and prints declared at base: @object-ui/core: patch / declares now: @object-ui/core: patch ⇒ the hazard that gate exists for — a colliding filename silently destroying a third party's release declaration — demonstrably did not happen, and its own header names this shape as the legitimate one.

One residual, recorded rather than hidden. That paragraph still OPENS with 「The two doors give different advice on purpose.」, which this change also falsifies. ⛔ Left as is, by seat decision: the paragraph's closing clause explicitly names that it supersedes its own opening sentence, so the text carries its correction inline rather than elsewhere, and the opener is not actionable on its own — a reader deciding whether to use skipFallback reads on and gets the right answer. ⚠️ Stated as a judgement call with its cost, not as a clean result: a reader who skims only the first line gets the stale impression.

The three pending changesets check-changeset-claims flags for naming Registry.ts were read as paragraphs and judged against the final diff, each with a same-command control: 6067-component-meta-derive-from-canonical (the key list is byte-identical between base and head), componentinput-reexport-4972 (no ComponentInput line in the diff), default-children-retired-5051 (defaultChildren absent; control skipFallback matches 13×). ⛔ None falsified.

Attribution, in prose so it survives a body rewrite: produced by the domain:ui#2 execution seat, session session_018HrVaotisyhgmot9o2MLRq.

🤖 Generated with Claude Code

https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq


Generated by Claude Code


Generated by Claude Code

… its stub

`Registry.register`'s bare stub-clearing lines sat outside the
`namespace && !skipFallback` branch, so a registration passing
`skipFallback: true` skipped the bare-key claim and deleted another
declaration's bare lazy stub anyway. The bare key ended up claimed by
nobody: the stub that would have loaded the chunk was gone, and the
registration that removed it had declined to replace it.

The clearing now sits inside that branch, under the same predicate that
takes the key. A registration with no namespace still clears the bare
stub, because its full type IS the bare key.

`unregister` carried the identical unguarded pair, one function down and
against the same victim, and is repaired with it: its bare stub now gets
the ownership test its bare `components` key already had. The bare
`unregister(type)` form stays unconditional on both tables.

The eager door's cross-table collision warning used to talk the author
out of `skipFallback: true`, which was true only while the delete ran
either way. It now prescribes the opt-out, as the lazy door already did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 3 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6067-component-meta-derive-from-canonical.md

  • names packages/core/src/registry/Registry.ts → packages/core/src/registry/Registry.ts — edited by this change

    packages/core/src/registry/Registry.ts declared its own ComponentMeta: thirteen keys, of which nine were restated from @object-ui/types' base.ts, four were registry-only (tier, namespace, skipFallback, labelling), and tags / description were absent — although both are declared on the canonical type and on the ComponentMetaSchema zod mirror. Two of the three authorities agreed and the registration surface did not, so those two keys were unwritable at exactly the declaration most component registrations import. That is the same two-key delta objectui#5893 had just closed inside @object-ui/types, arriving a third time on a third declaration, and objectui#5671 had already made the identical move for the sibling type ComponentInput in this very file.

.changeset/componentinput-reexport-4972.md

  • names registry/Registry.ts → packages/core/src/registry/Registry.ts — edited by this change

    @object-ui/core's ComponentInput (registry/Registry.ts) and @object-ui/types' plugin-scoped ComponentInput (plugin-scope.ts, published as PluginComponentInput) were structural copies of the interface in @object-ui/types' base.ts. Both are now re-exports of that one declaration, which is the disposition objectui#4580 ruled for the identical shape — a structural copy would reproduce the defect the moment either side moved — and the way core/src/types/index.ts already handles SchemaNode.

.changeset/default-children-retired-5051.md

  • names Registry.ts → packages/core/src/registry/Registry.ts — edited by this change

    If you author plugins against the published register-meta table, drop the key. It is gone from skills/objectui/guides/plugin-development.md, which had been teaching it. A meta that still declares it stays valid: ComponentMetaSchema is a plain z.object, and measured on zod 4.4.3 that STRIPS unknown keys rather than rejecting them — so the key is silently dropped from the parse output instead of failing validation. TypeScript authors get the loud signal instead: all three ComponentMeta declarations (@object-ui/types base.ts and plugin-scope.ts, @object-ui/core Registry.ts) no longer offer it, so re-declaring it is now a compile error.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with 6c3944c30 (merge-base with origin/main): 2 file(s) changed outside .changeset/, read against 1153 pending declaration(s) that publish a body (1687 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.8 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-CQLIAD3Q.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) 16.69KB 6.21KB
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) 11.08KB 4.58KB
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) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
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) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.61KB 20.05KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.37KB 65.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.74KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
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.93KB 1.49KB
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 (strict-authoring-face.js) 14.04KB 5.36KB
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

…entries

Prose only, frontmatter untouched, one sentence per file.

The pending objectui#9821 entry closed by saying `register` cannot
prescribe `skipFallback: true` "because it clears the bare stub whether
or not the fallback is taken". This branch removes exactly that reason,
and that sentence publishes verbatim into `@object-ui/core`'s CHANGELOG.
Its closing sentence now states what is true and names the change that
made it so.

This branch's own entry pointed at that paragraph as describing the old
behaviour; with the paragraph corrected in place, it now says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.8 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-CQLIAD3Q.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) 16.69KB 6.21KB
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) 11.08KB 4.58KB
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) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
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) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.61KB 20.05KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.37KB 65.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.74KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
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.93KB 1.49KB
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 (strict-authoring-face.js) 14.04KB 5.36KB
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-tesla
os-tesla marked this pull request as ready for review September 18, 2026 10:38
@os-tesla
os-tesla added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 2982ed9 Sep 18, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9839-register-skipfallback-preserves-stub branch September 18, 2026 10:58
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.

finding(core): register with skipFallback: true still deletes another declaration's bare lazy stub, leaving the bare key claimed by nobody

2 participants