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
{{ message }}
Repository navigation
Commit 72877bc
Browse filesBrowse the repository at this point in the historyBrowse files
feat(app-shell): the environment list offers only "Set up your production environment"; the development-environment door is gone (#29778)
<!-- claude-projects-attribution: {"github_login":"os-zhuang"} -->
_Requested by **Sam** · [project
thread](https://claude.ai/code/project/chan_01GsBogozeZG5sdpAARrN7HP?thread=cmsg_01GsBogozeZG5sdpAARrN7HPLuovszPmhavRYXixiYVakP)_
Part of #29770. This PR implements the whole card. The card is closed by
hand when this PR lands: the executing branch is a project-thread
branch, not `claude/issue-<n>-*`, and the closing-target guard only
accepts a claim that names the latter shape.
## What changes
Card B of the objectstack-ai/cloud#3487 ruling D ([ruling of
record](objectstack-ai/cloud#3487 (comment))):
each organization has exactly one environment, its production
environment, and the development type is retired. All changes are under
`ui/packages/app-shell` and `ui/packages/i18n`.
- **Toolbar (`EnvironmentListToolbar`).** "Set up your production
environment" is the only creation path. `decideEnvironmentCta` now
answers `setup_production` or `production_exists`. In
`production_exists` the `create_environment` action is withheld: no "Add
development environment" button, no plan-locked "Development
environments · Team plan and up" mark (`DevelopmentLockedMark` is
deleted), no dialog, no POST. Other toolbar actions render as before.
The `?runAction=create_environment` deep link is consumed and runs
nothing in that state, the same handling the retired plan-locked state
had. The loading skeleton and the no-signal fallback (metadata label +
dialog safety net) are unchanged.
- **Dialog (`entitlementDialogFromError`).** Maps `PRODUCTION_ENV_LIMIT`
only. The `DEV_ENV_PLAN_LOCKED` / `DEV_ENV_LIMIT` branches and
`planPhrase` are deleted. Those codes and cloud card A's new
`DEV_ENV_RETIRED` take the ordinary error path and show the server's own
message.
- **State.** `EnvironmentEntitlementsState.canCreateDevelopmentEnv` and
`EnvironmentEntitlementsSummary.development` are removed. None of these
types is exported from `@object-ui/app-shell`.
- **i18n.** Removed from all ten locale bundles: `planTier.teamAndUp`
(and the `planTier` namespace), `environment.addDevelopment`,
`environment.developmentLocked`,
`environment.entitlement.{planLockedTitle, planLockedBody, limitTitle,
limitBody, limitBodyWithCount, freePlan, namedPlan, upgradeCta}`. The
ADR-0060 live list `product-specific.allowlist.json` shrinks by the same
keys. It accepts deletions only, and this PR only deletes.
## Why
The control plane is closing its development doors in parallel (cloud
card A, objectstack-ai/cloud#3501, `DEV_ENV_RETIRED`). Without this
change, a Console showing "Add development environment" would only
produce refusals. Cloud's next framework pin carries this PR, and cloud
card D (objectstack-ai/cloud#3503) waits for that pin.
**Acceptance notes.**
- An organization that already owns its environment, in the
**no-signal** state (summary endpoint and row derivation both failed),
still sees the metadata-labelled create button, as before. A POST there
gets the control plane's answer. If that answer is
`PRODUCTION_ENV_LIMIT`, the limit dialog opens. If it is
`DEV_ENV_RETIRED`, a toast shows the server's localized sentence. Which
code cloud returns for a typeless create once production exists is cloud
card A's call.
- `classifyEnvironmentType` in `useEnvironmentEntitlements` still
mirrors the server's two types for the row fallback. It only decides
`hasProductionEnv`, and archived rows are filtered out. Cloud card D
retires the type end to end.
## Verification
- New pins: `EnvironmentListToolbar.test.tsx` › "no
development-environment door renders (cloud#3487 D)" renders nine
entitlement states (loading, no signal, no production / production ×
summary / rows × manager / member × free / team) in `en` and `zh`. Each
asserts that no text matches `/development/i` (`开发环境` in zh) and that
the retired locked-mark test id is absent. `entitlements.test.ts` and
`useConsoleActionRuntime.test.tsx` pin `DEV_ENV_PLAN_LOCKED` /
`DEV_ENV_LIMIT` / `DEV_ENV_RETIRED` to the ordinary error path, with no
dialog.
- Production path unchanged: the setup-production label, primary
variant, deep-link `autoTrigger` and skeleton pins are kept. The
overflow deep-link pin (#4162) now runs on `setup_production`, using
fillers with `order: -1` so the create action still overflows.
- Local run at `ebe16a4c` (objectstack-ai/objectstack): `pnpm --dir ui
exec vitest run packages/app-shell/src/environment/
packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.test.tsx
packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.apiActionDoor-27611.test.tsx
packages/app-shell/src/__tests__/product-neutral-renderer.ratchet.test.ts
packages/i18n/` → 106 files passed; 1634 tests passed, 13 skipped.
`eslint --quiet` on the touched files is clean. The whole UI suite and
type check ran in CI on the same head: all 16 Test UI shards, Type Check
UI and Lint UI passed.
Clause-②: no
Metadata-protocol: none
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [ ] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QUs3BuhJziidmx7KHut5GG)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(app-shell): the environment list offers only "Set up your production environment"; the development-environment door is gone
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
Each organization now has exactly one environment, its production environment: the development environment type is retired (objectstack-ai/cloud#3487, ruling D). The Console's `sys_environment` list follows.
12
+
13
+
-**Toolbar.** "Set up your production environment" is the only creation path. Once the organization owns its production environment, the `create_environment` action is withheld: no "Add development environment" button, no "Development environments · Team plan and up" locked mark, no dialog and no POST. The list's other toolbar actions render as before, and the `?runAction=create_environment` deep link is consumed and runs nothing in that state. The loading skeleton and the no-signal fallback (the action's metadata label) are unchanged.
14
+
-**Dialog.**`entitlementDialogFromError` maps `PRODUCTION_ENV_LIMIT` only. `DEV_ENV_PLAN_LOCKED`, `DEV_ENV_LIMIT` and the control plane's new `DEV_ENV_RETIRED` take the ordinary error path and show the server's own message.
15
+
-**State.**`EnvironmentEntitlementsState` drops `canCreateDevelopmentEnv`, and the summary type drops `development`; `EnvironmentCtaKind` is now `'setup_production' | 'production_exists'`. None of these is exported from `@object-ui/app-shell`.
16
+
-**i18n.** Removed from every locale bundle: `planTier.teamAndUp` (the namespace goes with it), `environment.addDevelopment`, `environment.developmentLocked`, and `environment.entitlement.{planLockedTitle, planLockedBody, limitTitle, limitBody, limitBodyWithCount, freePlan, namedPlan, upgradeCta}`. A host that read one of these keys through `t()` now gets the key back; there is no replacement, because nothing is left to label.
0 commit comments