Repository navigation
feat(spec): a settings toggle can declare the question asked before it is turned off (confirmOffText) - #30489
Conversation
…t is turned off (confirmOffText) SpecifierSchema gains an optional confirmOffText (I18nLabel), refused on every specifier type but toggle. The platform translation bundle carries it at settings.NS.keys.KEY.confirmOffText, and the auth manifest's email_password_enabled toggle declares the sentence from the card, in all four shipped locales. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzhstcrHvzYEWDZZDBx
📓 Docs Drift Check3 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 172 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8973d2523b6986b5efb6c5e4a6a6240b7dead709 && git checkout 8973d2523b6986b5efb6c5e4a6a6240b7dead709
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 48231b024ed7a9f0c9be73f34a5bf5f48c7a1d68 1a29fff73ae896337bf1a00f98f8e33e4ca77685 && git checkout -B drift-repro 48231b024ed7a9f0c9be73f34a5bf5f48c7a1d68 && git merge --no-ff 1a29fff73ae896337bf1a00f98f8e33e4ca77685
node scripts/docs-audit/affected-docs.mjs --json 48231b024ed7a9f0c9be73f34a5bf5f48c7a1d68 |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract-review-claim: session_01WGufzfgvGexKn9vLHsy89K · head 1428997 · 2026-11-05T13:24Z Full review of the first head at |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe fix is at the right layer and in the ruled shape: one optional key on Implemented-by: VERDICT: PASS |
…eset, and name the doors that refuse a bundle without the leaf Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzhstcrHvzYEWDZZDBx
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K 维护者速读
席位意见:fable 契约复审 PASS,没有必须修的。五条裁决约束都核对过。开发者自报的偏差(translation.zod.ts 加一个叶子,让带这句翻译的语言包能通过解析)判定为必要,范围也对。采纳了两条建议:service-settings 的版本级别改成 patch,正文写明实际拒绝它的两道门。没采纳给新键加别名,因为新键没有旧拼写。 席位核对 — ACCEPT · domain:spec#3 · head
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Requested by Yibo Wang · project thread
Fixes #30418
Ruling-ref: 6657805240
Clause-②: yes (widening: an optional
confirmOffTexton toggle specifiers; refused on every other specifier type)Metadata-protocol: added
Skills-docs: none owed — a settings manifest is platform code that no app can declare, and no
skills/**file teaches a specifier key (git grep -n -iE "specifier|settings.?manifest|email_password_enabled" -- skills/: two hits,skills/objectstack-platform/references/plugin-development.md:354, which names thefeature_flagsmanifest and no key, andskills/objectstack-i18n/SKILL.md:193, which sayssettingsis platform-only and stays true)Prior rulings read: confirmofftext,confirmtext,settings manifest toggle → 0 hits; none; thread: 1 ruling(s) (6657805240); repo: objectstack-ai/objectstack
Liveness: not governed — the liveness ledger walks registered metadata kinds plus the unregistered kind map (
authorableTypes()incheck-liveness.mts); a settings manifest is neither, and nopackages/spec/liveness/*.jsoncarries aSpecifierrow, so there is no row to add.check:livenessgreen.Carried: #30500, the
domain:uihalf (ruling (4)): the console's settings page asksconfirmOffTextbefore it saves a toggle turned off. Until it lands the key parses and is declared but is not shown.维护者速读
toggle)新增可选键confirmOffText,即「关闭前要弹出的确认文字」;其他类型的设置项声明它会在解析时被拒绝。认证设置里的「启用邮箱/密码登录」开关声明了这句话(英、中、日、西四种语言)。domain:ui后半),所以本 PR 落地后界面暂时没有变化。回滚即还原本 PR。packages/spec/src/system/settings-manifest.zod.ts新键与 superRefine 新增的拒绝分支;packages/services/service-settings/src/manifests/auth.manifest.ts的声明;settings-translation-coverage.test.ts新增的覆盖要求。pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/settings-manifest.test.ts,预期 78 例全绿,其中Specifier.confirmOffText一组 7 例。What changes
SpecifierSchema.confirmOffText(packages/spec/src/system/settings-manifest.zod.ts):I18nLabelSchema.optional(), described as "Confirm dialog text shown before a toggle's value changes to off (omit = no confirm)". Its TSDoc names the translation address and the toggle-only rule.superRefinegains one arm beside thevalueDomain-on-a-layout-specifier arm. On anytypebuttoggleit adds acustomissue at['confirmOffText']: "Specifier of type 'TYPE' has no off to confirm, so it must not declare a 'confirmOffText' (only a 'toggle' may)." It is an arm on the existing refinement, so no dropped-refinements row (the spec build is green).packages/spec/src/system/translation.zod.ts): the platform face'ssettings.NS.keys.KEYentry gains the leafconfirmOffText, beside the toggle'slabel, and the convention comment listssettings.NS.keys.KEY.confirmOffText. Thekeysentry is a strict object, so without this leaf two doors refuse a bundle carrying the text:PlatformTranslationDataSchemaat parse, andtscon the service's bundle literals, which are typedPlatformTranslationData. (The service'skernel:readyhook loads the bundle into the i18n service unparsed,settings-service-plugin.ts:296-302, so the runtime itself would not refuse it.) This is the translation half of "translated like the toggle's other text"; it sits in the platform-onlysettingsgroup, which both app-authored doors already refuse.auth.manifest.ts:email_password_enableddeclaresconfirmOffText: 'People who sign in with a password will need another way in.'(the Setup and login page: turning off email and password asks for no confirmation, and the login page still draws the email form when it is off #30299 sentence), and the four shipped bundles carry it atsettings.auth.keys.email_password_enabled.confirmOffText(en, zh-CN, ja-JP, es-ES).settings-translation-coverage.test.ts: a declaredconfirmOffTextis held to the same per-locale coverage as the toggle's label (key:KEY.confirmOffTextis reported missing), and a bundleconfirmOffTexton a key that declares none is reported as undeclared copy.authorable-surface/system.json(system/Specifier:confirmOffText, written by the spec build),content/docs/references/system/settings-manifest.mdxandtranslation.mdx(gen:docs).check:llms-txtre-derived its 98 claims with no change (llms.txt counts schemas, not keys)..changeset/30418-settings-toggle-confirm-off.md:@objectstack/specminor (the protocol change),@objectstack/service-settingspatch (a manifest declaration and its copy, graded as Authentication settings: turning off email and password with no other sign-in method saves without warning, and everyone is locked out #30202 graded its manifest change), with both declaration lines.Tests
settings-manifest.test.ts, describeSpecifier.confirmOffText — a toggle asks before it goes off: accepted on a toggle with its text; accepted as an inline locale map; absent still parses and readsundefined; refused ontextandselect(value-bearing) andgroupandaction_button(layout), each with a control that the bare specifier parses, then exactly onecustomissue at['confirmOffText']whose message names the type and the key.translation.test.ts: the platform face acceptssettings.auth.keys.email_password_enabled.confirmOffTextwith fullsafeParsesuccess and keeps the value.auth.manifest.test.ts: the parsed manifest'semail_password_enabledis atogglecarrying the sentence, and the en bundle carries the same text at its address.settings-translation-coverage.test.ts: the zh-CN / ja-JP / es-ES rows forsettings.authnow also require theconfirmOffTextleaf (green with the three translations).Acceptance notes
resolveSettingsFieldConfirmOffhelper is added toi18n-resolver.tsbesideresolveSettingsActionConfirm: the console resolves settings copy through its ownuseSettingsLabel, and the ruling binds no resolver export. If thedomain:uihalf wants one, it is a small spec addition there.Verification
Measured at head
14289976ed(local subset; CI families are the PR's own check-runs). Head1a29fff73a(review round 1) changes only the changeset grade and one test comment; on ittranslation.test.tspasses 218/218 and the Check Changeset family reruns green:check-changeset-no-major --base origin/mainexit 0 ("This diff introduces nomajorbump."),check-empty-changeset --base origin/mainexit 0,check-adr-0087-registration --base origin/mainexit 0,pnpm check:changeset-gate-self-testsexit 0.pnpm --filter @objectstack/spec testunder os-verify-lock: VERDICT command-exit 0, Test Files 815 passed (815), Tests 25626 passed, 1 todo.pnpm --filter @objectstack/service-settings test && … typecheckunder os-verify-lock (closure built first): VERDICT command-exit 0, Test Files 68 passed (68), Tests 1157 passed;tsc --noEmitclean.pnpm --filter @objectstack/spec check:generatedaftergen:docs: every artifact current (the first run named onlycontent/docs/references/**stale, whichgen:docsregenerated).node scripts/ablation-replace.mjsreplaced the anchorif (spec.confirmOffText !== undefined && spec.type !== 'toggle') {withif (false) {(anchor 1 → 0, blob cc1a18591108 → 4425afbb53ce) and ranvitest run src/system/settings-manifest.test.ts: Tests 4 failed, 74 passed — exactly the four refusal pins (text, select, group, action_button). Restored: blob == HEAD andgit diff HEADempty. The spec test imports the schema from source, so no dist leg is needed.node scripts/pm/dispatch-gates.mjs --ran FILE --repo objectstack-ai/objectstack: "106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN". Four families first exited 3 (prerequisite: a shallow clone forcheck-plugin-teardown-shape --self-test; built dists forcheck:skill-examples,check:dual-build-cjs-loads,check:i18n-walk-parity); aftergit fetch --unshallow origin mainand a fullturbo run build --concurrency=2(VERDICT command-exit 0) all four exit 0.check-changeset-no-major: "This diff introduces nomajorbump."pnpm check:dev-prereqsexit 0;pnpm check:skill-frame-freshnessexit 0.node scripts/pm/check-prior-rulings.mjs --card 30418 --terms …: exit 0, line above.Gate record (106 lines, as reconciled)
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzhstcrHvzYEWDZZDBx