Skip to content

fix(plugin-form): object-master-detail-form.fields is not ignored when sections is given — correct the declaration and name every loss - #10000

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-9884-master-detail-fields-ignored
Sep 19, 2026
Merged

os-tesla merged 2 commits into
mainfrom
claude/issue-9884-master-detail-fields-ignored

Conversation

@os-tesla

Copy link
Copy Markdown
Collaborator

Fixes #9884

object-master-detail-form's fields registration declared itself "Ignored when sections is given — sections carry their own field lists". It is not ignored: the two INTERSECT, and a section member outside fields vanished silently — heading and all when it was that section's last one.

The fork: I took (b) — the runtime is right, the declaration was wrong

The card offered (a) make fields genuinely ignored, or (b) correct the sentence, and leaned (a). The tree rules (b), on four readings taken on this branch's base 1ed2e69fa:

  1. One renderer, and this block's parent half IS an object-form. MasterDetailForm's parentSchema memo (MasterDetailForm.tsx:971) literally builds a node whose type is the string object-form, copies sections and fields onto it, and renders it through a directly imported ObjectForm element — no registry lookup. So nothing in the renderer can honour the exemption for this block without honouring it for object-form too, or forking two blocks whose parent schema is byte-identical.
  2. Three of the package's four fields registrations declare the opposite. object-form (index.tsx:232), form (:292) and embeddable-form (:346) all declare fields as "Bare field names to show, in order", with no exemption — and object-form declares sections in the same block (:235). objectFormFieldsMembers-8071 pins object-form.fields on authored order being preserved, i.e. on it being a real selection. Honouring :430 would falsify three declarations to satisfy one.
  3. The TypeScript face already states the truth. MasterDetailFormSchema (MasterDetailForm.tsx:90-92) documents sections and fields as "Parent form sections/fields — passed straight through to ObjectForm". That is true today and false under (a).
  4. The pool is not only the layout. fields builds formFields (ObjectForm.tsx:802, fieldsToShow), which also feeds create defaults, the initialValues merge, gatedFormFields and the values a submit carries. Ignoring it would change what a landed schema writes, not just what it draws.

⚠️ I have to correct one thing the card and the claim both assert. Both read slice 15's ablation leg 4 result — the assertion printing ['ref','memo','memo'] — as "the double render the declaration would produce if it were true", i.e. as a hint that (a) is undesirable. Measured on the base, it is not evidence about fields at all. Two sections naming the same field double-render it today, and a schema writing only sections — exactly the write the declaration encourages — is enough:

probe, master-detail on the base drawn fields
fields: ['ref','memo'] + sections Header:[ref,memo], Notes:[memo] ['ref','memo','memo']
no fields at all + the same two sections ['ref','memo','memo']
fields: ['ref'] + the same two sections (the pinned row) ['ref']

So ['ref','memo','memo'] is the pre-existing consequence of duplicate section membership, which fields: ['ref'] happened to mask in that fixture. It neither supports nor opposes (a). The fork was decided on 1-4 above instead.

What was measured about the blast radius

The triage seat asked for a count before anyone picks a side: how many schemas write both keys. Within reach of this branch — all 635 JSON documents in the tree, walked structurally:

  • 0 nodes carry both sections and fields;
  • 82 form-ish nodes carry fields alone;
  • 1 carries sections alone (examples/schema-catalog/src/schemas/plugin-form/object-form-tabbed-sections.json) — the lit control, so the instrument does find the shape it is looking for.

And every in-repo producer treats them as alternatives: resolveFormViewLayout emits sections and never fields, and useActionModal spells the choice explicitly as (d.fields || d.sections) ? {} : resolveFormViewLayout(...). Radius: in-repo JSON and in-repo producers only; schemas landed in customer trees are outside any instrument I have. The triage escalation condition (at least one real schema losing fields today ⇒ p1) is not met on this population, and that is a bounded reading, not a clean bill.

The re-taken line numbers

  • packages/plugin-form/src/index.tsx:430 — the fields registration description. Unmoved, and verbatim as the claim quotes it.
  • packages/plugin-form/src/ObjectForm.tsx:1473 — the intersection, applyFieldPerms(sourceFields.filter(f => sectionFieldNames.includes(f.name))). The claim's correction stands: the card's :1387 is stale.
  • The pool it filters against is built at ObjectForm.tsx:802: const fieldsToShow = schema.fields || Object.keys(objectSchema.fields || {}). That is the whole mechanism — with no fields, the pool is every declared field and the intersection is a no-op.

What changed

The sentence at :430 now states the intersection, the two consequences, and that each drop is reported.

