Repository navigation
feat(spec)!: a single-word language name (Chinese, English, Japanese) is refused as a locale - #26770
Conversation
The locale shape's 2–8 letter language subtag admitted any single word (Chinese, English, Japanese), though the refusal text says a language name is refused. The subtag is now 2–3 letters in LocaleSchema, SYS_USER_LOCALE_TAG_PATTERN and service-messaging's read-side copy. Claude-Session: https://claude.ai/code/session_01WGufziYkxRFm6oPR7CmuDg Co-authored-by: Claude <noreply@anthropic.com>
…eady-registered) Claude-Session: https://claude.ai/code/session_01WGufziYkxRFm6oPR7CmuDg Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check3 anchor(s) derived from 3 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 148 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 2e345bb42ce6902a7343d86363f582abe9c24d99 && git checkout 2e345bb42ce6902a7343d86363f582abe9c24d99
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 83c4bd29f7ee653f10f4b83f61e36a1aeb8292d9 25fe0e3fc0f28f718149dcdf8b372a2c9d1a91cb && git checkout -B drift-repro 83c4bd29f7ee653f10f4b83f61e36a1aeb8292d9 && git merge --no-ff 25fe0e3fc0f28f718149dcdf8b372a2c9d1a91cb
node scripts/docs-audit/affected-docs.mjs --json 83c4bd29f7ee653f10f4b83f61e36a1aeb8292d9 |
Contract reviewServed-tier: Inputs read: card #26463 body and all four comments (triage 6434121727, premise 6434443639, claim 6442902707, os-dev-report 6445697484); PR #26770 body, file list (8 paths, +97/-49), its one comment (docs-drift 6445696117; no review comments, no reviews), the net diff against ① Derived judgmentsShape. The one change is the primary-subtag width, Byte-identity. On the head, The two contrary pins. The seat's reading of the 2026-09-03 ruling holds: the ruling's quoted clause is "a malformed value is refused loudly by the column's BCP-47 shape check … and never dead-letters a notification", a shape check with no subtag length fixed; the quotation stays verbatim in the plugin-auth header (:4–11) and in the parity header (:7–14). The rewritten plugin-auth pin (:205–217) refuses ADR-0087. The disposition Declarations. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…nd say what a stored user locale does Claude-Session: https://claude.ai/code/session_01WGufziYkxRFm6oPR7CmuDg Co-authored-by: Claude <noreply@anthropic.com>
… locale clause Claude-Session: https://claude.ai/code/session_01WGufziYkxRFm6oPR7CmuDg Co-authored-by: Claude <noreply@anthropic.com>
维护者速读 (席位终稿)
席位意见:接受。契约复核 PASS(6446067343)。有两个旧测试按旧的 2–8 写法断言了相反结论,席位依据 2026-09-03 维护者裁决裁定它们随本卡一起改:裁决要求的是形状检查,没有规定长度。 席位核对 · ACCEPT ·
|
Requested by Yibo Wang · project thread
Fixes #26463
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none —
content/docs/ui/translations.mdxandcontent/docs/protocol/kernel/i18n-standard.mdxalready say a language name is refused (this diff makes that sentence true), andskills/objectstack-i18n/SKILL.mdalready teaches tag-shaped codes only (en,en-US,zh-CN,pt-BR); nothing taught there changes维护者速读
Chinese、English、Japanese这类单词语言名现在被拒;spec、sys_user.locale列规则、通知读侧三处同一字符串同步改。locale: 'Chinese'能保存成功,但没有会话会请求这个 locale,译文永远不显示;拒收文案和 spec: a translation item accepts anylocalestring ("not a locale!!" saves 200, valid) — LocaleSchema is a bare z.string() #25822 的 changeset 早已承诺「语言名会被拒」。Chinese之类的翻译行或用户 locale,解析时会被拒(存量行照常加载并由os migrate meta --stored列为 TODO);回滚即还原这三处正则。packages/spec/src/system/translation.zod.ts(正则与拒收文案)、packages/platform-objects/src/identity/sys-user.object.ts(列规则)、packages/services/service-messaging/src/recipient-locale.ts(读侧副本与null说明)。pnpm --filter @objectstack/spec exec vitest run src/system/translation.test.ts,预期Chinese/English/Japanese三行拒收、en/zh-CN/zh-Hans-CN/es-419通过。What changes
LOCALE_TAG_SHAPEaccepted a 2–8 letter language subtag, so any single English word of that length passed as a locale, thoughLOCALE_TAG_REFUSALand the #25822 changeset both say a language name is refused. The primary subtag is now 2–3 letters, the ISO 639 codes every tag in use starts with: BCP-47 reserves 4 letters and registers no 5–8 letter language. Hyphen subtags,zh-Hans-CN,es-419,yue-HKand case folding are unchanged.The one shape lives in three places, and all three move in the same commit, still byte-identical:
packages/spec/src/system/translation.zod.tsLOCALE_TAG_SHAPELocaleSchema: translation itemlocale, bundle keys,i18n.*locales, seedlocaleChineseand says write the tag, not the name)packages/platform-objects/src/identity/sys-user.object.tsSYS_USER_LOCALE_TAG_PATTERNlocale_bcp47_shaperule onsys_user.localepackages/services/service-messaging/src/recipient-locale.tsLOCALE_TAG_SHAPEnullis now shape-refused on both sides,NOTHING_LITERALSstays as the read side's own floorAlso: the 18.x D3 entry
translation-locale-non-tag-refuseddescribed the replacement as "a 2-8 letter language subtag"; this diff made that false, so it now reads 2-3 (one word). Its conversion already judges withLocaleSchema, so a stored item underChineseis now listed as a TODO with no further change.ADR-0087 disposition:
not-required (already-registered translation-locale-non-tag-refused)in the changeset. The D3 entry already prescribes rewriting a language name by hand; this diff narrows the same shape.Copies of the pattern deliberately not moved
packages/plugins/plugin-hono-server/src/current-user-endpoints-localization.test.ts:69: a fixture rule. Its own docblock says what is under test is that the endpoint evaluates whatever rule the registry declares, and none of its 13 cases depends on a 4–8 letter subtag (all 13 pass unchanged). Outside the claim's file surface.ui/packages/i18n/src/provider.tsx:224: the no-Intlfallback ofisWellFormedLanguageTag. The main branch isIntl.getCanonicalLocales, which acceptsChinese; narrowing only the fallback would make the two branches disagree. It reads a UI language preference, not stored metadata.ui/packages/app-shell/src/views/metadata-admin/previews/doc-draft.ts:28: add-locale input hygiene for a doc'stranslationsmap, which the spec keys by any string. A different contract, outside the claim's file surface.Tests
packages/spec/src/system/translation.test.ts: refusal rowsChinese,English,Japanese,chinese-CN,null(each aninvalid_formatissue whose message names the shape and the release); controlsen,zh-CN,zh-Hans-CN,es-419,yue-HK,zh-cnaccepted; the card's reproduction at the item door (locale: 'Chinese'refused atlocaleonly) and aChinesebundle key refused.packages/plugins/plugin-auth/src/sys-user-locale-write-contract.test.ts: the shape-not-membership pin is rewritten.Japaneseis now refused by the column (VALIDATION_FAILED), and a well-formed tag nothing ships (tlh-QQ) is still accepted, which keeps the pin about shape, not membership.packages/services/service-messaging/src/recipient-locale-shape-parity.test.ts: source equality unchanged; behavioural rows gainChinese,Japanese,null; thenullcase now pins that both sides refuse it by shape and the read side refuses it anyway.Acceptance notes
Clause-②: no (narrowing); this card follows it (the claim says the same).test:repo(vitestrepoproject) was not run locally: the diff edits none of its tests and the derived list does not name it; CITest Coreruns it.Verification
All readings on head
84506a0c(this branch merged withmain5cc31c78) unless noted.Tests and typecheck
ee3058c2:pnpm --filter @objectstack/spec test731 files, 21928 passed, 1 todo;@objectstack/service-messaging test61 files, 732 passed;@objectstack/plugin-auth test198 files, 3780 passed, 10 skipped;@objectstack/platform-objects test96 files, 1361 passed;plugin-hono-serversrc/current-user-endpoints-localization.test.ts13 passed.typecheckexit 0 for spec, platform-objects, service-messaging and plugin-auth (plugin-auth after a full build; its examples leg needs its own dist).84506a0c:src/system/translation.test.ts189 passed; platform-objects 96 files, 1361 passed;recipient-locale-shape-parity.test.ts19 passed;sys-user-locale-write-contract.test.ts24 passed.pnpm build: 116 of 116 tasks;pnpm --filter @objectstack/spec check:generated: all 14 generated artifacts up to date (no regeneration commit needed).Ablation (one-time, not kept)
node scripts/ablation-replace.mjsput the spec pattern back to the 2–8 letter subtag (anchor hit 1 to 0, blobfa4a2333to1108375e) and ransrc/system/translation.test.ts: 6 failed, 183 passed. The red rows areChinese,English,Japanese,chinese-CN,nulland the door test that carries the card's reproduction. Restored: blob equals HEAD (fa4a2333) andgit diff HEADis empty. The test importssrc, so no build leg applies.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 87 families on84506a0c. Each was run and recorded asCMD :: exit N: all 87 exit 0.--ranreconciliation: 87 derived, 87 run, 0 NOT-MEASURED, 0 UNRUN.check-adr-0087-registration --base origin/main: one declared-breaking changeset, dispositionnot-required (already-registered).Dogfood battery
pnpm --filter @objectstack/dogfood testunder the verify lock, after a full build. On84506a0c: VERDICT command-exit 0, 302 files passed and 3 skipped (305), 2505 tests passed and 20 skipped (2525). Also on the pre-merge head32f73697: the same counts.maingained six more commits after the merge (none touch the eight files here); CI judges the merge ref.Generated by Claude Code