Skip to content

perf(console): the first screen stops loading the spec entries only the metadata designers validate with; the eager-closure ceiling comes down by what that recovered (objectui#11101) - #11342

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11101-lazy-spec-validation
Oct 1, 2026

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11101
Clause-②: no. Load timing only; no contract, export or accept set moves, and validation is unchanged once loaded.

The console-side payback of the objectui#11088 decision 1 = A loan (the @objectstack/* 17.5.0 ceiling raise of PR #11086). Implemented by os-dev for the domain:devx seat 2 claim 5924908810, session https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh.

Measured first

Every @objectstack/spec entry the console bundles sits in one chunk, vendor-objectstack, and that chunk is a static import of the entry. Read on a console build of main at 5262f7d with a throwaway module-graph dump (not committed). "gzip alone" is each module's rendered code gzipped by itself: an attribution, not additive, and larger than the module's share of the chunk.

spec entry rendered gzip alone static importers in eager chunks verdict
root @objectstack/spec 2,370,467 692,269 @object-ui/core, @object-ui/auth, @objectstack/formula, metadata-admin view-item-normalize.ts runtime; objectstack#20646's
/system 1,039,623 281,625 views/FlowRunner.tsx (runtime), metadata-admin metadata-form-i18n.ts, previews/doc-draft.ts runtime
/kernel 886,545 247,041 @object-ui/data-objectstack (runtime), preview and designer modules runtime
/data 614,780 172,275 core, fields, grid, detail, form, client, formula runtime
/ui 584,593 156,024 shell, layout, components, grid, list, view runtime
/shared 368,108 81,754 preview/DraftChangesPanel.tsx (through ConsoleLayout's draft preview bar), inspectors/ObjectFieldInspector.tsx, previews/simulator/flow-sim-validate.ts admin/designer code, eager through the layout and the registration leaf
/api 322,818 90,817 core, data-objectstack, client, formula runtime
/automation 127,672 37,283 utils/approverIdentity.ts (runtime, DeclaredActionsBar) plus metadata-admin inspectors runtime
/ai 108,241 32,100 none: only clientValidation.ts, through await import() lazy in source, eager by the chunk group
/security 58,779 18,500 formula, plugin-grid runtime
/identity 26,836 9,348 inspectors/FlowReferenceField.tsx, eager through register-builtins.ts admin-only, eager through the registration leaf
/integration 23,392 6,981 none: only clientValidation.ts, through await import() lazy in source, eager by the chunk group
/contracts 1,467 639 none (lazy console pages, the linter) lazy in source, eager by the chunk group
@objectstack/sdui-parser (not spec) 19,801 5,694 none (the linter only) lazy in source, eager by the chunk group

⇒ The hypothesis held only in part. clientValidation.ts already loads every spec entry through await import(). What kept /ai and /integration on the first screen was the vendor-objectstack chunk group: a group claims by module id, not by reachability. That is the objectui#5266 mechanism, one package over; the lookaheads in VENDOR_OBJECTSTACK_TEST only ever excluded @objectstack/lint. Every other entry the validator loads is also imported statically by something on the first screen, so no lazy import in the validator can move it.

The change

  1. apps/console/vite.config.ts: the vendor-objectstack group gets tags: ['$initial']. That is rolldown's built-in tag for "statically reachable from an entry", so the group now claims only the entry's static closure. A vendor module that sits behind an import() follows its importer into a lazy chunk. Nothing the first screen executes moves, because $initial IS that closure. The lint lookaheads stay. A group-table pin in the budget's unit test records the tag.
  2. scripts/check-eager-closure-budget.mjs: the ceilings come down by exactly what was recovered (below). The owed squashMerge handle for 048e7f6 is back-filled, and the near-ceiling framework fixture pair holds the aggregate at BASELINE by construction.
  3. A patch changeset for @object-ui/console, whose published dist changes chunk layout. check-changeset-presence owes none, because no published source moved.

Eager closure, before and after

Two builds of one tree, one container, one instrument (CI=true pnpm exec vite build in apps/console, reading apps/console/dist/eager-closure.json), in one os-verify-lock.sh hold. The trees differ only in the vite.config line; the before leg put 5262f7d's blob on disk and the after leg restored HEAD's, each proven by blob hash.

5262f7d (main) 4acbea07 (this PR) delta
aggregate eager closure, gzip 3,671,724 3,638,104 −33,620
vendor-objectstack, gzip 1,704,699 1,670,964 −33,735
the other 329 eager chunks, gzip 1,967,025 1,967,140 +115 (import bookkeeping, 31 chunks, none over 20)
aggregate eager closure, raw 12,366,134 12,257,433 −108,701
eager / total chunks 330 / 2447 330 / 2450 three new lazy chunks: ai, integration, contracts

Noise: the instrument is deterministic per tree. The untagged config read 3,671,724 on three builds (the graph-dump build, the canonical before leg, the ablation's mutated leg), and the tagged config read 3,638,104 on three (the graph-dump build, the canonical after leg, the ablation's restored leg). The recovered 33,620 is 0.37x the 89 KiB regression the gate exists to catch.

The ceiling: lowered by the recovered amount

constant before after
MAX_EAGER_CLOSURE_GZIP_BYTES 3,694,000 3,660,380 (−33,620)
BASELINE 3,648,737 on 048e7f6 3,638,104 on 4acbea07
PER_CHUNK_GZIP_CEILINGS['vendor-objectstack'] 1,723,000 1,689,265 (−33,735)
PER_CHUNK_BASELINE['vendor-objectstack'] 1,703,690 on 048e7f6 1,670,964 on 4acbea07

The ceilings are not re-derived as "reading + half a regression". That would absorb main's own drift since 048e7f6 (+22,987 on 5262f7d) into the ceiling, which is a raise under another name. The live headroom main had before the payback is kept to the byte: 22,276 (0.24x) on the aggregate and 18,301 (0.20x) on vendor-objectstack. No build that passed before and measures under 3,660,380 fails after. REGRESSION_THIS_GATE_MUST_CATCH_BYTES and the other three per-chunk rows did not move.

squashMerge: 048e7f6 landed on main as 81f8498 (#11086, single parent, an ancestor of origin/main). That handle is back-filled in the header prose that still cites the 048e7f6 reading. This change re-pins BASELINE onto its own reading, so the field is null again by its own rule ("the change that re-pins the field above cannot write its own here"). The 4acbea07 → squash back-fill is owed once this merges.

Ablation: re-eager, and the lowered ceiling goes red

node ../objectstack/scripts/ablation-replace.mjs dropped tags: ['$initial'] (anchor 1 → 0, blob 40f6d42c → 32c403ab), rebuilt, and ran the gate:

  • mutated: 3,671,724 bytes, node scripts/check-eager-closure-budget.mjs exit 1. The aggregate line read "3585.7 KB … 11.1 KB over the 3574.6 KB budget", and vendor-objectstack read "1664.7 KB / 1649.7 KB ceiling (OVER by 15.1 KB)".
  • restored: blob back to HEAD (40f6d42c), git diff HEAD empty. The rebuild read 3,638,104 and the gate exit 0.

Validation unchanged once a designer opens

  • Probe on the BUILT console (vite preview, proxied to a --fresh objectstack 17.5.0 showcase backend started by e2e/live/ci/start-backend.sh, Playwright + /opt/pw-browsers/chromium). The probe ran two builds of the same tree, with and without the tag.
    • First screen (/apps/showcase_app, 331 JS requests): no ai-* or integration-* chunk is fetched.
    • /apps/showcase_app/metadata/agent/new: opening the designer fetches ai-rfegJgG7.js. Typing Bad Name! into Name raises "This metadata does not match the spec — 4 validation error(s). Name: Invalid string: must match pattern …".
    • /apps/showcase_app/metadata/connector/new: this designer fetches integration-DM--okeM.js, and the 3-error banner shows.
    • The validator's text is byte-identical between the untagged and the tagged build, for both designers.
    • The only non-2xx was GET /api/v1/usage/storage 404 from the backend, present on both builds. No pageerror.
  • Existing tests, all of packages/app-shell/src/views/metadata-admin/** (373 files): 36 clientValidation / ResourceEditPage / createConformance files plus 1 console file, 398 passed. The other 337 files ran in three chunks: 1066 passed; 1455 passed and 1 skipped; 1462 passed.

Verification (head 7d24740c)

  • Console build + budget, before and after, under the lock: the table above. Checker exit 0 on both. The after build gets exit 0 at the new constants: "3552.8 KB … budget: 3574.6 KB, headroom: 21.8 KB".
  • scripts/__tests__: the 18 files that read apps/console/vite.config.ts or the budget script, 1020 passed. That includes check-eager-closure-budget.test.ts at 179, plus vite-objectstack-spec-dist, vite-objectstack-client-dist, render-budget-comment and check-node-esm-load.
  • apps/console vitest project: 139 files, 1593 passed.
  • pnpm --filter @object-ui/console run type-check exit 0. Reverse check: tags: ['$bogus'] gives tsc -b tsconfig.node.json exit 1 with TS2769 at the group, then restored.
  • pnpm --filter @object-ui/app-shell run type-check exit 0.
  • Both type-checks ran after turbo run build --filter='@object-ui/console^...'.
  • pnpm check:control-bytes OK.
  • node scripts/check-changeset-presence.mjs exit 0.
  • pnpm changeset:check exit 0.
  • pnpm check:changeset-claims exit 0, report-only. It shows two pending bodies naming files touched here, 6681-declared-lazy-marketplace-routes.md and 7173-ai-pending-actions-inbox-i18n.md. I read both, and this diff falsifies neither.
  • pnpm check:new-line-citations 0 new.
  • Narrowed lint: eslint --no-inline-config --format json on the 3 changed code files gives 3 files, 0 errors, 0 warnings.
    • Population: all three are inside eslint.config.js's population; none was reported ignored.
    • Invariance: the config enables no type-aware linting, so this diff cannot move a verdict on an untouched file.
  • NOT MEASURED:
    • The full @object-ui/app-shell suite outside metadata-admin/**, because its ~600 remaining files exceed the foreground window on a box shared by three devs. packages/app-shell/src is untouched by this diff, and CI runs the suite.
    • The repo-wide pnpm lint, which CI owns.

Acceptance notes (observations, not filed)

  • /shared (81,754 gzip alone) and /identity (9,348) are reached on the first screen only by designer code. Their routes in are register-builtins.ts → inspectors/index.ts / previews/index.ts, which the package entry bare-imports, and ConsoleLayout → DraftPreviewBar → DraftChangesPanel. Recovering them means lazy-registering the inspector and preview tables, which is an architecture change, not this card. Carrier: none.
  • DraftChangesPanel.tsx's comment says @objectstack/core (inside @objectstack/client) has a runtime import of @objectstack/spec/shared that keeps that subpath eager. In this 17.5.0 bundle, /shared's importers are the three objectui modules above plus the linter. The comment's premise no longer holds. Carrier: none.
  • e2e/live/studio-editor.spec.ts waits on getByTestId('metadata-edit-page'). git grep finds that string in no other tracked file, and the agent and connector edit pages rendered without it (this probe's first wait on it timed out). Whether the live suite still runs that spec, and how it passes, is NOT MEASURED. Carrier: none.

Generated by Claude Code

claude added 2 commits October 1, 2026 05:15
…tatic closure, so an import()ed spec entry stays lazy (objectui#11101)

`vendor-objectstack` is a static import of the console entry, and an
`advancedChunks` group claims by module id, not by reachability, so every
`@objectstack/*` module it claimed was on every page load, including the
spec's `/ai` and `/integration` entries that only the metadata designers'
client validation reaches, through `await import()` in `clientValidation.ts`.
`tags: ['$initial']` restricts the group to the modules rolldown tags as
statically reachable from the entry; a vendor module behind an `import()`
now follows its importer into a lazy chunk. Nothing the first screen runs
moves: `$initial` is the entry's static closure.

The group-table pin in the budget's unit test records the tag.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…i#11101 recovered, the first payback of the 17.5.0 loan (objectui#11101)

Two console builds of one tree (`5262f7d`, and `4acbea07` = that tree plus
the `tags: ['$initial']` line) weigh 3,671,724 and 3,638,104 gzipped bytes:
-33,620 in aggregate, -33,735 in `vendor-objectstack`, the rest import
bookkeeping. Under objectui#11088 decision 1 = A the raise was a loan, so:

- `MAX_EAGER_CLOSURE_GZIP_BYTES` 3,694,000 -> 3,660,380 (exactly -33,620),
  `BASELINE` re-pinned onto the `4acbea07` reading;
- `vendor-objectstack`'s ceiling 1,723,000 -> 1,689,265 (exactly -33,735),
  `PER_CHUNK_BASELINE` with it.

Not re-derived as reading + half a regression: that would absorb main's
drift since `048e7f6` into the ceiling. The live headroom main had before
the payback is kept to the byte on both lines (0.24x / 0.20x).

`048e7f6`'s owed squash handle (`81f8498`, objectui#11086) is back-filled
in the prose that still cites that reading; `BASELINE.squashMerge` is null
again, owed for `4acbea07` once this merges (the field's own rule). The
near-ceiling `framework` fixture pair now holds the aggregate at BASELINE
by construction, since the aggregate's constant headroom is now the
narrower one. Patch changeset for `@object-ui/console`, whose published
`dist` changes chunk layout.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 2 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/6681-declared-lazy-marketplace-routes.md

  • names apps/console/vite.config.ts → apps/console/vite.config.ts — edited by this change

    Both marketplace chunks were held by CHUNK CO-TENANCY, not by any import of the page: rolldown had parked components/SuggestedBindingsPanel.tsx (statically imported by the eager views/studio-design/StudioDesignSurface.tsx) in the first, and console/marketplace/InstalledListWidget.tsx (bare-imported by the package barrel for its SDUI registration) in the second. An app-shell-eager-leaves group in apps/console/vite.config.ts isolates those co-tenants so the three declared-lazy pages chunk by their own dynamic-only reachability. The console eager closure moves 3180.2 KB → 3171.5 KB gzipped (−8,888 bytes, 48 → 45 eager chunks) with the three per-chunk ceilings unmoved.

.changeset/7173-ai-pending-actions-inbox-i18n.md

  • names scripts/check-eager-closure-budget.mjs → scripts/check-eager-closure-budget.mjs — edited by this change

    The ten pack blocks are locale DATA, and locale data lands in the console's eager framework chunk, so scripts/check-eager-closure-budget.mjs raises that chunk's ceiling from 512,000 to 524,000 gzipped bytes and re-pins its baseline onto a fresh measurement (502,405 to 514,863). Attributed by three console builds: the merge parent reads 510,192, this branch with the ten aiApprovals blocks cut reads 510,192 again, and this branch reads 514,863 — so the whole 4,671-byte delta is the pack data and nothing else. Headroom is kept at the line's own convention (9,137 bytes, 0.10x the regression the gate must catch) rather than widened; most of the overage was pre-existing drift, with the merge parent already at 510,192 of the 512,000 allowed.

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 770cc5ba4 (merge-base with origin/main): 3 file(s) changed outside .changeset/, read against 1897 pending declaration(s) that publish a body (2510 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.8 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Ci9A8kDU.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
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) 27.13KB 7.95KB
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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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) 6.62KB 2.45KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
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) 16.01KB 3.93KB
plugin-calendar (index.js) 52.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.7 KB 3574.6 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-Q7a2N4Ql.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
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) 27.13KB 7.95KB
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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
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) 6.62KB 2.45KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
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) 16.01KB 3.93KB
plugin-calendar (index.js) 52.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 07:12
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 07:12
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 993f27e Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11101-lazy-spec-validation branch October 1, 2026 07:28
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.

console: the metadata-admin-only spec validation leaves the first screen (lazy-loaded), the console-side payback of the objectui#11088 D1 ceiling raise

2 participants