Repository navigation
feat(console)!: an administrator's first-run checklist on Home replaces the currency and time zone dialog - #30868
Conversation
…s the currency and time zone dialog (#30206) The dialog that asked for the system timezone and default currency on the first page an administrator opened is gone. Its questions, and the other things a new workspace needs, are rows of a checklist at the top of Home: organization name, default language and country, system timezone, default currency, email delivery, and invite colleagues. Each row's state is read from the real setting; the localization rows edit in place with the Settings page's own fields, pre-filled from the browser and written only on the row's Confirm. Members see nothing; the close button hides it for that administrator in that workspace on that device. A country typed by name into a domain-bearing picker now commits the option's code (「中国」 → CN), and an invalid_format refusal reads in the console's words and the reader's language on the Settings page and the checklist. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
…国家/地区 (#30206) Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
… in its mock (#30206) Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
… workspace prompt (#30206) The console no longer opens the timezone and currency dialog, so the showcase smoke's global setup no longer looks for it. Before, with the currency unset, it waited 60 seconds for a dialog that never opens and then warned. Sign-in and the storageState it writes are unchanged. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
…eys as a narrowing (#30206) The ten published @object-ui/i18n packs drop the console.workspaceTimezonePrompt.* keys, so the declaration's arm is narrowing (the new keys stay named as its widening half), the summary carries the bang, and the body gains the BREAKING note, the removed-key table and the ADR-0087 runtime-interface-only disposition. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
Conflicts, each two neighbouring additions, both kept: the ten locale packs (main's console.settingsView.clientAddress* beside this branch's invalidFormat*), and de-quote-pairing-3876, whose counts now add main's one pair (#30323) and this branch's two: 107 matched spans, 107/107/0. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
…st's measured 203 bytes (#30206) Build & E2E UI went red on cda63e7: i18n-locale-en measured 60,138 gzipped bytes against a 60,000 ceiling. A control build of the PR base (main 48b3ae6, with ui/apps/console, ui/packages/i18n and ui/e2e restored to it) measures 59,935, so this branch adds 203 bytes: the first-run checklist copy and the invalid_format sentences, net of the retired prompt keys. Trimming the new copy saves at most 122 bytes, less than the 138 over, so the ceiling and PER_CHUNK_BASELINE both move by exactly 203. Headroom stays at the 65 bytes main had. The aggregate ceiling and every other key are unchanged. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
… checklist's measured 203 bytes (#30206)" This reverts commit 10e7823. The lane is not raising the i18n-locale-en ceiling: #30588 moves the marketplace.* copy into a lazily loaded namespace, which brings the eager en catalogue back under 60,000 once it lands and this branch merges main. Claude-Session: https://claude.ai/code/session_01L6bhgVwQZbHRRM9CaXeKif Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp Contract-review-claim: session_01L9qibxW6qraU2Z3mwupUGp · head e823d8f · 2026-11-06T07:28Z |
|
domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing removes only keys the retired dialog read: the head tree and hotcrm hold no reader of Implemented-by: VERDICT: PASS |
Requested by Tesla · project thread
Fixes #30206
Clause-②: yes (narrowing) — the published
@object-ui/i18npacks (all ten) drop the 14console.workspaceTimezonePrompt.*keys, which had no reader outside the removed dialog. The widening half: new console i18n keysconsole.firstRunChecklist.*andconsole.settingsView.invalidFormat/.invalidFormatHelp/.invalidFormatPick(all ten packs)Metadata-protocol: none
Supersedes #30552 (same change, taken over from a branch whose account is out of tokens: cut from
claude/project-thread-5murn7@2dc81e70, thenorigin/mainmerged).维护者速读
ui/apps/console/src/pages/settings/FirstRunChecklist.tsx(读取与只在确认时写入)、firstRunChecklistLogic.ts(每行何时算完成)、SettingsField.tsx的optionByName(输入名称匹配代码)。os dev --fresh后用管理员登录,打开 /home,顶部应有清单;中文浏览器下点「默认语言和国家/地区」的「设置」,在国家框输入「中国」后点「确认」,应提示已保存、该行打勾,Setup 页不再弹任何对话框。What changes
The dialog is gone; Home carries an administrator's first-run checklist.
WorkspaceTimezonePrompt.tsxand its mount inAppContent.tsxare removed.FirstRunChecklistis drawn at the top of Home by the console's Home route (App.tsx), above the page's own sections, inside theConsolePermissionsScopethat route already sits under. Its logic,firstRunChecklistLogic.ts, is the dialog's logic widened to every row (git shows it as a delete and an add; it keepsbrowserTimeZone,browserLanguage,browserCurrency,isAdmissibleValueandmayWrite).Row list (the card leaves it to the PR; the seat kept Home as the only placement). Each row's state is read from the real setting every time it is drawn, never from a click:
Default Organization, the name plugin-auth gives it (#30125 row 1)localization.localeand.default_countryboth resolve from a rung other than the manifest defaultlocalization.timezoneresolves from a rung other than the defaultlocalization.currencyholds any value (it has no platform default)mail.provideris notlog,from_emailis set, and SMTP hassmtp_host; shown only to a session that may write mailA done row shows its value; a row whose state cannot be read (mail unreadable, members unreadable) is left out rather than shown as not done. Once every row shown is done the checklist draws nothing, and it comes back on its own if a setting is unset again.
The rulings the dialog was built on still hold (objectui#11693 B2, #28872). The browser only pre-fills rows 2 to 4. Nothing is written until the row's Confirm, which is
saveSettingsNamespace('localization', changes), the Settings page's ownPUT /api/settings/localization. Cancel writes nothing. An unset currency stays on the list until it is set. Language, country and currency are one offer read from the browser's first language (zh-CN→ 中文, CN, CNY), so they cannot contradict each other.Who sees it. A session that may write
localization(its manifest'swritePermission, failing closed on an unreported answer). A member sees nothing and sends oneGET /api/settings(the list, which never refuses) and no namespace read. The close button hides it for that administrator, in that workspace, on that device (localStorageos:first-run-checklist:ORG:USER); nothing is written to the server and no row changes. Its reads wait for the console's first paint (whenBootPainted, objectui#12980), as the dialog's did.「中国」 in 默认国家. The refusal started in the console:
DomainComboboxswapped typed text for a code only when it equalled the option label (「中国 (CN)」) or the code, so 「中国」 went to the server as typed, and the server's two-letter pattern refused it withinvalid_formatin English. Now a typed name that exactly one option carries (the label without its own code beside it, trimmed, case-insensitive) is committed as that option's code; any other text is still committed verbatim and the server stays the judge. A refusal withcode: 'invalid_format'is now drawn in the console's words and the reader's language, naming the setting by the label its row draws (settingsFieldRefusals.ts): "Choose a value for Default country from the list." for a picker, and "Gateway model is not in a valid format. Use provider/model." for a typed field, repeating the field's own help line because the refusal takes its place. Every other refusal code keeps the server's sentence. The server's pattern check is unchanged (⛔packages/**).The narrowing.
@object-ui/i18n's exportedTranslationKeys(typeof en) loses the 14console.workspaceTimezonePrompt.*keys in all ten packs. The changeset isfeat(console)!, carries the BREAKING note, the removed-key table and the ADR-0087 dispositionnot-required (runtime-interface-only ui/packages/i18n/src/locales/en.ts#TranslationKeys), as on PR #29359.Eager language pack. The checklist's strings sit in the console group of the eager packs. After PR #30640 moved the marketplace pages' strings into the lazy
marketplacenamespace,i18n-locale-enmeasures 57,233 gz against its unchanged 60,000 ceiling on this branch. This PR adds no marketplace-page strings, so nothing moves into that namespace.Files beyond the claim's first surface, each a reader of what was removed (claim amendments 2 and 3):
ui/apps/console/src/pages/auth/SetupPage.tsx(importsbrowserTimeZonefrom the renamed module),ui/apps/console/src/loadLocales.ts(comment), the four console tests that mocked the dialog's module,ui/e2e/live/global-setup.tsandexamples/app-showcase/e2e/global-setup.ts(each waited up to 60 s for the dialog's testid before a run; the checklist is not modal, so that step is removed),docs/qa/platform-checklist/RUNNER.md(told QA runners to dismiss the dialog), and three i18n pins that named the dialog's keys or file (machineLocaleCensus-9909,console-copy-system-noun-12956) or count the de pack's quotes (de-quote-pairing-3876, 105 → 107 for two new „…“ pairs).Tests
ui/apps/console/src/pages/settings/__tests__/FirstRunChecklist.test.tsx(10), on the realapi.tsagainst a stubbedfetchand the realMePermissionsProvider: a member reads the list only and sees nothing; no capability answer or an empty one sends no request; a fresh workspace shows all six rows not done with their links and writes nothing; rows the server reports as set are done and a fully set workspace draws nothing; Set pre-fills the currency from the browser, nothing is written until Confirm, and Confirm sends exactly one PUT with{ currency: 'CNY' }; language and country are one offer ({ locale: 'zh-CN', default_country: 'CN' }); Cancel and the close button write nothing and the hide survives a remount with no request; aninvalid_formatrefusal is shown in the console's words and the row stays not done; zh copy; no read while the boot splash is up.firstRunChecklistLogic.test.ts(11):localeOffer,browserRegion, the admissible pre-fill,isKeySet, row done,isMailConfigured,isOrganizationNamed,hasInvitedColleagues, the dismissal record and unreadable storage.SettingsField.domainOptionTypedName-30206.test.tsx(5): 「中国」 commitsCN; trimmed and case-insensitive with full-width brackets; a name two options carry and part of a name stay verbatim; a currency name commits its code.settingsFieldRefusals-30206.test.ts(5), against the shippedentemplates.SettingsView.field-errors,SettingsView.crypto-unavailableandSettingsView.refusalToast-13146asserted the server's English sentence forinvalid_format; they now assert the console's sentence (and that the server's is gone).WorkspaceTimezonePrompt.test.tsx,.currency-28872.test.tsx,.afterBootPaint-12980.test.tsx; their pins (only Confirm writes, the browser zone pre-fill, the currency arm, the boot-paint wait, members never asked) are carried by the new files above.Acceptance notes
isDefaultlands its administrator in that app, and the checklist is then seen on the next visit to Home, not at sign-in.DEFAULT_ORGANIZATION_NAMEis a copy of plugin-auth's literal (ensure-default-organization.ts). No test holds the two equal: aui/test readingpackages/**source would be a cross-package input outside both CI rosters. A rename on the server side would make row 1 read "named" for an organization nobody named.invalid_format(for examplevalue_domain) still carry the server's English sentence; localizingFieldError.messageis a server concern and out of this card.os:workspace-timezone-prompt:*) is no longer read; it stays in browsers that have it, harmlessly.packages/plugins/plugin-auth/src/dev-admin-seed-workspace-timezone.test.tsstill describes the dialog in its doc comment (the condition it pins is still the one row 3 reads). Left alone: ⛔packages/**on this card.claude/project-thread-03bt30, notclaude/issue-30206-…. The eight commits carried over from feat(console): an administrator's first-run checklist on Home replaces the currency and time zone dialog #30552 name the card in their subjects; landed-branch history is not rewritten.Verification
All readings on head
e823d8fd9dunless a line names another.Eager closure (Console vite build, VITE_BASE_PATH=/console/)
pnpm --dir ui run check:eager-closureexit 0: eager closure 2157.8 KB gz across 103 of 2412 chunks (budget 2431.6 KB).i18n-locale-en57,233 gz (fromui/apps/console/dist/eager-closure.json) against the unchanged 60,000 ceiling, headroom 2,767. The checker's ceilings are byte-identical tomain(no diff tocheck-eager-closure-budget.mjs).Tests and type-check
pnpm --dir ui exec vitest run --maxWorkers=2 apps/console/ packages/i18n/(lock VERDICT command-exit 0): Test Files 402 passed (402), Tests 4540 passed, 13 skipped (4553).pnpm --filter @object-ui/i18n type-check,pnpm --filter @object-ui/console type-check,pnpm --dir ui run type-check:e2e,pnpm --filter @objectstack/example-showcase typecheck: all four exit 0 (one locked run, VERDICT command-exit 0).pnpm --dir ui run check:new-line-citations: 0 new citations.Full dogfood battery (Clause-② narrowing arm)
pnpm --filter @objectstack/dogfood test, unsharded, after a fullturbo run build --filter='!@objectstack/docs'(118 of 118 tasks): lock VERDICT command-exit 0, Test Files 424 passed, 3 skipped (427), Tests 3538 passed, 41 skipped (3579), no failures.Derived gates: node scripts/pm/dispatch-gates.mjs --commands (58) and --ran
pnpm --dir ui exec vitest run scripts/first answered 99 (lock not reached while the console tests held it) and was re-run under the same slot: Test Files 170 passed, 2 skipped; Tests 4979 passed, 2 skipped.--ran:✓ dispatch-gates --ran: 58 derived famil(ies) accounted for — 58 run, 0 NOT-MEASURED.Live: before on main, after on the branch (taken on #30552 head 9914c31; no console source changed since)
mainfa9c0aa2: entering Setup opened the currency dialog over the page, zh and en.zh-CN,Asia/Shanghai):/homeshows the checklist, progress已完成 2/6(the rig's dev seed sets the zone and seeds members). Opening 默认语言和国家/地区 pre-fills 简体中文 and 中国 (CN). Clearing the country box and typing 「中国」 leaves中国 (CN)in the box; Confirm sent exactly one write,PUT /api/settings/localization {"locale":"zh-CN","default_country":"CN"}, answered 200, and the row turned done, progress已完成 3/6./apps/setupafterwards: 0 dialogs.en-US,America/New_York):3 of 6 done; opening Default currency pre-fillsUSD — US Dollarand sends no write;/apps/setup: 0 dialogs.Ablations (taken on #30552, each through scripts/ablation-replace.mjs, restored to the HEAD blob with git diff HEAD empty)
SettingsField.tsxname match removed:SettingsField.domainOptionTypedName-302063 failed, 2 passed (the two verbatim cases stay green, as they should).settingsFieldRefusals.tsre-wording disabled: 7 failed acrosssettingsFieldRefusals-30206,FirstRunChecklistandSettingsView.field-errors.mayWriteno longer reads the manifest'swritePermission:FirstRunChecklist"a member … nothing is drawn" fails, 1 of 10.isKeySetno longer readssource: 7 failed acrossFirstRunChecklistandfirstRunChecklistLogic.🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibynkEvahBBwY1bFkmH