The silence — the half that was a defect under either reading — is now shut. warnSectionMemberExcludedByFields joins the two warnings already in sectionFields.ts (objectui#3090's mixed-vocabulary lint and objectui#8738's unresolved-member lint), same voice, same once-per-occurrence dedupe. It fires only when a member the object really declares is dropped for the sole reason that fields omits it, and it is measured before applyFieldPerms, so a field removed by per-caller permissions is never reported as an authoring mistake.

Deliberately not recruited: a member the object never declares at all (a typo, or a detail-column borrowed into the parent's vocabulary). That resolves to nothing whether or not fields is authored — a different silence with a different remedy, pinned as behaviour by row 2 of both sections member pins. Row 3d keeps the new warning off it.

No rendered outcome moved. The intersection stands.

The slice-15 pin: before and after

packages/plugin-form/src/__tests__/masterDetailSectionMembers-8071.test.tsx, row 3, titled "fields is NOT ignored when sections is given — the two INTERSECT, and the loser is silent".

Before it asserted, for fields: ['ref'] with sections Header:['ref','memo'] and Notes:['memo']: drawnFields is ['ref'], headings is ['Header']. Its docblock recorded the row as a DIVERGENCE from the block's own registration, handed back as a finding and "not fixed here, because fixing it changes either the renderer or the declaration, and this card writes pins only".

After it asserts those same two DOM outcomes, on the same fixture, unchanged — plus: both losses are named, one warning per (section, member) pair, each naming the section and saying which two keys collided. Row 3b isolates the last-member case on its own fixture and carries a counterfactual leg (drop fields, and the eaten member renders under its heading). Row 3c is the firing control (fields listing every member warns about nothing). Row 3d is the leg that keeps the warning off the other silence.

That is a strengthening, not a relaxation: the row went from 2 assertions to 9, kept both originals byte-for-byte, and the only thing deleted was the phrase "the loser is silent" — deleted because this change made it false. The pin's divergence-from-the-registration framing is replaced by the ruling, in the same commit that makes the registration true.

⭐ Row 3b also found a consequence sharper than the card states: when the vanishing section is the form's only section, the parent half draws no control at all. The grouped branch renders only what the sections resolved, so ref — present in fields, named by no section — never appears either. The row pins that with the counterfactual beside it.

Ablation — the red leg, from the committed fix

Mutation: the diagnostic's guard in ObjectForm.tsx disabled (if (false && ...)), restoring the pre-fix silent behaviour exactly.

On-disk proof, not an exit code:

PRE  anchor=1 mutant=0 hash=42c347ab03a7a046e58b1defefa3ededa2e6e709   (== HEAD blob)
POST anchor=0 mutant=1 hash=23f6b15a2676e852dd4192bab08392b14084178c

Red leg: Tests 2 failed | 5 passed (7) — exactly rows 3 and 3b, and within them exactly the warning assertions (expected +0 to be 2, expected +0 to be 1). Rows 1, 2, 4 and the two firing controls 3c/3d stayed green, and the DOM assertions inside rows 3 and 3b did not move — which is the shape that proves the ablation removed the diagnostic and nothing else.

Restore proven by blob hash and an empty diff, never by an exit code:

RESTORE: blob 42c347ab03a7a046e58b1defefa3ededa2e6e709 == HEAD blob 42c347ab03a7a046e58b1defefa3ededa2e6e709
RESTORE: git diff HEAD -- packages/plugin-form/src/ObjectForm.tsx >>><<<   (empty)

Green leg, re-run from the restored tree: Test Files 1 passed (1) · Tests 7 passed (7).

Checks run on this branch

check result
pnpm exec vitest run packages/plugin-form/ (103 files) 993 passed, 1 skipped — under the shared verify lock, VERDICT command-exit 0
pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts 199 passed — the gate that reads every registration's inputs
pnpm --filter @object-ui/plugin-form type-check exit 0, after building the dependency closure
targeted eslint --no-inline-config on the 4 changed sources exit 0, 0 errors (99 pre-existing no-explicit-any warnings)
check:control-bytes · check:new-line-citations · check:changeset-claims · check:pending-changeset-literals · check:comment-mask-corpus · check:test-path-roots all exit 0
node scripts/check-changeset-presence.mjs exit 0 — 4 published source files, 1 changeset
node scripts/check-governed-queue-guard.mjs --test on the 5 paths NOT GOVERNED
check:sdui-registration-pins NOT MEASURED — exit 2, PREREQUISITE NOT MET (it weighs apps/console/dist, which is not built here). It counts bundle chunks per registration KEY; this change moves a description string only, no key. Declared to CI.

The repo-wide scans (pnpm lint, pnpm test) are CI's run, not this branch's.

Acceptance notes

Outside the declared file face — reported, not ridden. The member-pin ledger in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts carries prose that this change makes stale, in two places: the object-master-detail-form.sections entry (:2443) quotes the old sentence, says the drop happens "with no diagnostic", and records the finding as "not fixed"; a second passage (:3405) repeats the quote. Neither is machine-compared to the registration — memberPinProblem only checks that the pin file exists and names the block and key — so nothing turns red, but the ledger now describes a tree that no longer exists. The declared face for this card is packages/plugin-form/src/** and .changeset/*, so I did not touch it. It wants a follow-up, or an extension of the face.

Observed, not filed. object-form's own fields description (index.tsx:232) says nothing about the interaction with sections — silence, not a false claim, so it is not the defect class this card carries; a sentence there would help authors and belongs to whoever next opens that block. And duplicate section membership double-renders a field (two sections naming it draw two bound controls) — pre-existing, reachable with sections alone, measured in the table above; it is a separate question from this one and no PR here touches it.

🤖 Generated with Claude Code

https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq


Generated by Claude Code

…hen `sections` is given — correct the declaration and name every loss

The registration declared `fields` "Ignored when `sections` is given — sections
carry their own field lists". It is not ignored: `SimpleObjectForm` builds the
parent field pool from `schema.fields` first and each section resolves its own
members against that pool, so authoring both INTERSECTS them. A section member
outside `fields` did not render, and a section that lost every member
disappeared with its heading — silently.

Ruled from the tree: the DECLARATION was the wrong half.

  - One renderer. `MasterDetailForm`'s `parentSchema` memo builds a
    `{ type: 'object-form', ... }` node and renders it through a directly
    imported `<ObjectForm>`, so this block's parent form IS an `object-form`
    and no code can honour the exemption for one without forking the two.
  - Three of the package's four `fields` registrations — `object-form`,
    `form`, `embeddable-form` — declare the key as the field selection with no
    such exemption, and `objectFormFieldsMembers-8071` pins it as one.
    Honouring the exemption would falsify three declarations to satisfy one.
  - The pool is not only the layout: it also feeds create defaults, the
    `initialValues` merge and the values a submit carries, so ignoring
    `fields` would change what a landed schema writes, not just what it draws.

So the intersection stands and the rendered outcome does not move. What moves
is the silence: `warnSectionMemberExcludedByFields` names the section, the
member and the two keys that collided, once per distinct pair, whenever a
member the object really declares is dropped for the sole reason that `fields`
omits it. A member the object never declares is deliberately left out of it —
that member resolves to nothing whether or not `fields` is authored, and it is
pinned as a separate silence by row 2 of both `sections` member pins.

The `object-master-detail-form.sections` pin moves in the same commit: its
sharp row keeps the two DOM assertions objectui#8071 slice 15 wrote and gains
the warning legs, a firing control, and a leg that keeps the warning off the
other silence. Nothing in it was relaxed.

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

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 4 address(es) in this pull request's own prose name a tree it replaced

Each was read from a tree this change itself moves, so a reader who follows it lands somewhere else. ⛔ Nothing here blocks and nothing here says the sentence is false — the question asked is arithmetic: does this diff move the line that number points at?

  • in this body, packages/plugin-form/src/index.tsx:430 — this change rewrites packages/plugin-form/src/index.tsx:430

    • packages/plugin-form/src/index.tsx:430 — the fields registration description.
  • in this body, packages/plugin-form/src/ObjectForm.tsx:1473 — this change moves packages/plugin-form/src/ObjectForm.tsx:1473 to :1517

    • packages/plugin-form/src/ObjectForm.tsx:1473 — the intersection, applyFieldPerms(sourceFields.filter(f => sectionFieldNames.includes(f.name))).
  • in this body, :2443 — this change rewrites apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:2443

    Outside the declared file face — reported, not ridden. The member-pin ledger in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts carries prose that this change makes stale, in two places: the object-master-detail-form.sections entry (:2443) quotes the old sentence, says the drop happens "with no diagnostic", and records the finding as "not fixed"; a second passage (:3405) repeats the quote.

  • in this body, :3405 — this change rewrites apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:3405

    Outside the declared file face — reported, not ridden. The member-pin ledger in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts carries prose that this change makes stale, in two places: the object-master-detail-form.sections entry (:2443) quotes the old sentence, says the drop happens "with no diagnostic", and records the finding as "not fixed"; a second passage (:3405) repeats the quote.

The repair is not to correct the number. Changing :246 to :274 is true today and born false again on the next insertion — objectui#9509 states that before anything else. Bind the number to the tree it was read from (`:246` at `b8a006883d`, `:274` at this head), which cannot re-stale because each number names its own tree; or state a rule instead of a coordinate, the way objectui#9495 replaced a file count with "every file in git diff --name-only against the merge base".

⚠️ 4 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/8067-component-input-member-kind.md

  • names apps/console/src/__tests__/registry-inputs-spec-parity.test.tsapps/console/src/__tests__/registry-inputs-spec-parity.test.ts — edited by this change

    A registration's type: 'array' said a value was a list and stopped there, so a member that drifted from @objectstack/spec was invisible to every layer that reads a declaration. page:header.actions is the measured cost: the contract declares z.array(z.string()) ("Action IDs"), the renderer read the members as ActionDef objects, and the repo-wide parity gate in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts stayed green for the whole life of the drift because both sides carried the key and neither could say what was inside it. What settled it was a maintainer ruling, not a test — and even after the fix, "these are ids" survived only as English in the registration's description.

.changeset/8738-fields-warn-route1.md

  • names sectionFields.tspackages/plugin-form/src/sectionFields.ts — edited by this change

    Top-level fields reads only bare field-name strings ({ name } tolerated) — a different vocabulary from sections[].fields, which also accepts the spec FormFieldSchema object (identity key field, e.g. { field: 'note', colSpan: 2 }). Moving one of those objects into a top-level fields array resolves to no name and used to vanish without a word; it is now reported once per distinct offender via console.warn, naming the skipped shape and the vocabulary difference, modelled on sectionFields.ts's existing warnOnMixedVocabulary.

.changeset/8738-object-form-fields-description.md

  • names ObjectForm.tsxpackages/plugin-form/src/ObjectForm.tsx — edited by this change

    The registration declared { name: 'fields', type: 'array' } with no description, so an author had nowhere to read that this key's members are bare field names — a different vocabulary from sections[].fields, which also accepts the spec FormFieldSchema object (identity key field, e.g. { field: 'note', colSpan: 2 }). Moving one of those objects to the top-level fields resolves to no name and is skipped by SimpleObjectForm (ObjectForm.tsx) and by buildFlatFields (flatFields.ts, shared by the drawer/modal presentations). Behaviour is unchanged by this entry; it only adds the description text an author would need to avoid the drop before writing it.

.changeset/console-formpage-visible-predicates-5594.md

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 1ed2e69fa (merge-base with origin/main): 5 file(s) changed outside .changeset/, read against 1205 pending declaration(s) that publish a body (1764 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3055.0 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DpOvj8UR.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.92KB 130.72KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 216.90KB 60.15KB
fields (index.js) 249.62KB 63.02KB
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.73KB 20.08KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 254.30KB 66.21KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 138.95KB 35.18KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 213.44KB 58.21KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 113.42KB 27.95KB
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) 109.04KB 36.08KB
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 (body-dialect.js) 4.38KB 1.98KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.74KB 2.54KB
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) 15.71KB 5.30KB
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

… not the divergence it replaced

The `object-master-detail-form.sections` entry and the slice-15 narrative both
described a tree that no longer exists. Three claims moved, and only those:

  - the quoted registration sentence — `fields` was declared "Ignored when
    `sections` is given"; the registration now states the intersection;
  - "with no diagnostic" — there is one, `warnSectionMemberExcludedByFields`,
    which names every drop the intersection causes;
  - "not fixed" / "handed back as a finding" — objectui#9884 ruled it, taking
    the DECLARATION as the wrong half, and the entry now says why: one
    `SimpleObjectForm` renders this block's parent half and `object-form`
    alike, and the three sibling `fields` registrations declare the key as the
    field selection with no such exemption.

Prose only. The ledger is not machine-compared to the registration —
`memberPinProblem` checks that a pin file exists and names its block and key —
so nothing here was red; it was simply saying the opposite of what the pin it
indexes now asserts, which is the one thing a registry of pins must not do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions github-actions Bot added the apps label Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3055.0 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DpOvj8UR.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.92KB 130.72KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 216.90KB 60.15KB
fields (index.js) 249.62KB 63.02KB
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.73KB 20.08KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 254.30KB 66.21KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 138.95KB 35.18KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 213.44KB 58.21KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 113.42KB 27.95KB
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) 109.04KB 36.08KB
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 (body-dialect.js) 4.38KB 1.98KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.74KB 2.54KB
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) 15.71KB 5.30KB
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

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

Projects

None yet

2 participants