Skip to content

finding(app-shell): two ad-hoc field-visibility evaluators bypass ExpressionProvider, so current_user, features and the ADR-0068 aliases are unbound on an object field's visible #6493

Description

@claude

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 siteEvaluatorBag it binds
nav / area items (AppSidebar, UnifiedSidebar, CommandPalette)useExpressionContext().evaluatorcurrent_user, user, ctx.user, os.user, app, data, features
views/RecordFormPage.tsx:183 → used at :204 / :209a local new ExpressionEvaluator(...)user, app, data
console/AppContent.tsx:671 → used at :1090 / :1094a 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 visibleobjectDef.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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions