Skip to content

feat(fields,app-shell): the capability select lists the capability registry; the matrix badge reads the layered envelope (part of objectui#7611) - #12153

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-7611-capability-registry
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-7611-capability-registry

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #7611
Clause-②: no

PR-1 of the objectui#7611 remainder, as the seat's claim amendment on objectui#7611 (comment 6111342697) dispatched it: census item 1 (the capability select), census row 1b (the Capabilities catalog page) and F2 (the permission-set editor's "Custom" badge). objectui#7611 stays open: items 3, 4 and 5 and row 8c are other PRs.

Clause-② is no: no export is added or removed and no exported type changes. CapabilityMultiSelectField keeps its props type; what changes is where its options come from (below), and it no longer reads the dataSource prop.

What changed

Item 1: the capability select lists the capability registry. CapabilityMultiSelectField (@object-ui/fields) reads the capability items of the console metadata store (MetadataCtx ensureType, which is GET /api/v1/meta/capability). That is the reader RecipientPickerField already uses for the position registry; there is no object-name special case. It no longer calls dataSource.find on sys_capability. No second list is merged in; the existing union with the set's own selections stays, so a granted name the registry does not serve is still shown, selected, under "Other". The grouping by scope and the curated client-side labels are unchanged. Without a metadata store the picker offers the set's own selections and loads nothing (no endless "Loading capabilities…" line). The host, PermissionMatrixEditor, no longer passes the adapter to it. The ten locale packs' comments that said the labels come from "the sys_capability registry" now say "the capability registry".

Row 1b: the Capabilities catalog page. Measured in a browser (below): /apps/setup/metadata/capability?scope=environment already renders the capability registry read-only, with no code change. It lists all 11 items, with no "New" button, no active switch, no status filter and no sys_capability read, and an item opens locked with no Save. So the change is documentation only. SETUP_CATALOG_TYPES keeps capability out ON PURPOSE: the constant names the types with an activation switch (hasCatalogActivation reads it), and the activation door refuses a capability. Adding it, the "smallest change" the census named, would put a switch on that page that every click refuses. The ablation below shows that. Its docblock and the app-shell README said the page "still reads sys_capability rows"; they now say what it serves.

The URL for objectstack's stage 8: nav_capabilities should point at /apps/setup/metadata/capability?scope=environment, the same scope-carrying form SystemCatalogRedirect uses for positions and permission sets. objectstack's nav is not changed here.

F2: the provenance badge. The identity strip's badge now also reads codeIsArtifact, the layered-envelope verdict the artifact lock already reads. A set a code package ships reads "Package" beside its "Read-only (this set is provided by a code package)" lock. Before this change it read "Custom", because the registry serves no managedBy and the environment scope passes no packageId. The badge gains data-testid="perm-provenance-badge".

Measured on a fresh showcase

objectstack origin/main 8532783f65 (objectstack dev --seed-admin --fresh, showcase), with this branch's console served by vite against it, signed in as the seeded admin. Before = the two changed source files at the base commit a7d4920 (the A1 mutation below); after = this branch.

  • The registry, by the API. GET /api/v1/meta/capability lists 11 items: the 9 curated PLATFORM_CAPABILITIES (_packageId com.objectstack.plugin-security) and 2 that the showcase package declares (showcase.export_data, showcase.restricted_ops). GET /api/v1/data/sys_capability answers total: 0; the control GET /api/v1/data/sys_user answers rows.
  • Item 1, an environment-authored set that grants setup.access. Before: one chip, "Setup Access", under "Other". The request was GET /api/v1/data/sys_capability?top=500&filter=…active…. After: 11 chips. Platform: Manage Metadata, Manage Platform Settings, Manage Users, Setup Access (on), Studio Access. Organization: Export Showcase Data, Manage Organization Users, Manage Sharing, Restricted Showcase Operations, View All Audit Log, Write Settings. The one capability request was GET /api/v1/meta/capability.
  • Row 1b. The page and the item reading above were the same before and after.
  • F2. showcase_ops and admin_full_access at the environment scope. Before: "Custom" beside the lock. After: "Package" beside the lock. The environment-authored probe set reads "Custom" both times.

Tests

New and updated pins, each red against an ablation, with the restore proven on disk:

  • CapabilityMultiSelectField.test.tsx — a new describe block, "the options are the capability registry (objectui#7611)", with three cases: it lists the registry items, curated and package-declared, and never asks a handed-in data source for rows; it adds a package-declared capability by name; with no store, it offers only the selections and does not sit on the loading line. The file's existing cases and CapabilityMultiSelectField.specParity-6285.test.tsx now feed the registry through a store instead of a mocked sys_capability data source.
  • PermissionMatrixEditor.provenanceBadge-7611.test.tsx (new) — a code-package set reads "Package" beside its lock. Three controls still read "Custom": no code layer, an organization-authored set with a code-looking _packageId, and the sys_metadata sentinel.
  • ResourceListPage.environmentScope-7611.test.tsx — a new describe block for the Capabilities page: every registry item is listed with the scope kept; no create, no status filter and no switch; no request to the ledger or to sys_capability.

Ablations, one per pin, each run from a committed state:

  • A1: CapabilityMultiSelectField.tsx set back to its base blob (git checkout of a7d4920, unstaged; on disk the registry read counts 0 and the row read 1, and the blob equals the base blob). Result: 9 failed, 11 passed, including all three new registry cases. Restored with git checkout HEAD -- on the absolute path; the blob equals HEAD and git diff HEAD is empty.
  • A2: scripts/ablation-replace.mjs deleting || codeIsArtifact from the badge (anchor 1 → 0). Result: 1 failed (Expected "Package", Received "Custom"), and the 3 controls passed. Restored; the blob equals HEAD and git diff HEAD is empty. A first attempt replaced the condition with a substring of itself; the tool refused it before running anything because the replacement count did not rise, so that attempt was a no-op.
  • A3: scripts/ablation-replace.mjs adding 'capability' to SETUP_CATALOG_TYPES. Result: the 1b case failed (the status filter rendered) and the other 11 passed. Restored the same way.

Local verification at 0070a807e

Every run below was on HEAD 0070a807e, the branch's last commit. Builds and tests went through the shared verify lock. The verdict quoted is the one each tool printed.

  • pnpm exec vitest run packages/fields/ — 244 files passed and 1 skipped; 3979 tests passed and 7 skipped.
  • pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ plus the 16 test files outside that directory that name a touched module (found with git grep; console, studio-design, plugin-detail, data-objectstack, schema-catalog and fields) — 488 files passed; 5512 tests passed and 1 skipped.
  • pnpm exec vitest run packages/i18n/ — 82 files passed; 1316 tests passed and 13 skipped.
  • pnpm turbo run build --filter='@object-ui/app-shell^...' — 28 of 28 tasks succeeded. It ran first so that the type checks read freshly built typings.
  • pnpm --filter @object-ui/fields type-check, @object-ui/i18n and @object-ui/app-shell — each exited 0. tsc -p tsconfig.test.json --listFilesOnly lists the new and changed test files in both packages.
  • eslint with the root config and --no-inline-config on the 17 changed TS files (counted from --format json): 0 errors. Type-aware linting is not configured (eslint.config.js sets no parserOptions.project or projectService), so this diff cannot move a verdict on an untouched file. Repo-wide pnpm lint is CI's.
  • Gates, each exit 0: changeset presence and no-major, check:new-line-citations (0 new), check:control-bytes, check:i18n-keys, check:i18n-drift, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:changeset-claims, check:pending-changeset-literals, check:unused-deps and check:phantom-deps. check-governed-queue-guard --test on the 19 paths: NOT GOVERNED.
  • NOT MEASURED: check:readme-exports. Its prerequisite was not met: it needs every package's dist typings, and it refused with "the population COLLAPSED" (36 of 40 packages unbuilt). This PR touches only prose in a README, not a code fence. CI builds and runs it.

Narrowed, and declared as narrowed: the app-shell package's own suite was not run whole. CI runs it.

Acceptance notes

  • On a framework from before objectstack#22669, the registry serves only the package-declared capabilities. There the picker offers the curated ones only where a set already grants them, while that framework still has sys_capability rows. The changeset says so. The objectui#7611 line targets the v18 framework, where the registry serves all of them.

Generated by Claude Code

…gistry; the matrix badge reads the layered envelope (part of objectui#7611)

CapabilityMultiSelectField now lists the console metadata store's
`capability` items (GET /api/v1/meta/capability: the curated platform
capabilities beside every package-declared one), read through
`MetadataCtx` `ensureType` as RecipientPickerField reads the position
registry. It no longer asks a data source for `sys_capability` rows,
which nothing writes any more, so on a fresh database it offered only
the set's own selections. No second source is merged in; the union
with the set's own selections stays so a granted name is never hidden.

PermissionMatrixEditor's identity badge reads "Package" off the same
layered-envelope verdict its artifact gate reads (`codeIsArtifact`), so
a set a code package ships no longer reads "Custom" beside its lock at
the environment scope.

Claude-Session: https://claude.ai/code/session_01Rerax7QTjKMPCUZxQUtPFR
Co-authored-by: Claude <noreply@anthropic.com>
…pe's registry list; pin it read-only with no switch (part of objectui#7611)

Measured on a fresh showcase: `…/metadata/capability?scope=environment`
lists the capability registry (the curated platform capabilities beside
the package-declared ones) read-only, with no create affordance and no
active switch, and reads no `sys_capability` row. No code change is
needed for it. SETUP_CATALOG_TYPES keeps `capability` out on purpose:
it names the types with an activation switch, and the activation door
refuses a capability. Its docblock and the app-shell README said the
page still read `sys_capability` rows; they now say what it serves.

The changeset covers the capability select and the provenance badge.

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 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/11806-login-server-unreachable.md

  • names packages/app-shell/README.md → packages/app-shell/README.md — edited by this change

    packages/app-shell/README.md documents the two new states in its sign-up table. Its useSignUpOffer recipe for hosts now asks decideSignUpOffer only once the read has answered, and answers unreachable for a failed read.

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 291 chunks) 3177.3 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-DlzKeQ-7.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) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
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) 587.77KB 141.53KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.50KB 70.15KB
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) 40.26KB 11.71KB
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 (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.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 250.37KB 66.07KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.80KB 46.04KB
plugin-gantt (index.js) 179.37KB 45.17KB
plugin-grid (index.js) 255.19KB 70.94KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.82KB 31.24KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
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.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
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 (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 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
objectstack-fleet Bot marked this pull request as ready for review October 11, 2026 19:22
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 11, 2026 19:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 53ee2f8 Oct 11, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-7611-capability-registry branch October 11, 2026 19:41
zhuangjianguo pushed a commit that referenced this pull request Oct 11, 2026
Brings main to faf4ce9 (objectui#12151, #12152, #12153, #12154, #12148,
#12149, #12147, #12158, #12159); none of them touches this branch's files.

Claude-Session: https://claude.ai/code/session_01TYgwmFK1q4KJ6Qq2WRLzsD
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants