Skip to content

finding(app-shell): RecordFormPage publishes a hand-rolled current_user (no id, no isPlatformAdmin) because buildExpressionUser cannot be imported from a lazy view #6515

Description

@os-support-ai

Found while implementing objectui#6493. Filed unassigned, recording only — not graded, no type.

Measured on origin/main @ d09e13fd7.

What was measured

objectui#6110 exported buildExpressionUser from packages/app-shell precisely so that
"every console surface that mounts the provider publishes the SAME current_user shape",
and its export comment says a second mount site re-deriving the shape by hand "would
reintroduce exactly the asymmetry #6010's parity pin exists to refuse".

RecordFormPage is such a second mount site, and it does re-derive it by hand:

// packages/app-shell/src/views/RecordFormPage.tsxconstexpressionUser=useMemo(()=>user
? {name: user.name,email: user.email,role: user.role??'user',positions: (userasany).positions??[]}
: {name: 'Anonymous',email: '',role: 'guest',positions: []},[user],);

Against buildExpressionUser that shape is missing id and isPlatformAdmin.
Both are roots real predicates name — ctx.user.isPlatformAdmin == true gates the
sys_environment "Change Plan (admin)" action, and record.id == ctx.user.id is the
shape sys_user's own action gates use throughout platform-objects. An absent key is
not false here: it makes the predicate FAULT, and a faulting visibility predicate fails
OPEN, so the gate silently does not bite. The signed-out branch diverges too
(buildExpressionUser(null) carries isPlatformAdmin: false and no positions gap).

This descriptor feeds BOTH the page's own ExpressionProvider (so every descendant of
that form sees the narrow user) and — until objectui#6493 — the ad-hoc field-visibility
evaluator beside it. objectui#6493 fixed which ROOTS are bound; it did not touch the SHAPE
of the user object bound under them, and deliberately left this alone rather than widen
its face.

Why it was not just fixed there

buildExpressionUser lives in packages/app-shell/src/console/AppContent.tsx.
RecordFormPage is lazy()-loaded BY AppContent, so a static import of it from the
view would create a source edge from the lazily-split chunk back into the console module
it was split out of — the thing scripts/check-eager-closure-budget.mjs exists to weigh.
So the repair is not a one-line import: the normaliser probably wants to move next to the
ExpressionProvider it feeds (providers/), with AppContent and the package index
re-exporting it so the published name is unchanged. That is a small module move plus a
gate that only measures on a built console — worth its own card rather than a rider.

Related

  • objectui#6110 — exported buildExpressionUser for exactly this reason; InternalFormRoute is the site that uses it correctly.
  • objectui#6493 — bound the roots on the two ad-hoc field-visibility evaluators. AppContent's hand-rolled user was fixed there (it is in the same file, so no module move was needed); this one was not.
  • objectui#5424 — the card that extracted buildExpressionUser so its SHAPE could be asserted on its own.

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