Skip to content

Pin @objectstack/* to 17.1.0 - #5529

Merged
os-support-ai merged 6 commits into
mainfrom
claude/issue-5328-spec-pin-17-1-0
Aug 22, 2026
Merged

Pin @objectstack/* to 17.1.0#5529
os-support-ai merged 6 commits into
mainfrom
claude/issue-5328-spec-pin-17-1-0

Conversation

@os-support-ai

@os-support-ai os-support-ai commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes #5328

Pins @objectstack/spec, @objectstack/client, @objectstack/formula and @objectstack/lint to 17.1.0, adapts the surfaces the new contract moves, and re-baselines the console eager-closure ceiling under the maintainer ruling on #5531. All gates green.

The pin

A lockfile refresh, nothing else. All four packages move together — a split resolution is what produced the dual-version spec graph that reddened check:spec-symbols in this repo's history.

package before after manifest edit
@objectstack/spec 17.0.0 17.1.0 none
@objectstack/client 17.0.0 17.1.0 none
@objectstack/formula 17.0.0 17.1.0 none
@objectstack/lint 17.0.0 17.1.0 none

No manifest edit was required, confirming the card's assumption: all 38 declarations across 33 packages are ^17.0.0, a single distinct range per package, and ^17.0.0 already admits 17.1.0. Two transitive @objectstack/* packages move in lockstep (core, sdui-parser), which is what keeps the graph single-version — counting the 17.0.0-pinned objectstack identity in pnpm-lock.yaml gives 0. One unrelated transitive re-resolved inside its own caret: @mongodb-js/saslprep 1.4.13 → 1.5.0.

motion is untouched, so PR #4958's lockfile change was not absorbed — the branch point is correct.

Note on pnpm update, because it does more than the card asked for: run against the four package names it rewrote all 31 manifests from ^17.0.0 to ^17.1.0, and also silently re-indented two unrelated check:* script lines in the root package.json. Both were reverted and the install re-run, so the lockfile's specifier: fields still read ^17.0.0 while version: reads 17.1.0. The diff is lockfile-only for the pin, as the card specified.

Green-keeping

Each item is forced by the new build and nothing else. Every one was counter-probed against origin/main with the 17.0.0 lockfile to confirm it is bump fallout rather than a pre-existing red.

Fixed here — mechanical, one correct answer already pinned by existing evidence:

  • icontains is a new canonical VIEW_FILTER_OPERATORS member (vocabulary 19 → 20) and is itself a member of VALID_AST_OPERATORS. Three places needed it: data-objectstack's alias table (an identity row like contains — unmapped, an authored icontains reaches the wire raw, the shape that shipped an unfiltered query in objectstack#3948); plugin-view's canonical-to-builder table, which is keyed by ViewFilterOperator and so failed to compile (mapped to the builder's containsCaseInsensitive, the id that authors $icontains — deliberately not folded onto contains, which would rewrite a case-insensitive filter as case-sensitive on the next save); and plugin-list's mapOperator, given an explicit arm rather than left to the default passthrough that happens to work today.
  • onSuccess added to core's SPEC_ACTION_KEYS — diagnostic only. That list feeds warnOnUnknownActionKeys, so without it an author writing the key the spec now declares is told it is unknown.
  • Palette: the stale element:filter exclusion is dropped and the newly-declared record:discussion excluded as the same renderer as the already-offered record:chatter. Nothing the palette offers changes.
  • Console registry parity: the two targetVariable exemptions deleted — they went dangling-and-stale exactly as that file's own docblock predicted they would at the next pin; the five record:* blocks 17.1.0 adds to ComponentPropsMap (37 → 42) pinned using the same pin-dependent shape GA_ONLY_BLOCKS already established.
  • Tombstone judge: 17.1.0 retires element:filter wholly — it leaves PageComponentType while its props schema stays in ComponentPropsMap with every key tombstoned. That is a retired block, not the "broken probe" the old floor assumed, so the floor now applies to blocks the enum still offers, and the control key is searched for rather than taken from the first sorted entry.

Recorded, not fixed — each is capability wiring or a contract call, which a pin bump does not take as a rider. Each uses the gate's own purpose-built, ratcheted ledger, so the entry dies the moment the real fix lands:

One observation filed and not acted on: the palette advertises the pair under the legacy alias (record:chatter) and now explicitly excludes the canonical record:discussion#5495.

Worth recording, because it defeats the obvious check: PageComponentType has 34 members before and after. element:filter left as record:discussion arrived, so a member-count assertion cannot see this release at all.

The eager-closure ceiling — re-baselined by ruling

The pin put the closure over the ceiling. The gate was right to refuse it, so the raise was escalated rather than taken locally; the maintainer ruled option A on #5531 — raise it permanently, with the headroom assertion that guards it.

constant before after
MAX_EAGER_CLOSURE_GZIP_BYTES 3,960,000 4,086,000
BASELINE.gzipBytes 3,881,609 4,005,911
BASELINE.chunks / totalChunks 58 / 507 52 / 508
BASELINE.commit 77f846a8b 4c1623c0c
REGRESSION_THIS_GATE_MUST_CATCH_BYTES 91,136 91,136 — unchanged

Headroom is 80,089 bytes = 2.00% of the new baseline, holding the header's existing ~2% discipline, and 80,089 < 91,136 with 11,047 to spare.

Both constants move in one commit by necessity, not preference: the guard is MAX − BASELINE < REGRESSION_THIS_GATE_MUST_CATCH_BYTES, so raising MAX alone leaves headroom near 200 KB and fails the gate's own unit test. That failure is the guard working.

The inequality is not the point on its own, so the substantive property was checked too: an 89 KiB repeat of the #5266 incident, measured from the new baseline, lands at 4,097,047 — still over the 4,086,000 ceiling. The gate is exactly as sensitive as it was.

REGRESSION_THIS_GATE_MUST_CATCH_BYTES is deliberately untouched. That is the gate's sensitivity, the ruling did not touch it, and a ceiling that rises while sensitivity relaxes is a gate quietly retiring itself.

The baseline was re-measured on this branch after merging origin/main, not carried over from the earlier 4,001,689 reading — main moved 62 commits in between and took the closure up 4,222 bytes on its own. Header prose was updated in the same commit so the comments and the constants agree; the 77f846a8b table stays as the explicitly-labelled motivating measurement.

For the record on where the bytes went: the release is ~930 KB larger uncompressed (dist .mjs 11,661,923 → 12,593,720) and essentially all of the gzipped growth lands in vendor-objectstack-*.js, 1493.3 KB → 1619.5 KB. #5490 is the ruled follow-up that puts a per-chunk budget on exactly that chunk.

Verification

Run on HEAD 4ad75fdfe, exit codes captured before any pipe.

  • pnpm build — turbo Tasks: 43 successful, 43 total
  • pnpm type-check — turbo Tasks: 81 successful, 81 total
  • pnpm lint — turbo Tasks: 47 successful, 47 total (the full farm, not a narrowed run)
  • pnpm test, all four shards: 1,819 files, 23,010 passed, 1 skipped, 0 failed
  • The gate's own unit test, scripts/__tests__/check-eager-closure-budget.test.ts: 27 passed, including the ceiling itself > passes on the measured baseline, with headroom and the ceiling itself > would have failed on the regression it exists to catch
  • All 19 check:* gates PASS, check:eager-closure among them

check:eager-closure, its own verdict line, before and after:

❌ Console eager closure is 3912.0 KB gzipped across 52 chunks — 44.8 KB over the 3867.2 KB budget.
✅ Console eager closure is 3912.0 KB gzipped across 52 of 508 chunks (budget: 3990.2 KB, headroom: 78.2 KB).

Two other gates' own lines:

✅  spec symbol derivation: 1282 files scanned against 4966 spec export names; 13 declared dialects, 4 untriaged collisions in 2 packages.
✅  action forward parity: 5 surfaces checked against 40 runtime-read keys from 4 consumers; 19 justified omissions, 11 known gaps, 0 payloads exempt from the freshness rule.

Generated by Claude Code

claude added 4 commits August 21, 2026 07:14
…oves

Lockfile refresh only — every manifest already declared `^17.0.0`, which
admits 17.1.0, so no dependency range changed. spec, client, formula and
lint move in lockstep (a split resolution is what produced the dual-version
spec graph that reddened check:spec-symbols in this repo's history).

Green-keeping, each forced by the new build and nothing else:

- data-objectstack: identity alias row for `icontains`, a canonical
  VIEW_FILTER_OPERATORS member as of 17.1.0 and itself a member of
  VALID_AST_OPERATORS. Unmapped, an authored `icontains` reaches the wire
  raw — the shape that shipped an unfiltered query in objectstack#3948.
- app-shell palette: drop the now-stale `element:filter` exclusion, exclude
  the newly-declared `record:discussion` as the same renderer as the offered
  `record:chatter`. Nothing the palette offers changes.
- check-spec-symbol-derivation DEBT: `ReferenceRailEntry`, which 17.1.0
  started exporting under a name objectui already used. The shapes disagree
  on `icon` (spec's schema is $strict and refuses it), so reconciling is a
  contract call — filed as #5494.
- check-action-forward-parity KNOWN_GAPS: `onSuccess` x4 declared surfaces.
  ActionRunner has honoured it all along; 17.1.0 supplied the missing half
  by declaring it authorable, so it became owed. Forwarding it is capability
  wiring — filed as #5493.

check:eager-closure is NOT addressed here and is left red deliberately: the
closure grows 129,270 gzipped bytes (3,871,997 to 4,001,267), 41,267 over a
ceiling the maintainer ruled on today in #5468 — "the absolute aggregate
ceiling stays as shipped", and "gate-strength policy is the maintainer's".

Refs #5328
All four are forced by the new contract and nothing else.

- plugin-view: map the newly-canonical `icontains` onto the builder's
  `containsCaseInsensitive` (the id that authors the spec's `$icontains`).
  The table is keyed by ViewFilterOperator, so this was a compile error,
  not a silent gap. Deliberately not folded onto `contains`, which would
  rewrite a case-insensitive filter as case-sensitive on the next save.

- test-support tombstone judge: 17.1.0 retires `element:filter` WHOLLY —
  it leaves PageComponentType while its props schema stays in
  ComponentPropsMap with every key tombstoned. That is a retired block,
  not the "broken probe" the old floor assumed, so the floor now applies
  to blocks the enum still offers. The control key is searched for rather
  than taken from found[0], which had made it depend on sort order.

- console registry parity: delete the two `targetVariable` exemptions,
  which went dangling-and-stale exactly as that file's docblock predicted
  they would at the next pin; pin the five `record:*` blocks 17.1.0 adds
  to ComponentPropsMap (37 entries to 42) using the same pin-dependent
  shape GA_ONLY_BLOCKS already established; exempt
  `record:reference_rail.entries`, an array of objects a flat scalar
  `inputs` carrier cannot express, whose editor also needs #5494's `icon`
  question settled first.

Refs #5328
Three inventories that enumerate a spec vocabulary, all named by their own
ratchets on the bump.

- plugin-list `mapOperator`: explicit `icontains` arm. The emitted spelling
  equals the input, so `default` already returned it — but that file's
  header records relying on the AST set happening to accept a raw view
  spelling as exactly how it stopped discriminating in #3641, so the arm
  is written out and pinned in EXPECTED_AST_TARGET.
- plugin-list tab presets: VIEW_FILTER_OPERATORS is 20, not 19.
- core `SPEC_ACTION_KEYS`: add `onSuccess`. Diagnostic only — the list
  feeds warnOnUnknownActionKeys, so without it an author writing the key
  the spec now declares gets told it is unknown. The forwarding gap stays
  open and tracked (#5493).

Refs #5328
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3908.4 KB 3867.2 KB
Main entry chunk (gzip) 151.1 KB 350 KB
Entry file index-DGDtL58r.js
Status FAIL

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 (index.js) 10.04KB 3.72KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 29.34KB 7.05KB
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) 6.35KB 2.43KB
auth (index.js) 2.77KB 1.22KB
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.02KB 0.89KB
auth (useIsWorkspaceAdmin.js) 3.04KB 1.45KB
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) 506.94KB 113.63KB
core (index.js) 4.51KB 1.80KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 160.38KB 44.54KB
fields (index.js) 237.14KB 59.49KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.22KB 3.08KB
i18n (provider.js) 23.13KB 7.63KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 30.51KB 7.57KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.95KB 10.97KB
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.55KB 0.62KB
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 (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.72KB 18.35KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 128.51KB 32.94KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 242.15KB 60.89KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 124.40KB 30.26KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 200.75KB 54.24KB
plugin-kanban (index.js) 52.93KB 14.60KB
plugin-list (index.js) 111.74KB 27.18KB
plugin-map (index.js) 20.06KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.49KB 11.93KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.56KB 20.69KB
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) 3.77KB 1.33KB
react (SchemaRenderer.js) 43.66KB 14.77KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.33KB 0.69KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.08KB 1.53KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

PM review — holding at draft, blocked on a maintainer decision

Reviewer of record: the spec@objectui seat (session_01BisYvhR7yZ7ggXAwthTZH5), the seat that dispatched #5328. Verified against GitHub, not against the report's self-description.

The red gate is correctly red, and correctly not fixed here

check:eager-closure fails by 41,689 B (branch 4,001,689 B vs MAX_EAGER_CLOSURE_GZIP_BYTES 3,960,000; origin/main measured at 3,871,997 B with 85.9 KB headroom). Raising the ceiling is gate-strength policy, which is a maintainer decision — not this dev's, and not mine.

The dev's citation checks out. I read #5490 directly: it records the maintainer's ruling of today verbatim as 「其他接受」 = A now + C next, B rejected, with the aggregate ceiling staying as shipped, and states in its own words that "Gate-strength policy is the maintainer's. ⛔ No seat should pick this." Refusing to raise it was the right call, and reporting it rather than quietly widening the budget is exactly the behaviour the gate exists to protect.

Escalated with a four-axis analysis and three costed options: #5531.

What I verified, beyond the report

  • PR shape: draft ✅, base main ✅, Fixes #5328 on the first line ✅, changeset present and scoped to the five affected packages ✅.
  • Governed-surface check (run before any ready/queue consideration, from get_files rather than the report): the 13 changed paths touch no governed surface — no .claude/**, no skills/**, no docs/adr/**, no AGENTS.md / CLAUDE.md. So this PR is on the ordinary path once it is green; it is held only by the gate above.
  • CI right now: 22 check runs, zero failures so far — 9 green, several still in_progress including Bundle Analysis, which is the one that will independently confirm the eager-closure red. No conclusion has been read as green that is merely pending.
  • Serial constraint held: motion is untouched in the lockfile, so PR chore(deps): bump motion from 12.43.0 to 13.1.0 #4958's change was not absorbed — the branch point was correct, as the report claims and the diff confirms.
  • Lockstep: all four declared @objectstack/* moved together, plus the two transitives (core, sdui-parser), with zero @objectstack/*@17.0.0 left in the lockfile — which is what keeps check:spec-symbols green.

On the out-of-scope findings

#5493, #5494 and #5495 were filed rather than ridden in, and each was parked in the gate's own ratcheted ledger so the entry dies when the real fix lands. That is the right shape — a pin bump takes no riders. I have not re-graded them; grading is triage's.

One judgement I specifically agree with: mapping icontains to the builder's containsCaseInsensitive rather than folding it onto contains. Folding would have silently rewritten a user's case-insensitive filter into a case-sensitive one on the next save — a data-visible defect that would have looked like a tidy one-line simplification.

Verdict

Not ACCEPT, not REWORK — ESCALATE. The implementation is sound and I found nothing to send back. It waits on #5531.

⛔ Staying draft. Not flipped to ready, not queued, not auto-merged.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

CI is fully in: 1 failure, and it is the escalated one

All 22 check runs are now completed on head 97c1162a. 18 success · 3 skipped · 1 failure. The single failure is Bundle Analysis — the eager-closure budget already escalated to #5531.

This upgrades the PR body's "everything else is green" from a self-report to a CI-verified fact. Green includes Type Check, Lint, all four Test shards, Build & E2E, Build Docs, Live E2E (informational), and every changeset and doc gate.

CI and the dev's local measurement agree exactly — the same numbers in different units:

CI (KiB) local (B) check
eager closure 3908.4 KB 4,001,689 ×1024 = 4,002,202 ✅
budget 3867.2 KB 3,960,000 ×1024 = 3,960,013 ✅

One reading CI adds that the local run did not surface: the entry chunk is 151.1 KB against a 350 KB budget — comfortably inside. So this is not an entry-point regression; it is the statically-reachable closure across 52 chunks. That matters for the decision on #5531, because it means no amount of entry-point tidying reaches this — only reducing what console's first screen statically pulls in, or accepting the weight.

Why I am not pushing a fix

⛔ The only change that turns this green is raising the ceiling, and that is gate-strength policy — the maintainer's, per the ruling recorded on #5490 today ("Gate-strength policy is the maintainer's. ⛔ No seat should pick this."). This is precisely the case the gate was built for: the regression is larger than the 89 KiB incident that motivated it.

Skipping, disabling, or re-baselining the gate to get green is off the table for this seat under any of the three options on #5531. The PR stays draft until that card is answered.

No re-run requested: this is a deterministic budget computation, not a flake — a second run returns the same bytes.


Generated by Claude Code

claude added 2 commits August 21, 2026 23:46
…1.0 pin

Maintainer ruling on objectui#5531: option A, raise the ceiling
permanently together with the headroom assertion that guards it.

MAX_EAGER_CLOSURE_GZIP_BYTES 3,960,000 -> 4,086,000
BASELINE.gzipBytes           3,881,609 -> 4,005,911  (52 of 508 chunks)
BASELINE.commit              77f846a -> 4c1623c

Both constants move in ONE commit by necessity, not preference: the
guard is `MAX - BASELINE < REGRESSION_THIS_GATE_MUST_CATCH_BYTES`, so
raising MAX alone leaves headroom near 200 KB and fails the gate's own
unit test. That failure is the guard working.

Headroom is 80,089 bytes = 2.00% of the baseline, holding the header's
existing ~2% discipline, and 80,089 < 91,136 with 11,047 to spare. The
substantive property is preserved, not just the inequality: an 89 KiB
repeat of objectui#5266 measured from the new baseline lands at
4,097,047, still over the ceiling, so the gate is exactly as sensitive
as it was.

REGRESSION_THIS_GATE_MUST_CATCH_BYTES is deliberately UNCHANGED. That
is the gate's sensitivity and the ruling did not touch it; a ceiling
that rises while sensitivity relaxes is a gate retiring itself.

The baseline was re-measured on this branch after merging origin/main
(4c1623c), not copied from the earlier 4,001,689 reading — main moved
62 commits in between and carried the closure up by 4,222 bytes of its
own. Header prose updated so the comments and the constants agree.

Refs #5328
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3912.0 KB 3990.2 KB
Main entry chunk (gzip) 151.6 KB 350 KB
Entry file index-Jm9uO8Qa.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 (index.js) 10.04KB 3.72KB
app-shell (runtime-config.js) 11.22KB 3.78KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 29.34KB 7.05KB
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) 6.35KB 2.43KB
auth (index.js) 2.77KB 1.22KB
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.02KB 0.89KB
auth (useIsWorkspaceAdmin.js) 3.04KB 1.45KB
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) 507.00KB 113.72KB
core (index.js) 4.51KB 1.80KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 160.38KB 44.54KB
fields (index.js) 238.85KB 60.13KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 23.13KB 7.63KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.95KB 10.97KB
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.55KB 0.62KB
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 (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.65KB 18.32KB
plugin-chatbot (index.js) 181.41KB 43.22KB
plugin-dashboard (index.js) 128.33KB 32.93KB
plugin-designer (index.js) 212.30KB 42.80KB
plugin-detail (index.js) 242.16KB 60.90KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 125.07KB 30.43KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 200.79KB 54.26KB
plugin-kanban (index.js) 52.93KB 14.60KB
plugin-list (index.js) 111.74KB 27.18KB
plugin-map (index.js) 20.06KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.49KB 11.93KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.54KB 20.69KB
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) 3.77KB 1.33KB
react (SchemaRenderer.js) 43.66KB 14.77KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.33KB 0.69KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.08KB 1.53KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants