Repository navigation
fix(app-shell): the lifecycle badge draws for an authoring caller only - #12227
Merged
objectstack-fleet[bot] merged 3 commits intoOct 12, 2026
Merged
Conversation
ManagedByBadge labels how an object is built (Admin config, System-managed, Platform schema, Read-only audit log, Identity). It is an authoring aid, so it now reads useCanAuthorMetadata (the server's manage_metadata answer) and draws nothing for a caller who cannot author. The gate lives in the badge, so the record page, the list header and the form page follow one rule. An unreported capability set keeps failing open, as the hook's doctrine says. Pinned through the real record page and MePermissionsProvider: an end user sees no badge, an authoring caller sees it, an object with no managedBy or platform draws none, and an unreported answer fails open. Claude-Session: https://claude.ai/code/session_01G5TSaEwbEQsKMjhzTB4rEd Co-authored-by: Claude <noreply@anthropic.com>
…form page The same pair the record page pins, at the two other sites that draw ManagedByBadge: an end user sees no badge, an authoring caller sees it, and an object with no managedBy draws none for either. Both mount the real view under the real MePermissionsProvider. Claude-Session: https://claude.ai/code/session_01G5TSaEwbEQsKMjhzTB4rEd Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5TSaEwbEQsKMjhzTB4rEd Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
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
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-12194-managed-by-badge-authoring
branch
October 12, 2026 11:09
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #12194
Clause-②: no
What changed
ManagedByBadgereadsuseCanAuthorMetadata()and renders nothing for a caller who cannot author metadata. Triage ruled the badge an authoring aid (option a, comment 6120170778). The check lives inside the badge, so the three pages that draw it follow one rule: the record page, the object list header and the record form page. No new prop, export, spec key or i18n key.ManagedByBadgeis not exported from@object-ui/app-shell, and its signature is unchanged. Comments at the three call sites say where the check lives.Effect: an end user opening a
better-authrecord sees no "Identity" badge (zh-CN shows 「身份」), for example on the cloud workspace page (sys_organization). An authoring caller still sees it.Measured before coding (the dispatch's three mechanism assumptions)
useCanAuthorMetadata()returnshasCapabilities(['manage_metadata']).MePermissionsProvider.hasCapabilitiesreturnstrueonly whensystemPermissionsis not an array. The framework'sGET /api/v1/auth/me/permissions(objectstackplugin-hono-server,current-user-endpoints.ts, read at objectstack6a40a09ed) always sendssystemPermissions: Array.from(...)on its success path.systemPermissions(the server itself fails open there); the endpoint's catch branch; and a host that mounts no permission provider.useCanAuthorMetadata: HomePage's builder CTAs, AppHeader's Studio bridge, PageView's edit pencil and the metadata-admin read-only gates. AppHeader and PageView also requireisAdminfor their editor entry points. The badge uses the capability alone, as the ruling names it. No new capability.RecordDetailView, the card's site): drawn in the view's own chrome above the page tree. Edit affordances are already gated by the bucket.ObjectView): every caller still has the toolbar's hidden New/Import/Delete and the read-only buckets' empty state (resolveManagedByEmptyState). Neither reads the badge.RecordFormPage): the dispatch asked whether the badge explains read-only fields to an end user. It does not. That sentence is plugin-form'sClosedAffordanceNotice(objectui#11000), drawn fromclosedFormAffordance, the same verdict that locks the fields. I also corrected the badge-site comment, which said forms disable inputs for every non-platformbucket. That is not true:configandsystem-dataare writable.Tests
The three new files mount the real view under the real
MePermissionsProvider(initialPermissions), so the chain under test is the shipped one: badge, thenuseCanAuthorMetadata, thenusePermissions, then the provider'shasCapabilities. An absence is read only after the page has painted, and the authoring case renders the badge in the same harness.RecordDetailView.managedByBadgeAuthoring-12194.test.tsx: end user sees none; author seesbetter-auth; CONTROL: nomanagedBydraws none for author or end user; CONTROL:platformdraws none for the author; an answer with nosystemPermissionsdraws the badge (the fail-open rule, unchanged).ObjectView.managedByBadgeAuthoring-12194.test.tsxandRecordFormPage.managedByBadgeAuthoring-12194.test.tsx: end user sees none, author sees it, and the no-managedByCONTROL for both callers.Ablation (one-off, no file kept). With the
if (!canAuthor) return null;line replaced by a marker through objectstack'sscripts/ablation-replace.mjs: anchor count 1 to 0, marker 0 to 1, blob8d5e4835a52dto1ce4cbfec118. The tests import the views and the badge relatively fromsrc, so nothing was built. Prediction, written in each file's header before the run: the three end-user cases go RED and everything else stays GREEN. Observed:Tests 3 failed | 8 passed (11), and the three failures were exactly those cases. Restore was proven: blob equals HEAD (8d5e4835a52d) andgit diff HEADis empty.Gates (worktree HEAD
4c026221, unless a line names another sha)pnpm --filter @object-ui/app-shell type-check: exit 0. It ran after building the upstream closure withpnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build(exit 0).tsc -p tsconfig.test.json --listFilesOnlylists all three new test files andManagedByBadge.tsx. A first attempt without the closure built exited 2 onTS2307for workspace imports. That is a missing prerequisite, not a measurement.pnpm --filter @object-ui/app-shell lint(eslint .): exit 0.4c026221. Plus every app-shell and console test file that imports a view that draws the badge, or the shell that mounts them (ObjectView,RecordDetailView,RecordFormPage,ObjectDataPage,InterfaceListPage,AppContent, or@object-ui/app-shell), derived withgit grepover test files: 166 files, all passed. 135 files / 1,135 tests ran at28ef0f23; 31 files / 312 tests ran at4c026221. The only change between those two commits is a typing cleanup in one new test file. The full app-shell suite is left to CI.pnpm check:control-bytesOK ·pnpm check:new-line-citations0 new citation(s)·node scripts/check-changeset-no-major.mjsOK · changeset presence / fixed / overwrite / claims OK ·check:vi-mock-specifiers/-inherit/-override-shapeOK ·check:test-path-rootsOK ·check:pending-changeset-literalsOK · type-check and lint coverage OK ·check:unreferenced-sourcesOK ·node scripts/check-governed-queue-guard.mjs --testwith the 8 paths: NOT GOVERNED.check:eager-closure, reason: it needsapps/console/dist(exit 2,No eager-closure report). The new import (hooks/useCanAuthorMetadata) is already imported byAppHeaderandHomePage, so I expect no new module in the eager closure. Left to CI.Acceptance notes
organization_adminwithholdsmanage_metadata), no longer sees thebetter-authtooltip's line about managing these records through the auth provider's flows. Thebetter-authlist empty states still give that guidance to every caller.utils/managedByEmptyState.tsmirrors the badge's mapping for empty states. It is out of scope and untouched..changeset/12194-managed-by-badge-authoring.md,@object-ui/app-shellpatch.Session:
https://claude.ai/code/session_01G5TSaEwbEQsKMjhzTB4rEdGenerated by Claude Code