Skip to content

Commit 977be38

Browse files
fix(service-settings)!: remove the Feature Flags page whose four toggles changed nothing (#30272)
<!-- claude-projects-attribution: {"github_login":"os-bill"} --> _Requested by **Bill** · [project thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgGwJpXNreDPXu62S58G5fJZ)_ domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb ## 维护者速读 - 改了什么:Setup 里的「功能开关」页整页移除:看板泳道、实时光标、行内评论、AI 助手四个开关,连同这个设置命名空间、它的 Setup 导航入口、四语文案和对外导出 `featureFlagsSettingsManifest` 一起删掉。 - 为什么改:这四个开关没有任何代码读取,管理员打开保存后什么都不会变(ADR-0049 要么兑现要么删除);四个删完页面只剩一条提示,按您「以上都同意,立卡」的方向整页移除,功能做出来时再把开关带回来。 - 风险与回滚:已存的 `sys_setting` 行和 `OS_FEATURE_FLAGS_*` 环境变量留在原处、变成无人读取的惰性数据,不做清理;访问 `/api/settings/feature_flags` 现在答 404。直接 import 该导出的宿主编译会报错(changeset 写明一行修法)。回滚即撤本 PR,无数据迁移。 - 先看:`packages/services/service-settings/src/manifests/index.ts`(聚合去掉一项)、`packages/platform-objects/src/apps/setup-nav.contributions.ts`(导航条目)、`packages/services/service-settings/src/settings-service.test.ts` 的新钉子 `no longer serves the retired feature_flags namespace`。 - 两分钟验证:`pnpm --filter @objectstack/service-settings exec vitest run src/settings-service.test.ts -t "retired feature_flags"`,预期 1 条通过;或在控制台 Setup → 配置分组里,预期不再出现「功能开关」。 - 需要你做的:无,席位落地。 Fixes #30204 Clause-②: no (narrowing) — removes the four settable keys of the `feature_flags` settings namespace, the then-empty namespace, its Setup entry and the `featureFlagsSettingsManifest` export; nothing is widened Metadata-protocol: none Skills-docs: follow-up #30275 ## What changes The card's direction, agreed by the maintainer on filing (「以上都同意,立卡」): "Remove the four toggles, and the settings page if it is left empty. A flag comes back with the feature that reads it." Triage 6648454483 asks to wire each flag or remove it and say which per flag. **Per flag: all four removed, none wired.** Measured at `origin/main` `5fa9375e` (and re-checked after merging `origin/main` at 1d8e770): a `git grep` for `ai_enabled`, `kanban_swimlanes`, `realtime_cursors`, `inline_comments` outside tests and docs hits only the manifest and the service-settings translations. No code under `packages/` or `ui/` reads `feature_flags.*` through the settings service, the settings client or an `OS_FEATURE_FLAGS_*` variable. | Key | Default | Reader found | Disposition | |:---|:---|:---|:---| | `ai_enabled` | false | none: the console's AI panel is decided elsewhere (`AppHeader.tsx`'s `topbar.aiAssistant` is a label key) | removed | | `kanban_swimlanes` | false | none | removed | | `realtime_cursors` | false | none | removed | | `inline_comments` | true | none | removed | The console's `features` map (`ui/packages/app-shell/src/console/AppContent.tsx`) comes from `/api/v1/auth/config`, not from this namespace, and is untouched. The cloud repository is not readable from this seat, so a reader there is not ruled out; it is said so here and in the report. **The namespace goes too.** With the four toggles gone the manifest held only a beta banner and two empty groups, so per the ruling the page is removed (seat amendment 03:16Z, Claim 6648715176): - `@objectstack/service-settings`: `manifests/feature-flags.manifest.ts` deleted; `builtinSettingsManifests` loses it (10 manifests to 9); the `featureFlagsSettingsManifest` export leaves `src/manifests/index.ts` and `src/index.ts`; the `feature_flags` block leaves the en / zh-CN / ja-JP / es-ES bundles; README's env-override example moves to `ai.log_prompts`, and its built-in table drops the row; the plugin option's comment names `builtinSettingsManifests` instead of a list. - `@objectstack/platform-objects`: the Setup entry `nav_settings_feature_flags` and its four locale labels and three source-hash rows are removed, so Setup leaves no link to a namespace nothing serves. - Texts that named it: `docs/qa/platform-checklist/areas/platform-core.json` (11 namespaces to 10, list and variants), `packages/cli/README.md`, `content/docs/protocol/kernel/config-resolution.mdx` (env example now `OS_AI_LOG_PROMPTS`), `content/docs/ui/apps.mdx` (Configuration group row), and comment-only lines in `plugin-security/src/objects/default-permission-sets.ts`, `service-datasource/src/admin-routes.ts` and `mcp/src/connect-ui.ts`. **What happens to a stored value.** Nothing registers `feature_flags` any more, so `SettingsService.getManifest('feature_flags')` throws `UnknownNamespaceError` (`SETTINGS_UNKNOWN_NAMESPACE`), a read or write through the service rejects the same way, and the REST door answers `404 UNKNOWN_NAMESPACE`. A `sys_setting` row stored under the namespace and an `OS_FEATURE_FLAGS_*` environment variable are left where they are and become inert: no code resolves them. They are not cleaned up; an operator may delete them at will. The changeset carries the BREAKING banner, both declaration lines, and the ADR-0087 disposition `not-required (runtime-export-retired @objectstack/service-settings#featureFlagsSettingsManifest)`, verified by the gate (predicates 1 to 3). ## Tests - New pin in `settings-service.test.ts`, `SettingsService — registry`: with every built-in manifest registered and `OS_FEATURE_FLAGS_AI_ENABLED=true` in the env, `builtinSettingsManifests` carries no `feature_flags`, `getManifest('feature_flags')` throws an `UnknownNamespaceError` whose `code` is `SETTINGS_UNKNOWN_NAMESPACE`, and `get('feature_flags', 'ai_enabled')` rejects with that code. - Tests that used the namespace as a convenient boolean toggle now use `ai.log_prompts` (default false) and `ai.trace_enabled` (default true): env coercion, `getNamespace`, the option-table negative case, and `envKeyOf`. - Count pins moved deliberately, each with its reason: built-in manifests 10 to 9 in `settings-visibility-declaration.pin.test.ts` (96 predicates unchanged: the removed manifest declared no `visible`), `settings-retired-workspace-noun.pin.test.ts` and `settings-translation-coverage.test.ts`. - `settings-branding-posture.pin.test.ts`: the walled-posture listing control keeps `company` and `localization` (the namespace it also named is gone); the Setup-order control now compares against `nav_settings_lifecycle`, the last platform-objects entry. - `settings-help-business-words.pin.test.ts` sweeps four namespaces instead of five. <details><summary>Suite readings on head 1d8e770 (after merging origin/main)</summary> - `pnpm --filter @objectstack/service-settings test`: Test Files 67 passed (67), Tests 1151 passed (1151); `typecheck` exit 0. - `pnpm --filter @objectstack/platform-objects test`: Test Files 110 passed (110), Tests 1514 passed (1514); `typecheck` and `check:test-typecheck` exit 0. - Before the merge (head a9be523): service-settings 1143/1143, platform-objects 1514/1514. </details> <details><summary>Full dogfood (owed by the narrowing arm), head 1d8e770: one red, also red with this diff reverted</summary> - `pnpm --filter @objectstack/dogfood test` under the verify lock: VERDICT command-exit 1; Test Files 1 failed, 418 passed, 3 skipped (422); Tests 1 failed, 3491 passed, 36 skipped (3528). - The one red: `test/permission-set-clone-door.dogfood.test.ts` "a platform set (the lock-and-clone rule's commonest source) copies into its shipping package's pair": the clone of `member_default` answers 422 `INVALID_METADATA` ("grants on object 'sys_comment' which is not defined in objects"). - Control leg: every package source file of this diff restored to `origin/main` (19 files), the five touched packages force-rebuilt, the same file run: the same test fails the same way (1 failed, 10 passed). Restored to HEAD afterwards (`git diff HEAD` empty) and rebuilt. So the red is on `main`, not caused by this PR. The `sys_comment` grant on `member_default` came in with 490d136 (#30222), before this branch's base. </details> ## Deviations from the order - Contract review 6649883787 (must-fix): `docs/qa/platform-checklist/areas/platform-core.json` still counted 11 open-framework namespaces in two places (the `GET /api/settings` step and the `settings-hub-roundtrip` scoring clause); both now say 10, matching the list. Fixed in 8dffea3. - Contract review 6649883787 (should-fix, surface widened by the seat): `packages/platform-objects/src/system/sys-setting.object.ts` no longer gives `feature_flags` as an example namespace (now `mail, branding, storage`); `node scripts/check-i18n-bundles.mjs --write` regenerated `en.objects.generated.ts`, and the zh-CN / ja-JP / es-ES help strings were updated by hand (merge mode keeps translated values). Fixed in 8dffea3 (source) and 74c2ef8 (bundles). - Test Core (1/8) red on 1d8e770 (surface widened by the seat): `packages/cli/test/connect-agent-both-halves-wire.pin.test.ts` pins the platform admin's Setup tree; with `nav_settings_feature_flags` gone it is 32 entries, not 33 (both assertions, and the comment's 24 contributions now 23). A grep of every test under `packages/` and `ui/` for `nav_settings_feature_flags` or a Setup entry total found no other pin. Fixed in 8dffea3. - The changeset names the export, not each removed key, on its FROM line: the ADR-0087 `runtime-export-retired` category reads exactly the marker's operands there. The four keys are listed in prose as removed, each with no effect. Readings on 74c2ef8: `dispatch-gates --commands` 96 derived (the 95 above plus `check:cli-test-child-env`), 96 exit 0, `--ran` 0 NOT-MEASURED; `pnpm lint` exit 0; cli unit pins (3 files, 12 tests), platform-objects 1514/1514 + typecheck, plugin-audit / plugin-security nav tests, rest.test.ts 216/216, service-settings setup-nav-hub-parity 9/9, all pass; `check:i18n` and `check:i18n-stale-fill` exit 0. ## Acceptance notes - `docs/adr/0007-settings-manifest-and-kv-store.md` still shows `feature_flags.ai_enabled` as its worked example; ADRs are outside this PR's surface and the example stands as the record of the decision. - `skills/objectstack-platform/references/plugin-development.md:354` names the `feature_flags` manifest as the place for org-tunable toggles; `skills/**` is outside this PR (the `Skills-docs:` line above), the seat files it. - The `docs.objectos.ai` repository documents `FEATURE_FLAGS_AI_ENABLED`; that text is now false and lives outside this repository. - `packages/spec` tests (`settings-banner-translation.test.ts`, `customization-package.test.ts`) and the console test `SettingsField.infoBanner-12671.test.tsx` use `feature_flags` as a self-contained fixture string; none reads the removed manifest, and both trees are outside this PR. - `docs/audits/2026-06-handwritten-docs-accuracy-followups.md:288` cites the old key as an example in a dated audit; left as history. - Released `CHANGELOG.md` entries that mention `OS_FEATURE_FLAGS_AI_ENABLED` are release-owned and unchanged. ## Verification <details><summary>Derived gates: 95 derived, 95 run, 0 NOT MEASURED</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (31 paths vs merge base) derived 95 families; the list re-derived on head 1d8e770 is identical. Every one exits 0. The sweep ran on a9be523; after the merge, these re-ran on 1d8e770, all exit 0: `check-adr-0087-registration --base origin/main` (verified: `runtime-export-retired @objectstack/service-settings#featureFlagsSettingsManifest`), `check-plugin-teardown-shape --self-test`, `spec check:skill-examples`, `check:dual-build-cjs-loads`, `check:i18n`, `check:i18n-stale-fill`, `check:type-check-coverage`, `docs-audit/check-affected-docs`, `check:published-files`, `check-changeset-no-major`, `check-empty-changeset`, `check:nul-bytes`, and `check-platform-checklist --changed-from origin/main` (outside the derivation). `--ran` reconciliation: "95 derived famil(ies) accounted for — 95 run, 0 NOT-MEASURED". Three families first exited 3 (prerequisite not met: a shallow fixture commit, an unbuilt `client`, an unbuilt workspace) and went green once the prerequisite was met; the first `check-adr-0087-registration` run was red on the changeset's FROM line and went green after the fix in a9be523. `pnpm lint`: exit 0 on 1d8e770. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01Utp6CZJQ8AFU2wk2a8fuhz)_ <!-- signed: domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8b117c0 commit 977be38

37 files changed

Lines changed: 97 additions & 188 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/service-settings': patch
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
fix(service-settings)!: Setup no longer offers a Feature Flags page whose four toggles changed nothing (#30204)
7+
8+
Clause-②: no (narrowing) — removes the four settable keys of the `feature_flags` settings namespace (`ai_enabled`, `kanban_swimlanes`, `realtime_cursors`, `inline_comments`), the namespace itself (left empty by their removal), its Setup entry and the `featureFlagsSettingsManifest` export. Nothing is widened.
9+
Metadata-protocol: none
10+
11+
<!-- adr-0087: not-required (runtime-export-retired @objectstack/service-settings#featureFlagsSettingsManifest) The one author-reachable removal is a runtime export: the `feature_flags` settings manifest constant leaves `@objectstack/service-settings`'s published entry and `builtinSettingsManifests`. A settings key is a value an administrator saves, not metadata an author writes: no spec key, authorable spelling, object definition or stored shape is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. -->
12+
13+
**BREAKING** for an administrator who saved one of the four Feature Flags toggles, and for a host that imports `featureFlagsSettingsManifest`.
14+
15+
- **Why.** No code read any of the four keys, so turning one on changed nothing (ADR-0049 enforce-or-remove). A flag comes back with the feature that reads it.
16+
- **Removed keys.** `feature_flags.ai_enabled`, `feature_flags.kanban_swimlanes`, `feature_flags.realtime_cursors` and `feature_flags.inline_comments`: removed; each had no effect. The in-app AI panel is decided elsewhere and is unchanged.
17+
- **The namespace.** `feature_flags` is no longer registered, so `GET` and `PUT /api/settings/feature_flags` answer `404 UNKNOWN_NAMESPACE`, and `SettingsService.getManifest('feature_flags')` throws `UnknownNamespaceError` (`SETTINGS_UNKNOWN_NAMESPACE`). Setup's Configuration group no longer lists Feature Flags (`@objectstack/platform-objects`, entry `nav_settings_feature_flags` and its four locale labels).
18+
- **Stored values.** A `sys_setting` row saved under `feature_flags` and an `OS_FEATURE_FLAGS_*` environment variable are left in place and become inert: nothing registers the namespace, so nothing resolves them. They are not cleaned up; delete them at will.
19+
20+
The one-line fix for a host:
21+
22+
- FROM `featureFlagsSettingsManifest` TO nothing: drop it from the host's `SettingsServicePlugin({ manifests })` list; `builtinSettingsManifests` no longer carries it.

‎content/docs/protocol/kernel/config-resolution.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ derived mechanically from `(namespace, key)` by `envKeyOf`: uppercase, `.` and
247247
# namespace 'crm', key 'api_key'
248248
OS_CRM_API_KEY=sk_live_...
249249

250-
# namespace 'feature_flags', key 'ai-enabled'
251-
OS_FEATURE_FLAGS_AI_ENABLED=true
250+
# namespace 'ai', key 'log-prompts'
251+
OS_AI_LOG_PROMPTS=true
252252
```
253253

254254
The raw string is coerced by the **type of the specifier's default**

‎content/docs/ui/apps.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ The Setup app's anchors:
870870
| `group_people_org` | Users, organizations, business units, teams, invitations |
871871
| `group_access_control` | Positions, permission sets, sharing rules, API keys |
872872
| `group_approvals` | Approvals inbox, requests, history, delegations |
873-
| `group_configuration` | Settings, localization, exchange rates, authentication, email, storage, AI, feature flags, branding |
873+
| `group_configuration` | Settings, localization, exchange rates, authentication, email, storage, AI, branding |
874874
| `group_diagnostics` | Sessions, notification events, audit log |
875875
| `group_integrations` | Webhooks, cloud connection |
876876
| `group_advanced` | OAuth applications |

‎docs/qa/platform-checklist/areas/platform-core.json‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@
632632
"as admin, GET /api/v1/meta/app?id=setup and extract every merged nav destination — Setup is a SHELL of empty group anchors (packages/platform-objects/src/apps/setup.app.ts: group_overview/apps/people_org/access_control/approvals/configuration/diagnostics/integrations/advanced) filled by SETUP_NAV_CONTRIBUTIONS (setup-nav.contributions.ts) plus capability-plugin contributions (plugin-security Positions/Permission Sets, plugin-sharing Sharing Rules/Record Shares, plugin-approvals, plugin-webhooks)",
633633
"GET /api/v1/meta/app with NO query first and record the names actually served — a stock boot answers exactly showcase_app, setup, account. Then GET /api/v1/meta/app?id=account (account.app.ts — Profile + Inbox/Security/Developer groups). ⚠️ GET /api/v1/meta/app?id=studio answers 200 {\"items\":[]} on every stock boot and that is the expected result (knownGap): record the absence as an assertion and move on — do NOT install @objectstack/studio to make the step runnable",
634634
"as admin, hand-walk each destination in each app. At a window narrower than 1416 px (1280 px included) the console folds the sidebar to its icon rail, and Setup and Account open that way (ui/packages/app-shell/src/layout/consoleSidebarRail.ts CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH, objectstack#27593; QA run #29877 met it at 1280 px): press the sidebar toggle at the app bar's left edge (Ctrl/⌘+B) to unfold it before expanding any group. EXPAND every nav group first: some render collapsed by default — Setup's Advanced group and Account's Developer group declare `expanded: false` (packages/platform-objects/src/apps/setup.app.ts, account.app.ts), and QA run #24557 found Setup's Configuration group collapsed as well — so a walk that never expands them never reaches their entries. Then navigate, wait for render, SCREENSHOT FIRST, then read the DOM — assert no pageerror, no 'failed to load', no blank <main>, no 'no actions configured' placeholder",
635-
"Setup detail: confirm each settings URL entry (nav_settings_* → /apps/setup/system/settings/<namespace>) opens the settings namespace form — the 11 namespaces of a stock single-posture boot, localization/company/branding/auth/mail/sms/storage/ai/knowledge/lifecycle/feature_flags: the nav_settings_* entries of packages/platform-objects/src/apps/setup-nav.contributions.ts, plus nav_settings_branding, which packages/services/service-settings/src/settings-service-plugin.ts contributes only where no organization wall stands, and Users / Organization / Business Units / Teams / Sessions / OAuth Applications render. User preferences and identity links are not Setup destinations: open a user from Users and confirm the user page's Preferences and Linked Accounts tabs render that user's rows",
635+
"Setup detail: confirm each settings URL entry (nav_settings_* → /apps/setup/system/settings/<namespace>) opens the settings namespace form — the 10 namespaces of a stock single-posture boot, localization/company/branding/auth/mail/sms/storage/ai/knowledge/lifecycle: the nav_settings_* entries of packages/platform-objects/src/apps/setup-nav.contributions.ts, plus nav_settings_branding, which packages/services/service-settings/src/settings-service-plugin.ts contributes only where no organization wall stands, and Users / Organization / Business Units / Teams / Sessions / OAuth Applications render. User preferences and identity links are not Setup destinations: open a user from Users and confirm the user page's Preferences and Linked Accounts tabs render that user's rows",
636636
"Studio detail is NOT walked from here — the app is not registered on a stock boot (knownGap), so there is no merged nav to render. Its metadata:resource lists and component surfaces (App Builder, All Metadata Types, Packages, API Console, Flow Runs, Public Forms) are reached through the console surface at /_console/studio/<pkg>/<pillar> and are covered by area studio-authoring. What THIS item asserts about Studio is the absence itself, per clause 6",
637637
"Account detail: confirm Profile (account:profile_card), Notifications (sys_inbox_message/mine), Approvals (nav_account_approvals, the approvals:inbox component, served as …/component/approvals/inbox — the Approvals Inbox, not the sys_approval_request grid; account.app.ts), Linked Accounts (sys_account), Active Sessions (sys_session/mine), API Keys (sys_api_key/mine), OAuth Applications (sys_oauth_application/mine) and Connect an Agent (nav_connect_agent) each render — 8 Account destinations and 46 Setup destinations for the admin on a stock single-org showcase boot, as QA run #29877 read them at 589049b0 and a fresh showcase boot re-read them at 232d3bb5ef (GET /api/v1/meta/app leaves, which match the nav source minus its gates). Since QA run #28545's reading Setup's Packaged automation page retired under ADR-0146 D11, leaving Packaged Actions (nav_packaged_actions, automation:packaged-actions; #28785), and its unfiltered Packages entry moved to Studio (#29547) (47 for #28927, 50 at eeec88d1 for QA run #28545, 48 re-measured at 3fd80337 for #28338, QA run #27481 read 44 and 8, QA run #24557 read 42 at 5b14b7f3, 45 on 2026-10-01, and #9453 recorded 43 and 7). Recount from the payload each run",
638638
"confirm the gated entries resolve to ABSENT-not-erroring for the admin: nav_organizations (requiresService org-scoping) is absent in single-org mode; nav_jwks Signing Keys is ABSENT for EVERY persona, admin included — the entry was removed in #7544 because sys_jwks declares apiEnabled:false, so its list answers OBJECT_API_DISABLED (404) for all callers and no permission gate could prune it; SSO Providers is absent unless OS_SSO_ENABLED (knownGap)",
@@ -785,15 +785,15 @@
785785
"fixtures": {
786786
"app": "showcase",
787787
"requires": [
788-
"the 11 open-framework settings namespaces a stock boot serves: the 10 in builtinSettingsManifests (packages/services/service-settings/src/manifests: localization, company, branding, auth, mail, sms, storage, ai, knowledge, feature_flags) PLUS lifecycle (5 specifiers), which is NOT in that aggregate — ObjectQLPlugin registers it at kernel:ready whenever a SettingsService is present (packages/objectql/src/lifecycle/lifecycle-settings.ts). Enumerating only the manifests directory undercounts by one",
788+
"the 10 open-framework settings namespaces a stock boot serves: the 9 in builtinSettingsManifests (packages/services/service-settings/src/manifests: localization, company, branding, auth, mail, sms, storage, ai, knowledge) PLUS lifecycle (5 specifiers), which is NOT in that aggregate — ObjectQLPlugin registers it at kernel:ready whenever a SettingsService is present (packages/objectql/src/lifecycle/lifecycle-settings.ts). Enumerating only the manifests directory undercounts by one",
789789
"the settings service wired with a data engine so it swaps from in-memory to the sys_setting table, and with the sys_secret store + auditWriter (SettingsServicePlugin binds these on kernel:ready; the showcase stack mounts the engine)"
790790
],
791791
"knownGaps": [
792792
"HONORING the ai.json caveat: a saved settings FORM is NOT proof of effect — the AI namespace's test action is a stub validator until @objectstack/service-ai is mounted, and a saved ai.provider does nothing observable without it. The effect clause therefore uses localization, whose consumer (resolve-authz-context.ts reads localization.timezone/locale/currency onto every ExecutionContext) is stock and observable; branding.workspace_name in the shell is the browser fallback. Never tick effect off a persisted form."
793793
]
794794
},
795795
"steps": [
796-
"as admin, GET /api/settings — capture the visible manifests (the 11 open-framework namespaces above; the route is the SettingsService REST surface in settings-routes.ts)",
796+
"as admin, GET /api/settings — capture the visible manifests (the 10 open-framework namespaces above; the route is the SettingsService REST surface in settings-routes.ts)",
797797
"pick localization (an OBSERVABLE-CONSUMER namespace — packages/core/src/security/resolve-authz-context.ts reads localization.timezone/locale/currency onto ExecutionContext). GET /api/settings/localization — capture {manifest, values}; note the timezone value and its source. On an `os dev --seed-admin` boot the dev seed has ALREADY written the host's zone (UTC in these containers) at tenant scope (packages/plugins/plugin-auth/src/auth-plugin.ts, #23175), so source reads 'tenant' before any PUT; only a boot without the dev seed starts at 'default'",
798798
"change it: PUT /api/settings/localization { timezone: 'Asia/Tokyo' }; re-read GET /api/settings/localization and confirm the value persists and source is the scope, 'tenant' (localization scope is 'tenant'): on a dev-seeded boot the source already read 'tenant', so the value is what round-trips; on an unseeded boot the source flips from 'default'. For the console badge (steps[5]) pick a localization key the dev seed did NOT write (e.g. default_country), so its source starts at 'default'",
799799
"prove EFFECT (not just the saved form): re-derive an ExecutionContext-dependent output and confirm it now resolves against the saved zone, not UTC. Two stock consumers serve: GET /api/v1/auth/me/localization (the resolved timezone the session carries), and a datetime the console renders — e.g. showcase_field_zoo's f_datetime on its record page, which shifts by exactly the zone offset. ⛔ The analytics date bucket has no stock observable (the only date-typed time dimension is due_date, and the seeded admin holds no export grant), so do not reach for it. If no localization effect is observable at all, fall back to branding.workspace_name visible in the console shell — but never tick effect off the form alone",
@@ -882,7 +882,7 @@
882882
"evidence": "the two 401 bodies"
883883
},
884884
{
885-
"clause": "each of the 11 open-framework namespaces resolves GET /api/settings/:ns with its manifest + values — no namespace 500s or serves an empty manifest",
885+
"clause": "each of the 10 open-framework namespaces resolves GET /api/settings/:ns with its manifest + values — no namespace 500s or serves an empty manifest",
886886
"oracle": "api",
887887
"verify": "one GET per variant in variants[]; each returns {manifest, values} with the manifest's specifiers",
888888
"evidence": "the per-namespace responses"
@@ -907,8 +907,7 @@
907907
"sms",
908908
"storage",
909909
"ai",
910-
"knowledge",
911-
"feature_flags"
910+
"knowledge"
912911
],
913912
"traps": ["stale-console-bundle", "dispatcher-vs-hono-route"],
914913
"source": [

‎packages/cli/README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ service plugins for every preset **except** `--preset minimal`:
433433
| `queue` | `QueueServicePlugin` | Background retries (mail, audit batching) need a queue. |
434434
| `job` | `JobServicePlugin` | Scheduled jobs (cleanup, reports, cron). |
435435
| `cache` | `CacheServicePlugin` | In-memory adapter; performance baseline. |
436-
| `settings` | `SettingsServicePlugin` | Mail / storage / branding / feature-flag UI + persistence. |
436+
| `settings` | `SettingsServicePlugin` | Mail / storage / branding settings UI + persistence. |
437437
| `email` | `EmailServicePlugin` | Auth callbacks (verify, reset, magic-link) hard-depend. |
438438
| `storage` | `StorageServicePlugin` | Avatars, attachments, exports. Local fallback in dev. |
439439

@@ -442,8 +442,8 @@ Apps no longer need to list these in `requires: [...]`.
442442
**Opt-out:** run `objectstack serve --preset minimal` to skip the slate
443443
and only load core + your explicit `requires`.
444444

445-
**Built-in Settings manifests:** `mail`, `storage`, `branding`,
446-
`feature_flags` are pre-registered. Both `mail` and `storage` expose a
445+
**Built-in Settings manifests:** `mail`, `storage` and `branding`
446+
are among those pre-registered. Both `mail` and `storage` expose a
447447
"Test connection" action that exercises the live transport / adapter
448448
end-to-end. Switching the storage adapter does **not** migrate
449449
previously uploaded files — the plugin logs a warning on every swap.

‎packages/cli/test/connect-agent-both-halves-wire.pin.test.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,16 @@ describe('#17647 — Connect-an-Agent visibility: BOTH halves, over the wire', (
254254
.toContain('nav_connect_agent');
255255
// The Setup tree the platform admin is served BY THIS COMPOSITION: 9
256256
// authored groups less `group_approvals`, which nothing contributes
257-
// into and the filter collapses, plus the 24 entries
257+
// into and the filter collapses, plus the 23 entries
258258
// `SETUP_NAV_CONTRIBUTIONS` carries, plus the MCP one. (25 until
259-
// #25098 moved the unfiltered Packages entry to Studio.)
259+
// #25098 moved the unfiltered Packages entry to Studio; 24 until
260+
// #30204 removed the Feature Flags entry with its namespace.)
260261
// `nav_settings_branding` is not among them, and not because of a
261262
// tenancy wall (this harness boots none): since #24766 it is
262263
// contributed by `@objectstack/service-settings` together with the
263264
// `branding` namespace, offered only where no organization wall stands,
264265
// and this harness composes platform-objects and the MCP bundle alone.
265-
expect(allowedIds).toHaveLength(33);
266+
expect(allowedIds).toHaveLength(32);
266267
expect(allowedIds).not.toContain('nav_settings_branding');
267268
// The two per-user stores are reached from the User record page, not
268269
// from Setup's menu (#23803).
@@ -285,8 +286,8 @@ describe('#17647 — Connect-an-Agent visibility: BOTH halves, over the wire', (
285286
expect(withAccountHalf.statusCode).toBe(200);
286287
expect(withoutAccountHalf.statusCode).toBe(200);
287288
expect(navIds(withAccountHalf.body?.item)).toEqual(navIds(withoutAccountHalf.body?.item));
288-
// 33: the composition the count in the test above states.
289-
expect(navIds(withAccountHalf.body?.item)).toHaveLength(33);
289+
// 32: the composition the count in the test above states.
290+
expect(navIds(withAccountHalf.body?.item)).toHaveLength(32);
290291

291292
// The mechanism behind the invariance, asserted rather than assumed:
292293
// the bundle aims exactly one contribution at `setup` and one at

‎packages/mcp/src/connect-ui.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* entry stays for admins. ⛔ Ungating Setup is NOT the fix and was measured:
3232
* the app-level `setup.access` gate fires BEFORE the group gate, so dropping
3333
* the group gate alone changes nothing, and dropping both serves 14+ unrelated
34-
* Setup surfaces (Users, Organization, Branding, Feature Flags, …) to every
34+
* Setup surfaces (Users, Organization, Branding, Company, …) to every
3535
* signed-in user — far past what this card asks for. ⛔ Nor is a
3636
* `requiresService: 'mcp'` gate on an `account.app.ts` entry: the `mcp` service
3737
* registers unconditionally in `init()` while this bundle registers behind

‎packages/platform-objects/src/apps/setup-nav.contributions.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ export const SETUP_NAV_CONTRIBUTIONS: NavigationContribution[] = [
154154
// `lifecycle` is `@objectstack/objectql`'s manifest (ADR-0057), offered
155155
// wherever a settings service is mounted, like the entries above.
156156
{ id: 'nav_settings_lifecycle', type: 'url', label: 'Data Lifecycle', url: '/apps/setup/system/settings/lifecycle', icon: 'timer', requiredPermissions: ['manage_platform_settings'] },
157-
{ id: 'nav_settings_feature_flags', type: 'url', label: 'Feature Flags', url: '/apps/setup/system/settings/feature_flags', icon: 'flag' },
158157
],
159158
},
160159
{

‎packages/platform-objects/src/apps/translations/en.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3388,7 +3388,7 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
33883388
},
33893389
namespace: {
33903390
label: "Namespace",
3391-
help: "Manifest namespace (e.g. mail, branding, feature_flags)."
3391+
help: "Manifest namespace (e.g. mail, branding, storage)."
33923392
},
33933393
key: {
33943394
label: "Key",

‎packages/platform-objects/src/apps/translations/en.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ export const en: TranslationData = {
129129
nav_settings_ai: { label: 'AI & Embedder' },
130130
nav_settings_knowledge: { label: 'Knowledge' },
131131
nav_settings_lifecycle: { label: 'Data Lifecycle' },
132-
nav_settings_feature_flags: { label: 'Feature Flags' },
133132
// Notifications (contributed by @objectstack/service-messaging)
134133
nav_notification_preferences: { label: 'Notification Preferences' },
135134
nav_notification_templates: { label: 'Notification Templates' },

0 commit comments

Comments
 (0)