You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
holdsStudioAccess is unreachable from @object-ui/app-shell, so the library's surfaces cannot share the console's one definition of "platform operator" #5576
Found while implementing #5521 (PR #5575), where it blocked one of that card's two proposed signals. Deliberately left out of that PR's scope.
Claim
holdsStudioAccess(systemPermissions) — the fail-closed predicate for "may this principal enter Studio", and this repo's single definition of "is this principal a platform operator rather than a business user" — lives in the console app:
apps/console/src/components/studioEntry.ts
apps/consoledepends on@object-ui/app-shell. So no module in packages/app-shell/** can import it: the dependency runs the other way and app-shell's manifest declares no console dependency (it could not, without a cycle).
That is fine while every consumer is a console page — which was true when #5563 reused it for the Approvals raw-payload panel. It stops being true as soon as a surface inside the library needs the same answer.
Where it just bit
#5521 asks the Console home page to honour studio.access for the "Build an app" card. That page is packages/app-shell/src/console/home/HomePage.tsx, re-exported as DefaultHomePage — inside the library. PR #5575 closed that card's features.aiStudio half and had to leave the studio.access half undone, because the only ways to do it were:
That studioEntry.ts is in the wrong place for the route gate. useStudioEntry — the hook, its retry policy, its endpoint, its pending/failed states — is console routing policy and belongs where it is. Only the pure predicate plus its capability constant are the shared fact:
It has no React dependency and no fetch dependency — it is a membership test over the array MePermissionsResponse already carries.
Shape of a fix
@object-ui/permissions is the natural home: it already owns MePermissionsResponse, already exposes the raw systemPermissions: string[] | undefined on its context, and is already a dependency of bothapps/console and packages/app-shell. Move the predicate and the constant there; re-export from studioEntry.ts so its existing importers and its docblock stay put.
The load-bearing part is the inversion doctrine, which must travel with the code rather than being left behind as a comment on a re-export: this predicate fails closed on undefined, opposite to usePermissions().hasCapabilities, which fails open by design. Both live in @object-ui/permissions after this move, one module apart, so the docblock explaining why they differ is more necessary there than it is today, not less. The undefined-vs-[] distinction objectui#4656 preserved is what makes the difference expressible at all.
Worth weighing before doing it
Whether a library surface should consume studio.access at all is a real design question and not settled by this issue. @object-ui/app-shell is embedded by hosts other than the console, and studio.access semantics are currently the console's route policy. A reviewer may reasonably conclude the home page should keep asking a deployment-level question (features.aiStudio, which is what #5575 shipped) and leave principal-level Studio entry to the route gate. In that case this issue closes as not planned and #5521's studio.access row is answered rather than implemented — which is a fine outcome, but it should be decided rather than defaulted into.
Found while implementing #5521 (PR #5575), where it blocked one of that card's two proposed signals. Deliberately left out of that PR's scope.
Claim
holdsStudioAccess(systemPermissions)— the fail-closed predicate for "may this principal enter Studio", and this repo's single definition of "is this principal a platform operator rather than a business user" — lives in the console app:apps/consoledepends on@object-ui/app-shell. So no module inpackages/app-shell/**can import it: the dependency runs the other way and app-shell's manifest declares no console dependency (it could not, without a cycle).That is fine while every consumer is a console page — which was true when #5563 reused it for the Approvals raw-payload panel. It stops being true as soon as a surface inside the library needs the same answer.
Where it just bit
#5521 asks the Console home page to honour
studio.accessfor the "Build an app" card. That page ispackages/app-shell/src/console/home/HomePage.tsx, re-exported asDefaultHomePage— inside the library. PR #5575 closed that card'sfeatures.aiStudiohalf and had to leave thestudio.accesshalf undone, because the only ways to do it were:#5575 took (3) and recorded why.
Not claimed
That
studioEntry.tsis in the wrong place for the route gate.useStudioEntry— the hook, its retry policy, its endpoint, its pending/failed states — is console routing policy and belongs where it is. Only the pure predicate plus its capability constant are the shared fact:It has no React dependency and no fetch dependency — it is a membership test over the array
MePermissionsResponsealready carries.Shape of a fix
@object-ui/permissionsis the natural home: it already ownsMePermissionsResponse, already exposes the rawsystemPermissions: string[] | undefinedon its context, and is already a dependency of bothapps/consoleandpackages/app-shell. Move the predicate and the constant there; re-export fromstudioEntry.tsso its existing importers and its docblock stay put.The load-bearing part is the inversion doctrine, which must travel with the code rather than being left behind as a comment on a re-export: this predicate fails closed on
undefined, opposite tousePermissions().hasCapabilities, which fails open by design. Both live in@object-ui/permissionsafter this move, one module apart, so the docblock explaining why they differ is more necessary there than it is today, not less. Theundefined-vs-[]distinction objectui#4656 preserved is what makes the difference expressible at all.Worth weighing before doing it
Whether a library surface should consume
studio.accessat all is a real design question and not settled by this issue.@object-ui/app-shellis embedded by hosts other than the console, andstudio.accesssemantics are currently the console's route policy. A reviewer may reasonably conclude the home page should keep asking a deployment-level question (features.aiStudio, which is what #5575 shipped) and leave principal-level Studio entry to the route gate. In that case this issue closes asnot plannedand #5521'sstudio.accessrow is answered rather than implemented — which is a fine outcome, but it should be decided rather than defaulted into.Refs
features.*andstudio.access— two of them contradict a flag the server already sends as false #5521 / PR Console Home honours features.aiStudio for the authoring front door (#5521) #5575 — where the import was needed and could not be writtenGenerated by Claude Code