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
Found while implementing objectui#6493. Filed unassigned, recording only — not graded, no type.
Measured on
origin/main@d09e13fd7.What was measured
objectui#6110 exported
buildExpressionUserfrompackages/app-shellprecisely so that"every console surface that mounts the provider publishes the SAME
current_usershape",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".
RecordFormPageis such a second mount site, and it does re-derive it by hand:Against
buildExpressionUserthat shape is missingidandisPlatformAdmin.Both are roots real predicates name —
ctx.user.isPlatformAdmin == truegates thesys_environment"Change Plan (admin)" action, andrecord.id == ctx.user.idis theshape
sys_user's own action gates use throughoutplatform-objects. An absent key isnot
falsehere: it makes the predicate FAULT, and a faulting visibility predicate failsOPEN, so the gate silently does not bite. The signed-out branch diverges too
(
buildExpressionUser(null)carriesisPlatformAdmin: falseand nopositionsgap).This descriptor feeds BOTH the page's own
ExpressionProvider(so every descendant ofthat 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
buildExpressionUserlives inpackages/app-shell/src/console/AppContent.tsx.RecordFormPageislazy()-loaded BYAppContent, so a static import of it from theview 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.mjsexists to weigh.So the repair is not a one-line import: the normaliser probably wants to move next to the
ExpressionProviderit feeds (providers/), withAppContentand the package indexre-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
buildExpressionUserfor exactly this reason;InternalFormRouteis the site that uses it correctly.AppContent's hand-rolled user was fixed there (it is in the same file, so no module move was needed); this one was not.buildExpressionUserso its SHAPE could be asserted on its own.Generated by Claude Code