Skip to content

fix(fields): PeoplePicker rows, its tray and the user cell draw a person's name and avatar from the fields the viewer may read (objectui#10535) - #10624

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10535-person-name-read-gate
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10535-person-name-read-gate

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10535
Clause-②: no

What changes

Head 78005aa7e, base 21d34d5e2. Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC (domain:ui seat 1, claim 5831720599).

The renderer-side FLS rulings objectui#7215 / objectui#7230 say "FLS gates the OUTPUT". objectui#10433 gated the subtitle and the avatar a person row and the tray draw. Two outputs of the same family were still drawn straight off the served row, and this PR gates both.

  • PeoplePicker's person name, in the row and the tray. getPersonName reads the name down a ladder: the display field, then name, username, label. Since objectui#10433, PeoplePicker gates each subtitle path and the avatar field with !perms.isLoaded || perms.checkField(objectName, f.split('.')[0], 'read'), with perms in the memo deps.
    • The same gate now filters the name ladder (readableNameFields). PersonRow and SelectionTray read the name from that ladder, through a new nameFields prop.
    • The ladder is spelled once, as getPersonNameFields in personDisplay.ts. getPersonName also accepts an already-filtered ladder, and its existing single-field calls are unchanged.
    • The records stay as served, so onSelect and onSelectRecords receive the same values as before. Nothing caches a resolved name: the name is derived on every render, so a policy that arrives later relabels the rows and chips already on screen.
    • The name also feeds the avatar alt, the initials and the chip's remove label, so those follow the gate.
  • UserCellRenderer, single and multi. Each person row goes through the module-private withoutDeniedFields(record, policy, objectName) that PR objectui#10592 added to the same file. The filter runs before anything is drawn: the empty-row check, the name (name, then username, then User), the initials, and the image src. usePermissions() is called first, before any early return.
    • The person's object is reference_to, then reference, then sys_user.
    • I checked how PeoplePicker resolves it. Its objectName is LookupField's referenceTo (reference_to or reference), read from the meta that UserField normalises to reference || reference_to || 'sys_user'. That resolves to the same object. LookupCellRenderer in the same file reads the same two spellings.

