Found while implementing objectui#6110 (PR #6261). Recording only — not graded, no domain:*, unassigned.
Measured on objectuiorigin/main @ cbc883960 plus PR #6261's branch.
What #6110 changed, and the half it deliberately left
#6110 found that the authed internal form route /forms/:name had noExpressionProvider above it at all — the console mounts one only in AppContent (the /apps/:appName/* subtree) and in app-shell's RecordFormPage. Its fix mounts the provider in InternalFormRoute with the session principal:
<ExpressionProvideruser={expressionUser}>ExpressionProvider publishes four scope roots — user (as current_user + the ADR-0068 user / ctx.user / os.user aliases), app, data and features. #6110 supplies only user, so on /forms/:namefeatures is {}.
Why that is a real asymmetry rather than a default
AppContent fills features from /api/v1/auth/config (getAuthConfig()), and the flags are already load-bearing for predicate evaluation elsewhere — ExpressionProvider's own docstring names sys_organization's create button being hidden when multiOrgEnabled === false. So an authored predicate naming features.multiOrgEnabled:
- inside
/apps/:appName/* — resolves against the real flags; - on
/forms/:name — names an unbound key, which faults, and visibleWhen fails OPEN, so the gated field/section is shown to everyone.
That is the same shape as #6110's own defect, one scope root over: one authored text, two verdicts depending on which route opened the form.
app is likewise {} there, though that one is arguably correct by construction — /forms/:name names no app, which is exactly why InternalFormRoute renders HomeLayout rather than ConsoleLayout (see that file's comment and objectui#4109).
Why it was not fixed in #6110
Out of that card's scope by its own words (it names current_user), and it is not a one-line addition: features requires an /auth/config fetch on a route that currently makes none, plus a decision about what a predicate should see before that fetch resolves. ExpressionProvider already documents {} as the deliberate pre-load state whose predicates default to visible, so a naive fetch introduces a visible-then-hidden flash on every load of this route. PR #6261 records the gap in FormPage.tsx's own comment rather than papering over it.
Not yet measured
Whether any shipped form-view metadata actually authors a features.* predicate. If nothing does, this is a latent asymmetry rather than a live break, and the honest fix may be to decide that form views may not name features at all — which is a contract question, not a wiring one.
Related
Found while implementing objectui#6110 (PR #6261). Recording only — not graded, no
domain:*, unassigned.Measured on
objectuiorigin/main@cbc883960plus PR #6261's branch.What #6110 changed, and the half it deliberately left
#6110 found that the authed internal form route
/forms/:namehad noExpressionProviderabove it at all — the console mounts one only inAppContent(the/apps/:appName/*subtree) and in app-shell'sRecordFormPage. Its fix mounts the provider inInternalFormRoutewith the session principal:ExpressionProviderpublishes four scope roots —user(ascurrent_user+ the ADR-0068user/ctx.user/os.useraliases),app,dataandfeatures. #6110 supplies onlyuser, so on/forms/:namefeaturesis{}.Why that is a real asymmetry rather than a default
AppContentfillsfeaturesfrom/api/v1/auth/config(getAuthConfig()), and the flags are already load-bearing for predicate evaluation elsewhere —ExpressionProvider's own docstring namessys_organization's create button being hidden whenmultiOrgEnabled === false. So an authored predicate namingfeatures.multiOrgEnabled:/apps/:appName/*— resolves against the real flags;/forms/:name— names an unbound key, which faults, andvisibleWhenfails OPEN, so the gated field/section is shown to everyone.That is the same shape as #6110's own defect, one scope root over: one authored text, two verdicts depending on which route opened the form.
appis likewise{}there, though that one is arguably correct by construction —/forms/:namenames no app, which is exactly whyInternalFormRouterendersHomeLayoutrather thanConsoleLayout(see that file's comment and objectui#4109).Why it was not fixed in #6110
Out of that card's scope by its own words (it names
current_user), and it is not a one-line addition:featuresrequires an/auth/configfetch on a route that currently makes none, plus a decision about what a predicate should see before that fetch resolves.ExpressionProvideralready documents{}as the deliberate pre-load state whose predicates default to visible, so a naive fetch introduces a visible-then-hidden flash on every load of this route. PR #6261 records the gap inFormPage.tsx's own comment rather than papering over it.Not yet measured
Whether any shipped form-view metadata actually authors a
features.*predicate. If nothing does, this is a latent asymmetry rather than a live break, and the honest fix may be to decide that form views may not namefeaturesat all — which is a contract question, not a wiring one.Related
current_userhalf, fixed.