From ba94399c3468bddfd905e3c2f1bb9bfcc5bd6437 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 08:05:29 +0000 Subject: [PATCH] fix(app-shell): object-list "New" and its mobile FAB consume createPredicates (#5153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `createPredicates` had exactly one consumer in objectui — the related-list toolbar (`RelatedRecordActionsBridge`, objectui#4646 / PR #5145). The standalone object-list page renders the same create affordance twice, in the PageHeader and as the phone-only floating "+", and neither read the key: both gated on `affordances.create && can(objectDef.name, 'create')`, the object-level verdict alone. One `userActions.create` object form therefore produced two different answers depending on which surface drew the button, and `visibleWhen: false` (the objectui#3492 shape) did not hide this "New". Both entry points now layer the toolbar-scope predicates on top of that verdict, mirroring the `import` half landed in this same file (#5142 / PR #5154): `visibleWhen` fails CLOSED with `?? true` declared-ness, `disabledWhen` fails SOFT with its `!= null` gate outside the evaluation. The predicate is computed once and consumed by both render points, which is what keeps them from disagreeing. Scope record is `null` — the spec's binding for a standalone list, where a `record.*` predicate has nothing to bind and fails closed. The phone FAB is a bare ` )} - {/* Primary action - always visible */} - {affordances.create && can(objectDef.name, 'create') && ( - @@ -2254,12 +2305,30 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }: an PageHeader's primary create action we just hid. Positioned above the bottom mobile-nav (h-12 + safe-area) so it doesn't collide with it. Hidden on tablets/desktops - because the inline header button is already visible. */} - {affordances.create && can(objectDef.name, 'create') && ( + because the inline header button is already visible. + + [#5153] Second render point of the SAME affordance, so it + consumes the SAME `objectCanCreate && createVisible` / + `createDisabled` pair as the header button — a phone user and a + desktop user must not get different verdicts for one + `userActions.create` declaration. + + DISABLED, on a control with no greyed form of its own: this is + a bare `