Blocked-by: objectstack-ai/cloud#1452
Part of objectstack-ai/cloud#1452 (filed by the triage seat from the cloud seat's transfer package on that thread — anchors re-verified at cloud's framework pin 907c11d2). Companion to #5519 (the /_console/studio/* route gate).
⚠️ THREE OF FOUR ROWS ARE DONE — do not implement from the body below without reading this
| row |
state |
| Browse App Marketplace |
already fixed by PR #5517 (#5504) — before this card was ever dispatched |
| Start with a template |
already fixed by PR #5517 — before dispatch |
| Build an app |
landed — PR #5575 (features.aiStudio read; hidden, not dimmed, per the flag's declared force-hide semantics) |
| Setup |
the only live row — blocked on cloud#1452's product ruling |
⛔ So this card's headline claim — "two of them contradict a flag the server already sends as false", called "the sharpest form of this bug" below — was already false at dispatch time. Anyone re-reading the body alone will find a bug that no longer exists and produce a plausible-looking diff that fixes nothing. The premise check that caught this is the highest-value thing on the card.
Also settled, ⛔ do not reopen: the studio.access half was answered rather than implemented — #5576 was ruled Option B on 2026-08-22 (the library home page asks only deployment-level questions; principal-level Studio entry stays with the console route gate) and closed as not planned. holdsStudioAccess lives in apps/console, which depends on @object-ui/app-shell, so the import is structurally impossible in the required direction.
Unblock reading, for whichever seat can reach objectstack-ai/cloud — this seat's credential cannot (measured: attach returns "you don't have access", and a repository listing filtered on cloud returns empty; that is outside the credential, not merely unattached, and ⛔ "could not check" is not "checked and still blocked"):
Is objectstack-ai/cloud#1452 closed, and did it rule the "Setup" row's product question?
Closed with a ruling ⇒ this card returns to pm:queue with only the Setup row live; re-verify that row's file face on the merged ref first. Still open ⇒ it stays pm:blocked and nothing is owed.
The Console home page offers a plain tenant four authoring / marketplace / setup entry points on a deployment that refuses all of them — and for two of those cards the server is already sending the correct false on /api/v1/runtime/config. Those two are the sharpest form of this bug: the flag is right, derived, pinned by tests on the serving side, and not read here.
Measured
Composed hosted-SaaS shape (OS_TENANCY_POSTURE=isolated, OS_AI_STUDIO_AGENTS=ask, HotCRM composed artifact), tenant northwind as plain org owner, Playwright. /_console/home shows, verbatim:
- "Build an app" — "Start from scratch — design objects, forms, automations and interfaces"
- "Start with a template" — "Install a template app from the marketplace and customize it"
- "Browse App Marketplace"
- "Setup"
Behind them on that same deployment: /api/v1/meta/* → 403, and install-local / marketplace-browse / cloud-connection → 404 (those routes are not merely guarded, they are absent — the host declines them by identity).
Re-measured 2026-08-21 on the EE 4.1.0 release image (cloud#1510): a plain tenant user (positions: ["user"]) follows the "Build an app" card into the full authoring flow and is only refused at submit (POST /api/v1/packages → 403).
The lockdown criterion for this shape is two-part — UI entry hidden AND API refused. The backend half is green.
The cards are produced here, not upstream
Both other repos were grepped for the verbatim strings above. Cloud: zero hits. Framework: two hits, both documentation. There is no SDUI or server-rendered home payload on either side — this list is this app's.
Two of the four already have a correct server signal that is not being read
GET /api/v1/runtime/config carries a features map. On the framework side, two of its keys are derived from what is really mounted, not declared:
features.marketplace — framework packages/cloud-connection/src/runtime-config-plugin.ts (hasMarketplaceBrowseMount, read off the app's own route ledger)
features.installLocal — same file, ceiling-and-observation (a host can lower it, never raise it)
On the composed shape both are false, and that is pinned by tests on the serving side — cloud apps/objectos-ee/test/composed-cloud-surfaces-declined.e2e.test.ts asserts features.marketplace === false against a real boot, and composed-install-local-declined.e2e.test.ts does the same for installLocal, each alongside the 404 it must agree with.
So "Browse App Marketplace" and "Start with a template" render in direct contradiction to a flag the server already sends correctly. Nothing on the server side can fix those two; they are a missed read here. (⚠️ Both have since been fixed — see the status box at the top.)
The third card now has a truthful signal too
"Build an app" is the AI/metadata-authoring entry. Its declared flag is features.aiStudio, documented by the same plugin as "whether the SPA should surface AI-driven metadata authoring ('online development') affordances … set false to force-hide the authoring UI" (runtime-config-plugin.ts:490 documents the default; the composed boot previously leaked default-true).
That flag was wrong on this shape until now: the composed boot passed no aiStudio, the open plugin defaults it to true, and no plan token is resolved there to override it — so the deployment served {"installLocal":false,"marketplace":false,"aiStudio":true,"autoPublishAiBuilds":true} while its ToolRegistry held zero authoring handlers. Fixed cloud-side in objectstack-ai/cloud#1471, where it is now derived from the same resolution that decides the agent mount. Worth stating plainly: before that fix, honouring features.aiStudio here would not have hidden this card, because the server was saying true. (⚠️ This row has since landed — PR #5575.)
The fourth card is a genuine question, not an assumed defect
"Setup" is gated by setup.access — and org admins and ordinary members legitimately hold it (framework plugin-security/src/security-plugin.ts excludes setup.access/setup.write from the platform-exclusive set on purpose: "they are the Setup app shell + tenant-settings-write caps, not platform powers"). So hiding Setup is a product decision about what a walled hosted-SaaS tenant should see, not a declared-vs-enforced restoration like the other three. It is deliberately carved OUT of this card, pending a ruling recorded on cloud#1452. Do not fold it in silently.
Proposed seam
Have the home card list honour the signals the Console already fetches, per card:
| card |
signal |
| Browse App Marketplace |
features.marketplace (already false — a missed read) |
| Start with a template |
features.installLocal / features.marketplace (already false — a missed read) |
| Build an app |
features.aiStudio, and/or studio.access from /auth/me/permissions |
| Setup |
setup.access — but see above; needs a product ruling first |
studio.access is available on the same /auth/me/permissions payload this app already consumes through useCapabilityGate (ADR-0066 D4), so no new server surface is required for any row. (⚠️ The studio.access row was subsequently ruled Option B and closed — see the status box.)
⛔ Do not weaken the backend
The 403 on /api/v1/meta/* and the 404s on install-local / marketplace / cloud-connection are measured green and are the other half of the criterion. Hiding the entries is not a reason to soften any of them.
Serial constraint (for the lane)
In-flight sibling #5504 (OS_CLOUD_URL=off marketplace load-failure + home still promoting the marketplace entry) touches the same home-card surface from a different posture. Whoever claims this card should name #5504 in the serial-constraints line and sequence behind it if the file surface overlaps. (⚠️ #5504 landed as PR #5517.)
Blocked-by: objectstack-ai/cloud#1452
Part of objectstack-ai/cloud#1452 (filed by the triage seat from the cloud seat's transfer package on that thread — anchors re-verified at cloud's framework pin
907c11d2). Companion to #5519 (the/_console/studio/*route gate).The Console home page offers a plain tenant four authoring / marketplace / setup entry points on a deployment that refuses all of them — and for two of those cards the server is already sending the correct
falseon/api/v1/runtime/config. Those two are the sharpest form of this bug: the flag is right, derived, pinned by tests on the serving side, and not read here.Measured
Composed hosted-SaaS shape (
OS_TENANCY_POSTURE=isolated,OS_AI_STUDIO_AGENTS=ask, HotCRM composed artifact), tenantnorthwindas plain org owner, Playwright./_console/homeshows, verbatim:Behind them on that same deployment:
/api/v1/meta/*→ 403, and install-local / marketplace-browse / cloud-connection → 404 (those routes are not merely guarded, they are absent — the host declines them by identity).Re-measured 2026-08-21 on the EE 4.1.0 release image (cloud#1510): a plain tenant user (
positions: ["user"]) follows the "Build an app" card into the full authoring flow and is only refused at submit (POST /api/v1/packages→ 403).The lockdown criterion for this shape is two-part — UI entry hidden AND API refused. The backend half is green.
The cards are produced here, not upstream
Both other repos were grepped for the verbatim strings above. Cloud: zero hits. Framework: two hits, both documentation. There is no SDUI or server-rendered home payload on either side — this list is this app's.
Two of the four already have a correct server signal that is not being read
GET /api/v1/runtime/configcarries afeaturesmap. On the framework side, two of its keys are derived from what is really mounted, not declared:features.marketplace— frameworkpackages/cloud-connection/src/runtime-config-plugin.ts(hasMarketplaceBrowseMount, read off the app's own route ledger)features.installLocal— same file, ceiling-and-observation (a host can lower it, never raise it)On the composed shape both are
false, and that is pinned by tests on the serving side — cloudapps/objectos-ee/test/composed-cloud-surfaces-declined.e2e.test.tsassertsfeatures.marketplace === falseagainst a real boot, andcomposed-install-local-declined.e2e.test.tsdoes the same forinstallLocal, each alongside the 404 it must agree with.So "Browse App Marketplace" and "Start with a template" render in direct contradiction to a flag the server already sends correctly. Nothing on the server side can fix those two; they are a missed read here. (⚠️ Both have since been fixed — see the status box at the top.)
The third card now has a truthful signal too
"Build an app" is the AI/metadata-authoring entry. Its declared flag is
features.aiStudio, documented by the same plugin as "whether the SPA should surface AI-driven metadata authoring ('online development') affordances … set false to force-hide the authoring UI" (runtime-config-plugin.ts:490documents the default; the composed boot previously leaked default-true).That flag was wrong on this shape until now: the composed boot passed no⚠️ This row has since landed — PR #5575.)
aiStudio, the open plugin defaults it totrue, and no plan token is resolved there to override it — so the deployment served{"installLocal":false,"marketplace":false,"aiStudio":true,"autoPublishAiBuilds":true}while its ToolRegistry held zero authoring handlers. Fixed cloud-side in objectstack-ai/cloud#1471, where it is now derived from the same resolution that decides the agent mount. Worth stating plainly: before that fix, honouringfeatures.aiStudiohere would not have hidden this card, because the server was sayingtrue. (The fourth card is a genuine question, not an assumed defect
"Setup" is gated by
setup.access— and org admins and ordinary members legitimately hold it (frameworkplugin-security/src/security-plugin.tsexcludessetup.access/setup.writefrom the platform-exclusive set on purpose: "they are the Setup app shell + tenant-settings-write caps, not platform powers"). So hiding Setup is a product decision about what a walled hosted-SaaS tenant should see, not a declared-vs-enforced restoration like the other three. It is deliberately carved OUT of this card, pending a ruling recorded on cloud#1452. Do not fold it in silently.Proposed seam
Have the home card list honour the signals the Console already fetches, per card:
features.marketplace(alreadyfalse— a missed read)features.installLocal/features.marketplace(alreadyfalse— a missed read)features.aiStudio, and/orstudio.accessfrom/auth/me/permissionssetup.access— but see above; needs a product ruling firststudio.accessis available on the same/auth/me/permissionspayload this app already consumes throughuseCapabilityGate(ADR-0066 D4), so no new server surface is required for any row. (studio.accessrow was subsequently ruled Option B and closed — see the status box.)⛔ Do not weaken the backend
The 403 on
/api/v1/meta/*and the 404s on install-local / marketplace / cloud-connection are measured green and are the other half of the criterion. Hiding the entries is not a reason to soften any of them.Serial constraint (for the lane)
In-flight sibling #5504 (⚠️ #5504 landed as PR #5517.)
OS_CLOUD_URL=offmarketplace load-failure + home still promoting the marketplace entry) touches the same home-card surface from a different posture. Whoever claims this card should name #5504 in the serial-constraints line and sequence behind it if the file surface overlaps. (