Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

fix(app-shell): show a position's label in a permission set's Assigned users (objectui#13477) - #13493

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-zcz7ll
Oct 25, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-zcz7ll

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

  • Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)

Fixes #13477

Clause-②: no
Metadata-protocol: none

What changed

AssignedUsersSection (a permission set's Assigned users list) drew each "via position" chip from the holder's sys_user_position.position, the position's machine name, through c.viaPosition(v.position). The same load already lists the registry's position definitions through ensureType('position') to find the positions that distribute the set, and that list carries each definition's label as /meta/position serves it in the caller's language (objectstack#25701).

  • A module-private positionLabels(positions) reads name to label from that one list (a blank or non-string label is skipped).
  • Each position entry of a row's via now carries label beside position.
  • The chip text is c.viaPosition(label ?? name), and the chip's title is the machine name whenever the text is not the name, the same rule CitedItemChip in AccessExplainPanel applies. With no label the chip reads by its name and has no title, as before.
  • No new read: the label comes from the list the section already fetched.
  • .changeset/13477-assigned-users-position-label.md: @object-ui/app-shell patch.

Files: packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx, its existing test AssignedUsersSection.test.tsx (the fixture's contributor definition has the label Contributor, so the chip assertion now reads via position Contributor), the new pin AssignedUsersSection.positionLabel-13477.test.tsx, and the changeset.

Tests

The new pin mounts the real AssignedUsersSection inside the real MetadataProvider, over a real ObjectStackAdapter whose fetch is the only stub, so the label reaches the chip through the store's own GET /api/v1/meta/position:

  • shows the label from /meta/position, with the machine name on title: chip via position Sales Manager, title="sales_manager", and no via position sales_manager.
  • a definition with no label reads by its name, with no title: chip via position field_rep with no title, beside the labelled one.

Readings, taken locally on the branch head:

  • pnpm exec vitest run packages/app-shell/src/views/metadata-admin/AssignedUsersSection: Test Files 5 passed (5), Tests 29 passed (29).
  • Nearby: the same plus PositionHoldersSection and AccessExplainPanel prefixes: Test Files 14 passed (14), Tests 84 passed (84).
  • Reverse check (fix committed, then only AssignedUsersSection.tsx put back to the base commit): both new tests and the updated chip assertion in AssignedUsersSection.test.tsx fail (3 failed, 11 passed across the two files); with the fix restored they pass.
  • pnpm --filter @object-ui/app-shell type-check (both tsc --noEmit and tsc -p tsconfig.test.json): exit 0.
  • pnpm exec eslint on the three touched source/test files: 0 errors, 15 warnings. Each warning is a rule on a line this branch did not write (no-explicit-any, react-hooks/refs, react-hooks/set-state-in-effect, and only-export-components on the existing positionsDistributing export). The new helper stays unexported so it adds none.
  • node scripts/check-changeset-presence.mjs: pass, 1 changeset declared for the 1 released package.
  • pnpm check:new-line-citations: 0 new citations.

Acceptance notes

The sweep looked for any other site that draws a raw position or permission-set machine name from a payload other than the /meta/position or /meta/permission list. The brief allowed a fix in the same diff only where the fix is the same one-line label lookup from a list that component already loads. None of the sites below qualified, so this diff touches none of them.

Already labelled, nothing to do:

  • PositionHoldersSection (a position's holders) lists people only. It draws no position or permission-set name.
  • Approver type: 'position' (the decision_output_defs entry in apps/console/src/services/approvalsApi.ts): the decide dialog's picker is built by registryOptions in decisionOutputParams.ts from ensureType('position'), using label and falling back to the name. Approver display goes through useApproverDirectory, whose registry leg labels position: references from the same store read.
  • AccessExplainPanel Principal card and "Contributed by" chips already label through CitedItemChip.

Raw names that remain, each needing more than a one-line lookup (none of these components loads the list):

  • Sharing-rule sharedWith: the catalog's "Shared with" list column (the render on the sharedWith column in anchors.ts) and the summary row in SharingRuleCloneDialog both print TYPE: VALUE, e.g. position: sales_manager. Neither loads /meta/position, and the recipient type itself is a raw token.
  • PositionGrantCells (the position list's "Permission sets" column) draws _grantedPermissionSets[].name from the list's served listInclude column. The component loads no /meta/permission list.
  • PositionPreview lists the draft definition's permissionSets as code-formatted machine names. It loads no permission list.
  • Suggested bindings (SuggestedBindingsPanel, its Studio copy in StudioDesignSurface, and MarketplacePackagePage toasts) interpolate the suggestion payload's permission_set_name into their sentences. None of them loads /meta/permission.

Locale of the metadata store's reads: MetadataProvider.ensureType reads through adapter.getClient().meta.getItems(type). The console's adapter is built in AdapterProvider with createAuthenticatedFetch(), which sets Accept-Language from document.documentElement.lang on every API call that does not already carry one. ConnectedShellInner keys MetadataProvider on the UI language (key={language}) and clears the adapter cache on a switch, so /meta/position and /meta/permission are read again in the new language. Nothing on this branch re-checks that wiring; it was read from the source and the new pin does not assert the header.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XWGTsmqrtrt1Zqh1ioWqsT

…d users (objectui#13477)

The "via position" chip drew the holder's sys_user_position.position, the
position's machine name, although the section already lists the registry's
position definitions (ensureType('position')) to find the positions that
distribute the set, and /meta/position serves their labels in the caller's
language. The chip now reads the label from that list and keeps the name on
its title, as CitedItemChip in AccessExplainPanel does; a definition with no
label reads by its name.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWGTsmqrtrt1Zqh1ioWqsT
…bjectui#13477)

Nothing outside the section reads it, and a module-level export beside the
component costs a react-refresh warning.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 85 chunks) 3033.7 KB 3496.0 KB
Main entry chunk (gzip) 80.3 KB 350 KB
Entry file index-B6iRDuzz.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 (buildIdentity.js) 0.80KB 0.49KB
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.41KB 7.90KB
app-shell (runtime-config.js) 24.52KB 8.48KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 8.84KB 3.61KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 614.94KB 150.52KB
core (index.js) 10.51KB 4.16KB
create-plugin (index.js) 27.92KB 9.50KB
data-objectstack (index.js) 253.04KB 70.68KB
fields (index.js) 295.90KB 75.88KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.17KB 11.96KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (ServerReadScope.js) 4.03KB 1.82KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.99KB 0.43KB
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.91KB
plugin-calendar (index.js) 55.31KB 16.16KB
plugin-charts (index.js) 88.30KB 24.33KB
plugin-chatbot (core.js) 5.22KB 2.18KB
plugin-chatbot (index.js) 237.93KB 56.26KB
plugin-dashboard (index.js) 147.15KB 39.95KB
plugin-designer (index.js) 232.83KB 50.01KB
plugin-detail (index.js) 268.92KB 71.56KB
plugin-editor (index.js) 2.21KB 1.04KB
plugin-form (index.js) 185.46KB 48.48KB
plugin-gantt (index.js) 178.19KB 45.06KB
plugin-grid (index.js) 277.62KB 77.39KB
plugin-kanban (index.js) 64.27KB 19.81KB
plugin-list (index.js) 149.51KB 39.15KB
plugin-map (index.js) 27.54KB 9.10KB
plugin-markdown (index.js) 13.79KB 4.79KB
plugin-report (index.js) 46.42KB 13.06KB
plugin-timeline (index.js) 39.74KB 12.00KB
plugin-tree (index.js) 15.25KB 5.38KB
plugin-view (index.js) 93.37KB 24.00KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 129.13KB 42.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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 (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · PR #13493 @ 05f61208ac38347635eb937c68949fd9b4779a84 · domain:ui#4 · 2026-10-25T02:56Z

Reviewer of record: session_01XWGTsmqrtrt1Zqh1ioWqsT (seat 4). Implemented-by: claude/project-thread-zcz7ll (in-seat mode:subagent dev). Card objectui#13477, claim 6392364908.

  1. Shape: draft, base main, Fixes #13477. No other open card number sits next to a closing keyword. 4 files: AssignedUsersSection.tsx, its existing test (one assertion), one new pin, one changeset. All are inside the claimed surface.
  2. Premise measured: the holder chip printed c.viaPosition(v.position) with the raw sys_user_position.position, while load already called ensureType('position'). A module-private positionLabels now maps name → label from that same list, skipping blank or non-string labels. Each position entry carries label, and the chip shows label ?? name, with the name on title when the text differs. That is CitedItemChip's rule. No new read. The pin mounts the real component inside the real MetadataProvider over a real ObjectStackAdapter, with only fetch stubbed. The dev's reverse run: 3 red on the base component.
  3. Gates on this head: 33 check-runs success (Lint, Type Check, Test shards 1–16 and the aggregate Test, Build & E2E, Build Docs, Repo Gates, the changeset family, Line Citation Gate, Governed Surface Queue Guard, Bundle Analysis, Test (dist pins), label, Live E2E (informational)) and 3 skipped (coverage jobs, dependabot). mergeable_state: clean.
  4. Accepted deviations: none in the diff. The commits carry AGENTS.md's model-free trailer.
  5. Out of scope: the sweep found raw names that each need more than the one-line lookup, because the component loads no list. They are the sharing-rule "Shared with" column (anchors.ts) and SharingRuleCloneDialog's summary (position: sales_manager), PositionGrantCells (_grantedPermissionSets[].name), PositionPreview's draft permissionSets, and the suggested-bindings sentences (permission_set_name). They are recorded in the PR's Acceptance notes and not filed. The store's /meta/position / /meta/permission reads carry Accept-Language from <html lang> (read from source, not asserted by a test).
  6. Landing: not governed. Changeset: @object-ui/app-shell patch, Clause-② no / Metadata-protocol none. The seat read it against the diff and it matches. The localized label also needs a server on 47af3dde42 or later; the render change itself builds on today's pin. The maintainer's standing landing word was given in this seat's project thread at 2026-10-25T01:48Z, so the seat lands it.

Generated by Claude Code

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

Projects

None yet

1 participant