Found while implementing objectui#6487 (the per-tier advice paragraph on reportUnresolvableVisibilityPredicate). Filed unassigned, recording only — not graded, no domain:*, no type.
Measured on origin/main @ 830ed5803.
What was measured
evaluateVisibility (packages/app-shell/src/providers/ExpressionProvider.tsx) is reached with three different evaluators, not one. Only the first is the provider's:
| Call site | Evaluator | Bag it binds |
|---|
nav / area items (AppSidebar, UnifiedSidebar, CommandPalette) | useExpressionContext().evaluator | current_user, user, ctx.user, os.user, app, data, features |
views/RecordFormPage.tsx:183 → used at :204 / :209 | a local new ExpressionEvaluator(...) | user, app, data |
console/AppContent.tsx:671 → used at :1090 / :1094 | a local new ExpressionEvaluator(...) | user, app, data (here data is editingRecord) |
// packages/app-shell/src/views/RecordFormPage.tsx:183constexpressionEvaluator=useMemo(()=>newExpressionEvaluator({user: expressionUser,app: {name: appName},data: {}}),[expressionUser,appName],);Both ad-hoc bags are missing current_user and the ADR-0068 ctx.user / os.user aliases, and both are missing features. The gate they feed is an object field's visible — objectDef.fields[].visible — so a field gate authored as 'sales_manager' in current_user.positions names an unbound root there, faults, and visible fails OPEN: the field is shown to everyone, which is the same defect shape objectui#6010 and objectui#6110 each fixed one surface at a time.
RecordFormPage's own comment says the evaluator mirrors "the global ModalForm setup in AppContent" — the two ad-hoc bags are consistent with each other and inconsistent with the provider they sit beneath. RecordFormPage in fact mountsExpressionProvider at :270 with the full bag; the evaluator at :183 is built beside it and does not read it.
Why it is worth recording now
objectui#6487 made the reporter's advice paragraph per tier, and the app-shell tier's paragraph names the roots ExpressionProvider binds — current_user with its three alias spellings, app, features. That copy is correct for the provider's evaluator and over-promises at these two sites: an author whose object-field visible faulted there is told about roots their evaluator does not carry. The right repair is at the producer (bind what the tier declares), not at the copy — ADR-0068 D1 already says a predicate authored against any one form must evaluate identically, and objectui#6110's changeset says every console surface should publish the same shape via buildExpressionUser.
Why it was not fixed in objectui#6487
That card is diagnostics copy. Binding current_user here is a behaviour change, not a wiring tidy-up: objectui#6110's changeset documents exactly what happens when an inert current_user gate becomes live — fields that have always been visible start disappearing for the users the rule excludes — and it asks for a pre-upgrade metadata audit. That needs its own card and its own changeset warning.
Related, and NOT a duplicate
- objectui#6262 —
/forms/:name mounts ExpressionProvider with user only, so features is {} there. Same symptom class, different mechanism: that route under-feeds the provider; these two sites never consult it. - objectui#6110 — bound the host scope for
apps/console's form renderer and WizardForm's submit gate. Neither of these two evaluators was in its file face. - objectui#6010 — the
packages/components form-renderer half.
Not yet measured
Whether any shipped object metadata authors a current_user or features gate on a field's visible (as opposed to a form view's visibleWhen). If nothing does, this is latent rather than live — and, as objectui#6262 notes for its own gap, the honest fix might instead be to decide these evaluators may not see those roots at all, which is a contract question.
Generated by Claude Code
Found while implementing objectui#6487 (the per-tier advice paragraph on
reportUnresolvableVisibilityPredicate). Filed unassigned, recording only — not graded, nodomain:*, no type.Measured on
origin/main@830ed5803.What was measured
evaluateVisibility(packages/app-shell/src/providers/ExpressionProvider.tsx) is reached with three different evaluators, not one. Only the first is the provider's:AppSidebar,UnifiedSidebar,CommandPalette)useExpressionContext().evaluatorcurrent_user,user,ctx.user,os.user,app,data,featuresviews/RecordFormPage.tsx:183→ used at:204/:209new ExpressionEvaluator(...)user,app,dataconsole/AppContent.tsx:671→ used at:1090/:1094new ExpressionEvaluator(...)user,app,data(heredataiseditingRecord)Both ad-hoc bags are missing
current_userand the ADR-0068ctx.user/os.useraliases, and both are missingfeatures. The gate they feed is an object field'svisible—objectDef.fields[].visible— so a field gate authored as'sales_manager' in current_user.positionsnames an unbound root there, faults, andvisiblefails OPEN: the field is shown to everyone, which is the same defect shape objectui#6010 and objectui#6110 each fixed one surface at a time.RecordFormPage's own comment says the evaluator mirrors "the global ModalForm setup in AppContent" — the two ad-hoc bags are consistent with each other and inconsistent with the provider they sit beneath.RecordFormPagein fact mountsExpressionProviderat:270with the full bag; the evaluator at:183is built beside it and does not read it.Why it is worth recording now
objectui#6487 made the reporter's advice paragraph per tier, and the app-shell tier's paragraph names the roots
ExpressionProviderbinds —current_userwith its three alias spellings,app,features. That copy is correct for the provider's evaluator and over-promises at these two sites: an author whose object-fieldvisiblefaulted there is told about roots their evaluator does not carry. The right repair is at the producer (bind what the tier declares), not at the copy — ADR-0068 D1 already says a predicate authored against any one form must evaluate identically, and objectui#6110's changeset says every console surface should publish the same shape viabuildExpressionUser.Why it was not fixed in objectui#6487
That card is diagnostics copy. Binding
current_userhere is a behaviour change, not a wiring tidy-up: objectui#6110's changeset documents exactly what happens when an inertcurrent_usergate becomes live — fields that have always been visible start disappearing for the users the rule excludes — and it asks for a pre-upgrade metadata audit. That needs its own card and its own changeset warning.Related, and NOT a duplicate
/forms/:namemountsExpressionProviderwithuseronly, sofeaturesis{}there. Same symptom class, different mechanism: that route under-feeds the provider; these two sites never consult it.apps/console's form renderer andWizardForm's submit gate. Neither of these two evaluators was in its file face.packages/componentsform-renderer half.Not yet measured
Whether any shipped object metadata authors a
current_userorfeaturesgate on a field'svisible(as opposed to a form view'svisibleWhen). If nothing does, this is latent rather than live — and, as objectui#6262 notes for its own gap, the honest fix might instead be to decide these evaluators may not see those roots at all, which is a contract question.Generated by Claude Code