refactor(managedBy): consolidate lifecycle-bucket logic into one shared source of truth#2712
Merged
Merged
Conversation
…ed source of truth
The managedBy bucket taxonomy was hand-mirrored across crudAffordances.ts,
ManagedByBadge.tsx (own Bucket union + isWriteOptedIn + writable-system
derivation), and plugin-detail record-details.tsx (NON_EDITABLE_BUCKETS,
duplicated because it can't depend on app-shell) — a drift risk. The schema
managedBy type was also open-ended ((string & {})), so unknown buckets slipped
through and silently defaulted to fully-editable.
- @object-ui/types owns the closed ManagedByBucket union (+ MANAGED_BY_BUCKETS);
ObjectSchema.managedBy is tightened to it (unknown buckets now a type error).
- @object-ui/core owns the React-free runtime logic (resolveCrudAffordances,
isWriteOptedIn, isSystemWritable, isObjectInlineEditable), reachable by every
UI package including plugin-detail.
- app-shell crudAffordances.ts is now a re-export shim; ManagedByBadge uses the
shared isSystemWritable; record-details uses isObjectInlineEditable in place of
the hand-mirrored NON_EDITABLE_BUCKETS.
Behavior-preserving: full-graph build typechecks; existing affordance/edit-gate
tests stay green; new core unit tests cover the logic incl. the previously
untested isSystemWritable. Follows framework ADR-0103.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fp9yZxRQ3mb7p4vVwqFXKE
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 20, 2026 02:32
os-zhuang
added a commit
that referenced
this pull request
Jul 20, 2026
…ompletes #2712, framework#3343) (#2724) #2712 consolidated the bucket union + affordance set mirrors but left four surfaces still parsing the `userActions.{create,edit,delete}` override shape by hand. They now all route through the shared @object-ui/core policy — no package re-implements the boolean / #2614-object-form parse locally. - core: promote internal `normalizeOverride` to exported `normalizeUserAction` (the one parser) + add `userActionPredicates` for per-record predicate extraction. - app-shell/managedByEmptyState: writable-`system` create check + local `EmptyStateUserActions` → `resolveCrudAffordances({ managedBy, userActions }).create`. - plugin-grid/rowCrudAffordances: local `isOptedOut`/`predicatesOf` + duplicated types fold into `normalizeUserAction`; historical type names stay re-exported. - plugin-detail/RelatedList: inline `predicatesOf` → `userActionPredicates`. - plugin-form/ObjectForm: hand-rolled `managedBy !== 'platform'` blanket lock + userActions unlock → resolved affordance for the mode (edit/create), the same `resolveCrudAffordances` contract detail (`isObjectInlineEditable`) and grid use. Behavior-preserving for platform/system/append-only/better-auth, with one deliberate alignment: an admin-editable `config` object (sys_webhook, sys_permission_set) is now editable in ObjectForm — it was over-locked as non-`platform` while detail/grid already treated it as editable (config resolves edit:true). New unit coverage for the shared parser + the config/create-mode form gate; all existing affordance/edit-gate tests stay green. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
Follows framework ADR-0103 (the
managedBy: 'system'taxonomy split) and the Console copy work in #2705. Eliminates the hand-mirrored bucket logic that ADR-0103's whole design had to work around.Problem
The object-lifecycle
managedBybucket taxonomy was hand-mirrored in several places, kept "in lockstep" by comments:app-shell/utils/crudAffordances.ts— the canonicalManagedByBucketunion +DEFAULTSmatrix +resolveCrudAffordances.app-shell/components/ManagedByBadge.tsx— its ownBucketunion,isWriteOptedIn, and the ADR-0103 writable-system derivation.plugin-detail/renderers/record-details.tsx— its ownNON_EDITABLE_BUCKETSset (a comment explains it's duplicated because plugin-detail can't depend on app-shell).And the schema type was open-ended —
managedBy?: 'platform' | 'better-auth' | (string & {})— soconfig/system/append-onlyslipped through the escape hatch and an unknown value silently defaulted to fully-editableplatform.Change (behavior-preserving)
Single source of truth, split by the dependency graph (plugin-detail can only reach
@object-ui/typesand@object-ui/core, both React-free):@object-ui/typesowns the closedManagedByBucketunion +MANAGED_BY_BUCKETS, andObjectSchema.managedByis tightened to it — unknown buckets are now a compile error, not a silent fallthrough.@object-ui/coreowns the runtime logic:resolveCrudAffordances,isWriteOptedIn,isSystemWritable,isObjectInlineEditable.app-shell/utils/crudAffordances.tsbecomes a thin re-export of@object-ui/core(existing./crudAffordancesimports keep working);ManagedByBadgeconsumes the sharedisSystemWritable;record-details.tsxreplaces the hand-mirroredNON_EDITABLE_BUCKETSwithisObjectInlineEditable(which is exactly the resolvededitaffordance — verified equivalent bucket-by-bucket).Translated copy (badge variants, empty-state messages) stays in app-shell — only the booleans and the union move down.
Verification
resolveCrudAffordances/rowCrudAffordances/ObjectForm.managedEdit/managedByEmptyStateexisting suites stay green (37 tests through the re-export shim);plugin-detailsuite 237 pass.@object-ui/coremanagedBy.test.tsadds direct coverage, including the previously-untestedisSystemWritablederivation.Scope notes
managedBystrings in the repo (package/admin/user) belong to the unrelated permission-set provenance vocabulary, which is not typed againstObjectSchema.managedBy.🤖 Generated with Claude Code
Generated by Claude Code