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 5448585
Browse filesBrowse the repository at this point in the historyBrowse files
fix(service-settings): Settings help in business words; Country is a picker that shows names and stores the code (#28063)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DHDem82MKCs3q2zQkEAkpqZ)_Fixes#27853Fixes#27867
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:设置页(认证、功能开关、邮件)的帮助文字改成业务用语,不再出现环境变量名、选项键、错误码、对象 API
名或代码调用;继承值徽标从「租户」改为「组织」;Webhooks
副标题改写;公司「国家」与本地化「默认国家」改为显示国名、存储代码的选择器。四个语言包同步。
- **为什么改**:管理员在 Setup → Settings
看到的是像代码注释的说明(`invite_only`、`OS_AUTH_REQUIRE_EMAIL_VERIFICATION`、`defineWebhook()`),国家要手打两位大写代码,输入「de」或「Germany」都被拒且没有提示。
- **风险与回滚**:低。只改文字与国家字段的控件类型;国家仍以 `iso_3166_alpha2` 值域校验,接受的值集合不变(`DE`
照收,`de`、`ZZ` 照拒)。回滚 = revert 本 PR。
-
**先看**:`packages/services/service-settings/src/manifests/company.manifest.ts`(国家选项从共享的
249
个代码生成);`packages/services/service-settings/src/manifests/auth.manifest.ts`(帮助改写);`packages/services/service-settings/src/settings-help-business-words.pin.test.ts`(形状钉子)。
- **两分钟验证**:`pnpm --filter @objectstack/service-settings exec vitest run
src/settings-help-business-words.pin.test.ts
src/manifests/country-picker.pin.test.ts`,预期全绿;或打开 Setup →
Company,在「国家」输入 Germ,列表出现「Germany (DE)」,选中后保存为 DE。
- **需要你做的**:无,席位落地。
## What changes
**#27853 — Settings help in business words.** Every string an
administrator is shown for Authentication, Feature Flags and Mail (the
manifest literals, which are the English fallback, and the en / zh-CN /
ja-JP / es-ES packs) now names the effect instead of the machine value:
- Self-registration audience: the help says Invitation only /
Allowlisted email domains / Open (the labels the page shows), not the
stored option keys. The email-verification override, the Google client
ID and secret, and the reverse-proxy trust switch are described as "set
in the server configuration" instead of by env var name or stack-config
path. The API-key help no longer quotes error codes; the permission-set
help no longer quotes permission-set API names.
- Feature Flags beta banner: no env var, no code span.
- Mail: the durable-queue help no longer names the queue object. The
Send-test-email result for a log-only provider
(`packages/plugins/plugin-email/src/email-plugin.ts`, the two message
strings only) says "kept in the email log" instead of naming the mail
object.
- The inherited-value badge (`settingsCommon.sourceLabels.tenant`) reads
Organization / 组织 / 組織 / Organización.
- The Webhooks subtitle (`sys_webhook` description,
`packages/plugins/plugin-webhooks/src/sys-webhook.object.ts`) says what
a webhook is without the code calls or the internal component names; the
code-level account stays in the comment beside it. The en bundle is
regenerated with `node scripts/check-i18n-bundles.mjs --write`; the
zh-CN / ja-JP / es-ES values are hand-translated leaves.
The technical detail the help used to inline is already in the
deployment docs (`content/docs/deployment/environment-variables.mdx`
names the email-verification override). The settings manifest has no
per-field docs-link slot, so the help now carries no technical detail
rather than a link.
**#27867 — Country pickers.** Company → Country and Localization →
Default country change from `text` to `select`. Their `options` are
every member of the `iso_3166_alpha2` value domain, generated from the
shared `ISO_3166_ALPHA2_CODES` (`@objectstack/spec/shared`) with names
from `Intl.DisplayNames`, never a hand-kept list; each locale pack names
the same 249 codes in its own language through
`countryOptionLabels(locale)`. The `valueDomain` declaration stays, so
it remains the enforcement boundary and the accepted set is unchanged.
The console renders a domain-bearing `select` as its suggest-and-pick
combobox: typing a name lists the named options, and picking one stores
the code. `pattern` / `minLength` / `maxLength` stay on the specifier,
so the server's verdict for every value is the same as before.
**Premise correction for #27867.** Company has no Locale or Timezone
setting; it has only Country. Locale and Timezone live on Localization,
where `locale` is already a closed picker with language names and
`timezone` is the suggest-any-IANA-zone combobox with named suggestions,
so neither changes here. Localization's `default_country` had the same
two-letter free-text shape as Company's country and gets the same picker
(file surface amended on the claim).
## Tests
- `src/settings-help-business-words.pin.test.ts` (new): for auth,
feature_flags and mail, the manifest literals and every shipped pack
carry no env var or error code, enum key or object API name, camelCase
code identifier, code call or code span; a teeth case proves each shape
catches the string it was written for. Every pack's tenant source label
is not "tenant" in any of the four languages.
- `src/manifests/country-picker.pin.test.ts` (new): both keys are
`select` with `valueDomain: 'iso_3166_alpha2'` after the spec parse;
their options equal the 249 domain codes, each named with its code; a
stored `DE` round-trips through `SettingsService` and is one of the
options; `ZZ` and `de` are still refused with `value_domain` and
`Germany` is still refused; each pack names all 249 codes.
-
`packages/plugins/plugin-webhooks/src/sys-webhook.description.pin.test.ts`
(new): the source description and the four bundles carry no code call,
camelCase identifier, object API name or internal component name.
- `src/manifests/auth.manifest.test.ts`: the two pins that held the old
wording now hold the business wording (the sign-up help names each
posture by its label and spells no key; the Google help names the server
configuration and no env var).
## Acceptance notes
- The console shows a stored country code (`DE`) in the combobox text
box, with the named options in its list. Showing the stored code as its
name inside the box is a `ui/` change to `DomainCombobox`
(`ui/apps/console/src/pages/settings/SettingsField.tsx`), outside this
PR's file surface; reported to the seat for a follow-up card.
- The console's own fallback for the source badge
(`ui/apps/console/src/pages/settings/useSettingsLabel.ts`, used only
when no server bundle loads) still reads "Tenant"; outside this file
surface.
- AI & Embedder help still names env vars and `sys_ai_trace` (parent row
2 lists it); not in this card's three namespaces; reported to the seat.
## Verification
All readings at head `b7cfa217` (base `origin/main` `5aee2065`).
<details><summary>Package tests and typecheck (under the shared verify
lock)</summary>
- `@objectstack/service-settings`: Test Files 64 passed (64), Tests 1053
passed (1053); `typecheck` exit 0.
- `@objectstack/plugin-webhooks`: Test Files 25 passed (25), Tests 258
passed (258); `typecheck` exit 0.
- `@objectstack/plugin-email`: Test Files 45 passed (45), Tests 749
passed (749); `typecheck` exit 0.
</details>
<details><summary>Ablations (scripts/ablation-replace.mjs, restored by
blob hash)</summary>
- `company.manifest.ts` country `type: 'select'` mutated to `'text'`:
`country-picker.pin.test.ts` 1 failed / 16 passed (company.country is a
select that keeps the iso_3166_alpha2 domain); restored blob `0ee79213`
matches HEAD.
- en pack Feature Flags banner given `OS_FEATURE_FLAGS_AI_ENABLED`:
`settings-help-business-words.pin.test.ts` 1 failed / 19 passed
(feature_flags: the en pack carries no developer-only value); restored
blob `eaba9945` matches HEAD.
</details>
<details><summary>Gate families (dispatch-gates --commands, reconciled
with --ran)</summary>
- 62 families derived; 61 run with exit 0; 1 NOT MEASURED: `pnpm
check:dual-build-cjs-loads` exits 3 (PREREQUISITE NOT MET: needs every
package's dist), left to CI.
- `check-plugin-teardown-shape --self-test` first exited 3 in the
shallow clone; after fetching its pinned fixture commit it exits 0 (48
cases).
- `check:i18n` exit 0 after `node scripts/check-i18n-bundles.mjs
--write` (only `plugin-webhooks` en bundle moved).
- `check:dts-closure` and `check:sourcemap-no-sources-content` swept the
62 built packages only; CI sweeps the whole tree.
- `pnpm lint` exit 0.
- Reconcile: `62 derived famil(ies) accounted for — 61 run, 1
NOT-MEASURED`.
</details>
Not browser-driven: the combobox rendering was read from
`SettingsField.tsx`, not re-driven in Chromium.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4d1MApr9y6yjfjdVYmA)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(service-settings): Setup → Settings help says what a setting does in business words, and Country is a picker that shows names and stores the code
8
+
9
+
Clause-②: no
10
+
Metadata-protocol: none
11
+
12
+
An administrator in Setup → Settings read help written like code comments. It now names the effect, and the technical detail stays in the deployment docs.
13
+
14
+
-**Authentication.** The help names the Self-registration audience by the labels the page shows (Invitation only, Allowlisted email domains, Open), not by their stored keys. It no longer quotes env var names (the email-verification override, the Google client ID and secret), the stack-config paths, the API-key error codes or permission-set API names; it says "set in the server configuration" instead.
15
+
-**Feature Flags.** The beta banner says a feature set in the server configuration is locked for the whole deployment, without quoting an env var.
16
+
-**Mail.** The durable-queue help no longer names the queue's object, and the Send-test-email result for a log-only provider says the message was "kept in the email log" (`@objectstack/plugin-email`).
17
+
-**Webhooks.** The `sys_webhook` description, shown as the Webhooks subtitle, says what a webhook is without `defineStack` / `defineWebhook()` or the auto-enqueuer (`@objectstack/plugin-webhooks`).
18
+
-**The inherited-value badge** names the layer "Organization" (组织 / 組織 / Organización) instead of "Tenant".
19
+
-**Country pickers.** Company → Country and Localization → Default country are `select`s whose options are every ISO 3166-1 alpha-2 code with its name, in each shipped locale (`Germany (DE)`, `德国 (DE)`). The stored value is still the code, a stored code is one of the options, and the `iso_3166_alpha2` value domain stays the enforcement boundary, so the set of accepted values is unchanged. The names come from `Intl.DisplayNames`; the codes come from the shared `ISO_3166_ALPHA2_CODES`.
20
+
21
+
Every shipped pack (en, zh-CN, ja-JP, es-ES) carries the same wording.
// [ADR-0131 D2] A row is an organization's own subscription; a declared
57
57
// webhook is read from the metadata registry (`declared-webhooks.ts`) and
58
-
// writes no row. The description says so to every Setup/Studio reader.
59
-
description: 'Outbound HTTP webhook subscription authored by an organization in the Studio editor or through the API. A webhook declared in code via defineStack({ webhooks }) / defineWebhook() is read from the metadata registry and writes no row here. Both are dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.',
58
+
// writes no row. The description says so to every Setup/Studio reader, in
59
+
// an administrator's words: a webhook declared in code (`defineStack({
60
+
// webhooks})` / `defineWebhook()`) is the one "built into an app", and the
61
+
// "delivery queue" is the auto-enqueuer's shared HTTP outbox.
62
+
description: 'An outbound HTTP webhook your organization creates here or through the API. Webhooks that come built into an app are not listed here. Both kinds are sent through the same delivery queue.',
description: "Outbound HTTP webhook subscription authored by an organization in the Studio editor or through the API. A webhook declared in code via defineStack({ webhooks }) / defineWebhook() is read from the metadata registry and writes no row here. Both are dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.",
21
+
description: "An outbound HTTP webhook your organization creates here or through the API. Webhooks that come built into an app are not listed here. Both kinds are sent through the same delivery queue.",
description: "Suscripción saliente de Webhook HTTP creada por una organización con el editor de Studio o mediante la API. Un webhook declarado en código mediante defineStack({ webhooks }) / defineWebhook() se lee del registro de metadatos y no escribe ninguna fila aquí. Ambos los despacha el encolador automático de webhooks al outbox HTTP compartido.",
21
+
description: "Webhook HTTP saliente que su organización crea aquí o mediante la API. Los webhooks incluidos en una aplicación no aparecen aquí. Ambos tipos se envían por la misma cola de entrega.",
Copy file name to clipboardExpand all lines: packages/services/service-settings/src/manifests/auth.manifest.ts
+10-10Lines changed: 10 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -45,7 +45,7 @@ const manifest = {
45
45
required: false,
46
46
default: true,
47
47
description:
48
-
'Opens the email-and-password sign-up form. Who it admits is decided by the Self-registration audience below: under invite_only, only an address holding a pending invitation; under email_domain, the allowlisted domains and invited addresses; under open, anyone. Off closes the form under every audience, invitations included.',
48
+
'Opens the email-and-password sign-up form. Who it admits is decided by the Self-registration audience below: under Invitation only, only an address holding a pending invitation; under Allowlisted email domains, the allowlisted domains and invited addresses; under Open, anyone. Off closes the form under every audience, invitations included.',
{value: 'open',label: 'Open — anyone may self-register'},
114
114
],
115
115
description:
116
-
'invite_only closes self-registration: users come into existence only through an operator-side act (invitation, admin create/import, SCIM, enterprise SSO). email_domain opens it to the allowlisted domains below. open admits anyone. Both email_domain and open require the self-registration permission set below. email_domain always forces email verification on. open forces it on unless the deployment turns it off (OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false, or emailAndPassword.requireEmailVerification: false in the stack config); a false saved in this console is refused under open.',
116
+
'Invitation only closes self-registration: people become users only when an administrator invites, creates or imports them, or through directory provisioning (SCIM) or enterprise single sign-on. Allowlisted email domains opens it to the domains listed below. Open admits anyone. Both Allowlisted email domains and Open need the self-registration permission set below. Allowlisted email domains always requires email verification. Open requires it unless the server configuration turns it off; turning it off in this console is refused under Open.',
117
117
},
118
118
{
119
119
type: 'textarea',
@@ -130,7 +130,7 @@ const manifest = {
130
130
label: 'Self-registration permission set',
131
131
required: false,
132
132
description:
133
-
'Name of the permission set granted to each self-registrant (declaring member_default explicitly is fine; admin_full_access is refused). Required whenever the posture permits self-registration.',
133
+
'Name of the permission set each self-registered user receives. The standard member set is fine; the full administrator set is refused. Required whenever the audience allows self-registration.',
'CIDR ranges or exact IPs (one per line, or comma-separated), e.g. 203.0.113.0/24. When set, sign-in from outside these ranges is rejected, and so is any request whose client address the server cannot establish. Empty = no restriction. The address judged is the connection\'s peer; behind a reverse proxy, declare server.trustProxy in the stack so the forwarded client address is judged instead.',
372
+
'CIDR ranges or exact IPs (one per line, or comma-separated), e.g. 203.0.113.0/24. When set, sign-in from outside these ranges is rejected, and so is any request whose client address the server cannot establish. Empty = no restriction. The address judged is the one the connection comes from; behind a reverse proxy, the server must be configured to trust that proxy so the original client address is judged instead.',
373
373
},
374
374
// [#23367, ruling A] The API-key lifecycle policy. Two readers, one
375
375
// source: `bindAuthSettings` binds these into the auth service
@@ -405,7 +405,7 @@ const manifest = {
405
405
min: 1,
406
406
max: 3650,
407
407
description:
408
-
'The furthest expiry date a new key may ask for, counted from its creation. A longer one is refused (API_KEY_EXPIRY_TOO_LONG); exactly this many days is accepted.',
408
+
'The furthest expiry date a new key may ask for, counted from its creation. A longer one is refused; exactly this many days is accepted.',
409
409
},
410
410
{
411
411
type: 'number',
@@ -416,7 +416,7 @@ const manifest = {
416
416
min: 0,
417
417
max: 3650,
418
418
description:
419
-
'A key with no recorded use for this many days is refused (API_KEY_INACTIVE). It is not revoked; create a new key. A key never used counts from its creation, or from the policy start below when it is older. 0 turns inactivity expiry off.',
419
+
'A key with no recorded use for this many days is refused. It is not revoked; create a new key. A key never used counts from its creation, or from the policy start below when it is older. 0 turns inactivity expiry off.',
420
420
},
421
421
{
422
422
type: 'text',
@@ -434,7 +434,7 @@ const manifest = {
434
434
label: 'Social sign-in',
435
435
required: false,
436
436
description:
437
-
'Configure the built-in Google sign-in provider. Deployment env vars still win.',
437
+
'Configure the built-in Google sign-in provider. Credentials set in the server configuration still win.',
438
438
},
439
439
// Google is offered only when this is on AND a client ID and secret both
440
440
// resolve (saved here, or GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, which
@@ -458,15 +458,15 @@ const manifest = {
458
458
required: false,
459
459
default: true,
460
460
description:
461
-
'Offers Google sign-in once a client ID and a client secret both resolve: saved below, or GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET on the server, which win over saved values. Until both resolve, the login page shows no Google button, even while this is on. Off withdraws Google sign-in even when the server sets both. Create the OAuth client in Google Cloud Console.',
461
+
'Offers Google sign-in once a client ID and a client secret are both available: saved below, or set in the server configuration, which wins over saved values. Until both are available, the login page shows no Google button, even while this is on. Off withdraws Google sign-in even when the server sets both. Create the OAuth client in Google Cloud Console.',
462
462
},
463
463
{
464
464
type: 'text',
465
465
key: 'google_client_id',
466
466
label: 'Google client ID',
467
467
required: false,
468
468
description:
469
-
'OAuth client ID from Google Cloud Console. GOOGLE_CLIENT_ID can also be set on the server.',
469
+
'OAuth client ID from Google Cloud Console. It can also be set in the server configuration.',
470
470
visible: "${data.google_enabled !== false}",
471
471
},
472
472
{
@@ -476,7 +476,7 @@ const manifest = {
476
476
required: false,
477
477
encrypted: true,
478
478
description:
479
-
'Stored encrypted at rest. GOOGLE_CLIENT_SECRET can also be set on the server.',
479
+
'Stored encrypted at rest. It can also be set in the server configuration.',
0 commit comments