The denied-name fallback (the triage's condition)

I checked it in LookupField.tsx recordToOption (objectui#10411). The option label is built from withoutDeniedFields(record, readable), and the existing label chain falls through "exactly as it does for that row", meaning the row ObjectStack's FieldMasker serves. The lookup cell's name ladder from objectui#10501 uses the same wording. That fallback works by skipping each denied rung, so the label falls to the next readable one. It does not add a new rung.

The person surfaces now follow the same fallback. A denied rung is skipped and the next readable one shows. When nothing is readable, each surface shows the floor it already had: the row and the tray show — with ? initials, and the cell shows User. Each surface draws exactly what it already draws today for a stripping backend's row, and the pins assert that byte for byte (gated equals stripped).

⚠️ Stated boundary: the lookup chain's own last rung is the id, while the person ladders end in a placeholder. This PR adds no rung and moves no floor. The report on the card raises this for the seat.

No new export, and no new copy of the row filter (objectui#10594)

  • No new public export. The package entry does not re-export personDisplay.ts, PersonRow, SelectionTray or PeoplePicker: its export * list names none of them, and only LookupField.tsx imports PeoplePicker. getPersonNameFields is package-internal.
  • This PR adds zero copies of the row filter. The user cell reuses the copy already in index.tsx. PeoplePicker's name gate is a field-LIST filter, the shape objectui#10594 puts out of scope. git grep -c -E "function (withoutDeniedFields|readableRow|fieldReadGate)" over packages/*/src (tests excluded) counts 7 at the base and 7 at the head.

Pins (real PermissionProvider, backends that do not strip)

packages/fields/src/widgets/PeoplePicker.nameFls-10535.test.tsx, on sys_user:

  • a denied name is not drawn in the row or the tray, and the next readable source stands in; gated equals stripped;
  • a row whose name is denied can still be chosen;
  • the configured display field is the rung judged first, not a literal name;
  • with every name source denied, the row and the tray read as for a stripping backend (html equal);
  • a policy that changes after mount relabels the same mounted row and chip (node identity asserted);
  • controls: a name the policy does not deny still prints; with no provider mounted, the row is named as served.

packages/fields/src/__tests__/userCell.readGate-10535.test.tsx:

  • single and multi: a denied name and a denied image are not drawn; gated html equals stripped html;
  • the object comes from reference_to / reference, and a sys_user policy says nothing about a field that points elsewhere (the default sys_user leg is the single and multi tests, whose field names no object);
  • a policy that changes after mount relabels the same mounted cell;
  • controls: fields the policy does not deny still print, single and multi; with no provider mounted, the row is drawn as served.

LoadedImage reports every image as loaded (the objectui#10433 technique), so an img renders exactly when a URL reaches the Avatar. The controls assert that img.

Verification

  • Reproduced before the fix, with the pins written first and the sources at base: Tests 8 failed | 5 passed (13). The row drew alt="Amy Lin" src="http://x/amy.png" and the text Amy Lin. The cell drew title="Amy Lin" and src="http://x/amy.png".
  • Whole @object-ui/fields suite at 78005aa7e, run as pnpm exec vitest run --maxWorkers=2 packages/fields/ from the repo root: Test Files 205 passed | 1 skipped (206), Tests 3360 passed | 7 skipped (3367).
  • Ablation at 6afa54c6e (the fix commit; later commits change only the two pin files' typing). The ablation removed both gates through ablation-replace.mjs, which checks every leg on disk:
    • PeoplePicker.tsx: the readableNameFields filter became the unfiltered ladder. Anchor count went 1 to 0, and the blob went 1b3a98f4515a to cf58c6203ebd.
    • index.tsx: both withoutDeniedFields(..., perms, personObject) calls were handed a not-loaded policy. Anchor count went 2 to 0, and the blob went 6fdf855a20d7 to ec6d0e5f19b0.
    • Result: Tests 8 failed | 5 passed (13). The 8 red tests are every denial and relabel pin. The 5 green are the 4 controls plus "can still be chosen".
    • Both files were restored by blob (blob after restore == blob at HEAD, git diff HEAD empty), and git status --porcelain was empty afterwards.
  • Type-check: built the closure with pnpm --workspace-concurrency=2 --filter '@object-ui/fields^...' build (exit 0). Then pnpm --filter @object-ui/fields type-check exited 0 (tsc --noEmit && tsc -p tsconfig.test.json), and tsc -p tsconfig.test.json re-ran clean on the head's bytes. --listFiles shows both new pin files in the test program.
  • eslint (--no-inline-config, per-rule counts, head vs base) over the 5 edited sources: identical on every file. react-hooks/exhaustive-deps is 0 at both. The two new pin files have 0 messages.
  • Gates at 78005aa7e: pnpm check:control-bytes ✅, pnpm check:new-line-citations (0 new), node scripts/check-changeset-presence.mjs ✅ (1 changeset), pnpm check:changeset-claims exit 0 (see Acceptance notes). Also green: check:pending-changeset-literals, check:test-path-roots, check:vi-mock-specifiers, check:unreferenced-sources, check:shell-escape-residue, check:i18n-keys, check:handler-key-reads.
  • Changeset: .changeset/10535-person-name-read-gate.md, @object-ui/fields patch.

Acceptance notes

  • One pending changeset sentence went false with this PR, and this PR corrects it (the seat's ruling on the dev's question, head 16d126a58). .changeset/7166-retire-inert-fieldmeta-copies.md said: "a user column resolves to UserCellRenderer, which destructures { value } and reads no field meta at all." After this PR, the renderer destructures { value, field } and reads reference_to / reference.
    • The fix is an insert-only in-release note ("Superseded in this release by objectui#10535: …"), with frontmatter byte-identical and zero lines deleted (check-changeset-overwrite case 2). The paragraph's point still holds: none of the three keys it retires has a reader on the cell path.
    • 6625-retire-fieldmeta-decimals, 6694-dashboard-lookup-reference-meta and 6837-reference-to-arm-deletion stay true.
  • The claim placed PersonRow / SelectionTray in PeoplePicker.tsx. They live in PersonRow.tsx / SelectionTray.tsx, and this PR touches them for the name path only.
  • personDisplay.ts's module header says its helpers are shared by "the read-only user cell renderer". UserCellRenderer imports none of them; its own ladder is name || username || 'User'. That line predates this PR and I left it as is.
  • The user cell reads two spellings. reference is the spec's spelling and the key a grid column forwards. UserFieldMetadata in the objectui types declares neither spelling, so the pin casts. These are the same two spellings LookupCellRenderer reads, and objectui#6837 left those FieldMetadata readers alone.
  • A policy that loads after mount: MePermissionsProvider renders its loadingFallback until it has loaded, so in the console these surfaces mount under a policy that has already loaded. The pins follow the objectui#10501 precedent, a policy that changes after mount (from open to denying) on the same mounted nodes.
  • Docs: no guide or README describes the person display's FLS behaviour, so none is updated.
  • Defence in depth, as for objectui#10433: ObjectStack's FieldMasker already strips denied fields server-side, and on its rows nothing changes.

Generated by Claude Code

…son's name and avatar from the fields the viewer may read

PeoplePicker read the person name down its ladder (display field, then
name, username, label) straight off the served row, so a name the loaded
policy denies on the queried object printed in the row, the tray chip,
the avatar alt and the chip's remove label. The read-only user cell drew
name and image of an expanded person the same way, single and multi.

- personDisplay: getPersonNameFields names the ladder; getPersonName
  also takes the ladder, already filtered.
- PeoplePicker: readableNameFields filters that ladder with the same
  gate the subtitle and avatar use; PersonRow and SelectionTray take it
  as nameFields. Records stay as served; the name is derived per render.
- UserCellRenderer: reads each person through the module's existing
  withoutDeniedFields on reference_to / reference / sys_user, with
  usePermissions called before any early return.

The denied-name fallback is the lookup option label's: skip the denied
rung, fall through to the next readable one, as for a stripping
backend's row.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
…known in the 10535 pin

The objectui types declare only `reference_to` on a user field; the pin
also reads the spec's `reference`, the key a grid column forwards.

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

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

.changeset/6625-retire-fieldmeta-decimals.md

  • names fields/src/index.tsx → packages/fields/src/index.tsx — edited by this change

    buildFieldMeta computed decimals: overrides.decimals ?? meta?.decimals ?? meta?.scale on every call and the value reached nothing. Re-measured on this branch's base (efdc6c62): zero .decimals member reads across @object-ui/fields, @object-ui/i18n, @object-ui/components, @object-ui/core and plugin-dashboard itself — the only non-comment occurrence was the write being removed here. The positive control in the same query shape fires: .scale member reads hit NumberField.tsx, GridField.tsx and fields/src/index.tsx. So the zero is a finding, not a broken query. The overrides.decimals ?? head of that chain had already lost its only feeder when objectui#6425's ruling removed the authored read from ObjectDataTable.enrich(); RecordDetailDrawer, the only other buildFieldMeta caller, passes no overrides at all. Both halves retire together, so the key leaves in one move.

.changeset/6694-dashboard-lookup-reference-meta.md

  • names packages/fields/src/index.tsx → packages/fields/src/index.tsx — edited by this change

    ⚠️ The copy set is three keys where ObjectGrid's RELATIONAL_META_KEYS is nine, and the difference is measured per key, not preferred. The grid's cells are EDITABLE, so its extra keys drive the inline picker's query (LookupField / UserField read id_field, description_field, lookup_filters, lookupFilters); these two widgets are read-only and their render path ends at a cell renderer. packages/fields/src/index.tsx reads exactly reference_to, reference and display_field off a cell's field prop; titleFormat is never read off a field meta at all (its readers take it off the object schema, which arrives here through useRefObjectSchema(reference_to)), and reference_to_field has zero member reads anywhere in the repo. Copying the other six would mint six members written on every call and read by nothing — precisely what objectui#6625 (decimals) and objectui#6597 (referenceTo) retired from this same file.

.changeset/6837-reference-to-arm-deletion.md

  • names fields/src/index.tsx → packages/fields/src/index.tsx — edited by this change

    Three readers were deliberately left alone. LookupCellRenderer (fields/src/index.tsx), LookupField and UserField read FieldMetadata — ObjectUI's OWN contract, whose LookupFieldMetadata declares reference_to and never declares reference. They are fed by the emitters above and by published example schemas (examples/schema-catalog/src/schemas/fields-lookup/*.json), so narrowing them would break in-repo producers, and plugin-grid's relationalMetaCopySet.derivation.test.ts re-derives its read set from exactly those three sources — where reference_to is recorded with verdict adapter-stamped. DetailViewFieldSchema is likewise untouched.

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3048.4 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-_mwFXR7K.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.57KB 6.15KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
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.39KB 1.21KB
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.52KB 3.45KB
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) 546.99KB 130.83KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.50KB 65.80KB
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 (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.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.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) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.60KB 28.31KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
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) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
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) 18.27KB 6.20KB
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) 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) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
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

…CellRenderer sentence

The pending 7166 entry says `UserCellRenderer` destructures `{ value }`
and reads no field meta. After objectui#10535 it destructures
`{ value, field }` and reads `reference_to` / `reference` to name the
person's object; it still reads none of the three retired keys. An
insert-only in-release note, frontmatter byte-identical.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3048.4 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-_mwFXR7K.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.57KB 6.15KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
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.39KB 1.21KB
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.52KB 3.45KB
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) 546.99KB 130.83KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.50KB 65.80KB
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 (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.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.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) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.60KB 28.31KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
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) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
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) 18.27KB 6.20KB
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) 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) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
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 September 25, 2026 12:45
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 6516320 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10535-person-name-read-gate branch September 25, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant