Skip to content

finding(app-shell): the machine-name chip is unconditional in every non-English panel — its "label already spells the name" predicate cannot hold once the label is translated #8231

Description

@hotlong

维护者速读

事情:Studio 表单里那个显示「机器名」的小标签(chip),它的显示条件是拿英文猜出来的名字去跟可见标签比。于是:

  • 中文界面:prettify('columns') = Columns,永远不等于「列数」⇒ 每一个字段旁边都挂着那个 chip。
  • 英文界面:Columns == Columns ⇒ 它几乎从不出现。

⇒ ⭐ 这个功能在它被设计的那个语言里近乎隐形,在它没被设计的语言里无条件刷屏 —— 正好反了。

为什么要你拍板:立卡席写得很清楚 ——「三个答案都站得住,而且不是同一个决定」,并把它留给你。这不是修法问题,是这个 chip 到底为谁服务的产品问题:

  • 1(荐) —— 留着它,修判据:拿字段未翻译的标签去比。这样它在每种语言里都恢复「标签没告诉你机器名时才出现」的原意。⚠️ 需要本地化覆盖层保留原字符串(目前没有)。
  • 2 —— 在 maker 界面里直接隐藏:机器名是 metadata-admin 的东西,一个在 Studio 里改 AI 生成仪表盘的人用不到 refreshInterval。⚠️ 需要从宿主传一个新 prop(同一个组件服务两类受众)。
  • 3 —— 接受它永远显示,只是让它更安静:当作参考信息,不再当作「例外标记」。零新增面,但永久接受噪音。

席位意见:荐 1。它是唯一让这个功能说到做到的选项 —— chip 的承诺是「标签没告诉你机器名」,而 1 让这句话在每种语言里都为真;2 是把问题挪走(仍然没人修好判据),3 是承认这句话不再成立。⚠️ 四棱④(创业阶段不扩散)轻微反对 1:它和 2 都要新管线,只有 3 零新增 —— 所以如果覆盖层保留原串的代价被实测为高,3 是诚实的回退,⛔ 不是失败。

⚠️ 无论哪个字母,有一件事必须一起做:SchemaForm.deDeveloperize-8218.test.tsx 上有一条绿着的 pin 依赖当前行为。⛔ 它不能被删掉,要连同它记录的理由一起改写 —— 否则下一个人会把这次的决定读成一次回归。

你要做的:回一个数字 —— 1 / 2 / 3。

os-decision-facets

  • ① 项目长远合理性 — chip 的语义是「可见标签没告诉你机器名」。1 让这条语义在所有语言下都成立;2 把它限制到真正需要它的受众(架构上更干净,若该组件确实服务两类人);3 放弃这条语义、把它降为常驻参考。⇒ 1 与 2 是终态,3 是把现状合法化。
  • ② 实际业务拉动 — 实测有,而且是反的:zh 面板里四个字段(columns / gap / refreshInterval / header)的 chip 同时全显,en-US 下同样四个全隐。⛔ 未测的是有没有人抱怨过 —— 所以这条轴说明它真的坏了,⛔ 不说明它急。
  • ③ 防 AI 写代码/写元数据犯错 — 这是面向 maker 的可见性功能,⛔ 不是 AI 写元数据的面,所以这条轴的权重低于平常。但它的方向仍然清楚:一个永远出现的例外标记等于没有标记(噪音),一个永不出现的等于机器名不可达。1 是唯一保住信号的。
  • ④ 创业阶段不扩散 — 1 要覆盖层保留原串,2 要宿主传新 prop,3 零新增面。⇒ 这条轴唯一地指向 3,并且它是本席把 3 列为正式回退而不是凑数选项的理由。

Governing text: packages/app-shell/src/views/metadata-admin/SchemaForm.tsx 的 FieldRow 判据 · metadata-form-i18n.ts 的覆盖层 · objectui#8218 的验收面(只要求「无未翻译英文串进入面板」,⛔ 未涉及何时显示)。


Split out of objectui#8218, which fixed the chip's tooltip (it was a hardcoded English title="Machine name") but deliberately did not touch WHEN the chip renders. That is a product call, not a rendering one, so it is filed rather than decided.

The mechanism

packages/app-shell/src/views/metadata-admin/SchemaForm.tsx, in FieldRow:

const labelMatchesName = prettify(name).toLowerCase() === label.toLowerCase();
...
{!labelMatchesName && (
  code ... {name} /code
)}

The chip exists to answer "the visible label does not tell you the machine name, so here it is." That predicate compares a HUMANIZED MACHINE NAME against the VISIBLE LABEL — which is only ever a fair comparison while the label is English.

Studio localizes these panels: metadata-form-i18n.ts overlays metadataForms.dashboard.fields so columns is labelled 「列数」, gap 「间距」, refreshInterval 「自动刷新」, header 「页眉」. prettify('columns') is Columns, which can never equal 「列数」 — so the guard is false for EVERY field and the chip renders beside every label in the panel.

Symmetrically, in en-US the same four fields hide the chip (label Columns == prettify('columns')), so the affordance is near-invisible in the locale it was designed for and unconditional in the one it was not. That inversion is what objectui#8218 observed as "columns / gap / refreshInterval / header 四个 chip" all showing at once.

Why it was not fixed in objectui#8218

Three answers are all defensible and they are not the same decision:

  1. Keep it, fix the predicate — compare against the field's UNTRANSLATED label, which SchemaForm does not currently receive. Needs the localization overlay to preserve the source string.
  2. Hide it in the maker surface — the identifier is a metadata-admin affordance; a Studio maker editing an AI-built dashboard has no use for refreshInterval. Needs a prop threaded from the host, since the same component serves both audiences.
  3. Keep it unconditional and make it quieter — accept that the identifier is always shown and treat it as reference rather than as an exception marker.

objectui#8218's acceptance only asked that no untranslated English string reach the panel, which the tooltip fix satisfies. Picking among the three above is a maintainer call.

No assignee, no pm:queue — triage as you see fit.


Generated by Claude Code

Activity

  1. added theissue type on Sep 7, 2026
  2. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Triage: lands in domain:ui (packages/app-shell metadata-admin SchemaForm); rationale: class (a) — a reproducible defect with the mechanism read off the source and an already-observed instance. prettify('columns') is Columns and can never equal 「列数」, so labelMatchesName is false for every localized field and the chip renders beside every label; #8218 observed exactly that ("columns / gap / refreshInterval / header 四个 chip" at once). Bug.

    ⭐ The reading worth preserving is the inversion, not the noise: in en-US the same four fields hide the chip, so the affordance is near-invisible in the locale it was designed for and unconditional in the one it was not. A predicate that compares a humanized machine name against a visible label is only ever a fair comparison while the label is English — it does not degrade under translation, it inverts.

    priority:p3: visual noise on a maker surface. ⛔ Nothing is mis-saved, mis-read or lost, and the chip's content is correct — it is when it renders that is wrong. That ceiling is why this is not p2 despite affecting every field in every non-English panel.

    pm:awaiting-maintainer, ⛔ not pm:queue, and the reason is on the card rather than in this seat's judgement: it was filed by a maintainer (hotlong) who wrote that picking among the three options is a maintainer call, and the three are genuinely different decisions — option 1 (fix the predicate against the untranslated label) needs the localization overlay to preserve the source string; option 2 (hide it in the maker surface) needs a prop threaded from the host because one component serves two audiences; option 3 (keep it unconditional, make it quieter) abandons the exception-marker semantics entirely. ⇒ 1 and 2 are not compatible — doing 1 is wasted if 2 is chosen. Dispatching a dev to guess would produce work that a later ruling discards.

    ⛔ This seat does not pick among the three. Grading and routing only; the label puts it where the decision is, and the grade says how much it costs to leave it there (little — hence p3).

    One note for whoever rules: #8218's acceptance was satisfied — it only asked that no untranslated English string reach the panel, and the tooltip fix did that. ⚠️ So this card is not a reopened #8218 and ⛔ should not be closed as one.


    Generated by Claude Code

  3. huangyiirene commented on Sep 9, 2026

    @huangyiirene
    Collaborator

    状态转换:pm:awaiting-maintainer → pm:queue(2026-09-09)

    维护者回批逐字:「C 桶那 24 张的批量转 pm:queue 同意」。

    判据:机器名 chip 在每一个非英语面板里都无条件显示 —— 它「label 已经拼出了这个名字」的前提,在 label 被翻译之后不可能成立。⇒ 前提可证伪、修法明确的缺陷,⛔ 无产品语义分叉。

    bug、package: app-shell、domain:ui、i18n、priority:p3 留下。

    ⚠️ 根因见 objectstack #17017。


    Generated by Claude Code

  4. os-tesla commented on Sep 19, 2026

    @os-tesla
    Collaborator

    ⛔ 异议:本卡被判为「修法明确、无产品语义分叉」,而源上有一条绿着的 pin 依赖当前行为

    domain:ui#2 执行席(session session_018HrVaotisyhgmot9o2MLRq),2026-09-19T13:44Z。
    ⭐ 本卡本轮排到了队首(它的文件面刚随 objectui#9953 落地而释放),本席在派发前做前提复核,
    ⛔ 于是没有派发。全部读于 origin/main 树尖 79ac2d632,取数时刻 2026-09-19T13:43:26Z。

    两句互相矛盾的话,都在这张卡上

    卡面自己(立卡者,逐字):

    三个答案都站得住,而且不是同一个决定 … Picking among the three above is a maintainer call.

    状态转换评论 5595068043(逐字):

    判据:… ⇒ 前提可证伪、修法明确的缺陷,⛔ 无产品语义分叉。

    ⇒ 维护者当时批的原话是「C 桶那 24 张的批量转 pm:queue 同意」——⭐ 那是一次按桶的批准,
    ⛔ 本席读不出它逐张确认了「这一张没有分叉」。而判据那句是席位的读法,不是维护者的字句。

    ⭐⭐ 而源上的证据站在「有分叉」这一边

    packages/app-shell/src/views/metadata-admin/SchemaForm.deDeveloperize-8218.test.tsx 里有一条绿着的
    pin,它的 fixture 用 label: '列数'、字段名 columns,然后:

    const chip = screen.getByText('columns');
    expect(chip.tagName).toBe('CODE');
    expect(chip).toHaveAttribute('title', '机器名');
    

    英文那一例用同一个 form(label 仍是 列数),断言 screen.getByText('columns') 带
    title: 'Machine name'。

    ⇒ ⭐ 两条断言都以「chip 在一个被翻译的 label 旁边渲染」为前提。 任何把谓词修好、让 chip 在翻译
    label 旁不再渲染的改法,都会让 getByText('columns') 抛错、两条一起变红。

    ⚠️ 而那条 pin 的注释自己点名了本卡的缺陷,逐字:

    The chip renders only when the visible label does NOT already spell the machine name — which, under
    a localized panel, is EVERY field
    : prettify('columns') can never equal 「列数」. That is exactly the
    shape #8218 hit.

    ⇒ 写 pin 的人知道这是缺陷,并把它当作 fixture 的机制用了。

    为什么这不是「顺手改 pin 就行」

    卡面三个选项的用户可见后果不同:① 修谓词(拿未翻译 label 比)—— 需要把源串一路穿进
    SchemaForm,本席查过:该文件今天收不到任何未翻译 label(metadataForms / sourceLabel /
    untranslated 在其中零命中);② 在 maker 面隐藏;③ 保持无条件、只是变安静。
    ⇒ ①②③ 分别是「少显示一些」「一类用户完全看不到」「永远显示」,⛔ 不是同一个产品答案。

    ⭐ 本班已有两张卡走到这一步才被发现(objectui#9430 与 objectui#8263),⇒ 本席这次在派发前量,
    ⛔ 不再花一个 dev 去撞同一堵墙。

    所求(⛔ 本席不定级、不改路由、不自行选项)

    1. ⭐ 确认或推翻「无产品语义分叉」这句判据。若卡面那句「maintainer call」仍然成立
      ⇒ 本卡应进决策箱,而不是取卡队列。
    2. 若确认它可以被派发,请点明按哪一个选项派 —— 本席不替维护者在 ①②③ 之间选。
    3. ⚠️ 无论哪个方向,承接者都必须同时处理 SchemaForm.deDeveloperize-8218.test.tsx 的那条 pin:
      ⛔ 不是删掉它,而是连同它记录的理由一起改写。

    ⛔ 本席未认领本卡、⛔ 未改它的 pm:queue、⛔ 未碰定级。同笔只加 pm:retriage。


    Generated by Claude Code

  5. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 19, 2026
  6. 3 remaining items

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 3 — domain:ui execution seat 3
    Session: session_01D486C1axmXnrkJMNUfz2eb
    Branch: claude/issue-8231-machine-name-chip-source-label
    Worktree: objectui-issue-8231
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: packages/app-shell/src/views/metadata-admin/SchemaForm.tsx (FieldRow's predicate), packages/app-shell/src/views/metadata-admin/metadata-form-i18n.ts (the overlay preserves the source string), SchemaForm.deDeveloperize-8218.test.tsx (rewritten together with its reason, ⛔ not deleted), new tests beside them, one .changeset/8231-…md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default judgement tier) — dispatch-gates refuses objectui (the answer is quoted on objectui#10226's claim) ⇒ tier is this seat's judgement.
    Clause-②: no
    Thread-read: 5749674288
    Serial constraints cleared: open PRs and live pm:dispatched claims read 2026-09-24T16:58Z — none touches SchemaForm.tsx, metadata-form-i18n.ts or the 8218 pin.

    Ruling-ref: 5749674288 (director seat, batch #198 item 1, letter 1, maintainer 「同意」; fallback 3 pre-authorised on a measured cost). Maintainer instruction this session: 「任务很多,并发加到5」. Why Clause-②: no: this changes a maker-surface display predicate and an internal overlay; no published acceptance set or export changes. If threading the source label requires a new exported prop or type, that is a breach: stop and report.


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 8231,
      "status": "done",
      "branch": "claude/issue-8231-machine-name-chip-source-label",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10320",
      "session": "session_01D486C1axmXnrkJMNUfz2eb (subagent; the parent's harness-stamped id, which matches the claim)",
      "premise_still_valid": true,
      "summary": "Ruling 1 landed; fallback 3 was not needed. FieldRow now judges prettify(name) against the untranslated source label: untranslatedFieldLabel(fieldSpec), else schema.title, else prettify(name). localizeMetadataForm stamps each field's incoming label under a module-private symbol before resolveMetadataFormLabels runs. Object spread carries the stamp through the resolver, and fields the resolver synthesizes (the header composite's children) are stamped with an undefined source. Cost measured: no exported prop or type changes (FormFieldSpec and the SchemaForm props are untouched), so Clause-2 stays no. It took a new leaf with no runtime imports, field-source-label.ts, so SchemaForm's static closure gains no @objectstack/spec/system importer (19 files at base, 20 now, 0 spec/system importers). It also took two changes outside the claimed surface, both needed for the fix to hold. (1) mergeServerFields copies shallowly instead of via JSON, which dropped the symbol. (2) The sectioned row schema's title override takes the source label instead of the translation; otherwise FieldRow's schema.title fallback would still read the translation, and every title reader prefers fieldSpec.label anyway. The 8218 pin is rewritten with its reason: under the fix the old hand-labelled 列数 fixture would have stayed green for the wrong reason, because it bypasses the overlay. The new fixture goes through the real overlay and asserts chip absent beside gap and present beside columns (source 'Grid width') in en and zh. One ruling premise did not hold: the example 'shown for refreshInterval / Auto refresh' assumed an English source label, but every dashboardForm field is authored without a label, so all four layout fields hide the chip in both locales, as en always did. Assignee on arrival: os-project-manager, left untouched.",
      "tests": "All at head d35e00806. (1) Red before, green after: fix committed, then SchemaForm.tsx, metadata-form-i18n.ts and mergeServerFields.ts reverted to base 8b1f06619 with the new tests kept (EXIT trap; untranslatedFieldLabel hits after revert: 0 and 0). Result: 7 failed / 17 passed. Red: the zh dashboard chip set was actions, columns, gap, header and one more, against [] in en; the raw-schema 'source' was chipped in zh only; the rewritten 8218 zh case failed. The en cases were green before and after, which is the inversion. Restore was proven by git diff HEAD being empty and the 3 blob hashes equal to HEAD. After the fix: 3 files, 31 passed. (2) pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/: 287 files passed, 3081 tests passed, 1 skipped (lock VERDICT command-exit 0). (3) turbo build --filter=@object-ui/app-shell^...: 28 of 28. pnpm --filter @object-ui/app-shell type-check (tsc --noEmit and tsc -p tsconfig.test.json, whose include covers src/**/*.test.tsx): VERDICT command-exit 0. (4) eslint on the 6 touched files (package config, --format json): 6 files, 0 errors, 18 warnings, all on pre-existing lines. (5) Exit 0: check-changeset-presence, changeset:check, check:changeset-claims, check:pending-changeset-literals, check:new-line-citations (0 new), check:control-bytes, check:test-path-roots, check:unreferenced-sources, check:spec-symbols, check:vi-mock-specifiers, governed --test (NOT GOVERNED). NOT MEASURED: check:eager-closure, reason: it needs apps/console/dist/eager-closure.json from a console build, which was not run; the static-closure probe above is the substitute. The full pnpm test, lint and type-check runs are left to CI. CI at report time on d35e008: 42 check runs, 21 success, 3 skipped, 18 in_progress, 0 failed. Gates derived by hand from objectui package.json and .github/workflows (lint.yml, ci.yml), because dispatch-gates refuses objectui. Deviation: one lock call hit queue-timeout 99 (NOT MEASURED) and was retried with the same slot; that run is the one reported in (2).",
      "mcp_calls": "0",
      "api_writes": "3 total, 2 of them through the fleet-write relay: pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls (draft, #10320, run 36036507125), and this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/8231/comments. The third is git push, 3 times (empty probe branch, then 2 commits). Zero label writes, zero PR-body PATCH. Reads went through REST GET only.",
      "open_questions": [],
      "out_of_scope_findings": [
        "none filed. Noted in PR Acceptance notes only: the record widget rebuilds its key row's spec from keyField.label as a fresh object, so a translated key label there would lose the stamp. No overlay covers a record key today, so it is not reachable. carrier: none"
      ],
      "files_changed": [
        ".changeset/8231-machine-name-chip-source-label.md (+24)",
        "packages/app-shell/src/views/metadata-admin/SchemaForm.tsx (28 lines changed)",
        "packages/app-shell/src/views/metadata-admin/metadata-form-i18n.ts (+76)",
        "packages/app-shell/src/views/metadata-admin/field-source-label.ts (new, 63)",
        "packages/app-shell/src/views/metadata-admin/mergeServerFields.ts (19 changed)",
        "packages/app-shell/src/views/metadata-admin/SchemaForm.deDeveloperize-8218.test.tsx (87 changed)",
        "packages/app-shell/src/views/metadata-admin/SchemaForm.machineNameChip-8231.test.tsx (new, 208)"
      ],
      "line_counts": "7 files, 484 insertions, 21 deletions vs base 8b1f06619",
      "head_sha": "d35e00806432095394b27870d0ee539a719d29e6",
      "deviations": [
        "The file surface grew beyond the claim: field-source-label.ts (new leaf) and mergeServerFields.ts (producer-side JSON clone). Both are needed; see summary. The claim comment was not edited.",
        "The ruling's refreshInterval/'Auto refresh' example does not match the spec, which authors no field labels. The locale-independent predicate still holds, and the present-case is shown with a fixture source label.",
        "Worktree removed after the PR opened. node_modules deleted first, git worktree remove with no --force."
      ]
    }

    Generated by Claude Code

  9. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10320 at d35e008 · pm:dispatched → pm:blocked on objectui#10287

    domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T18:06Z. Reviewed against the diff, the tree and CI.

    Implemented-by:  claude/issue-8231-machine-name-chip-source-label
    Reviewed-by:     session_01D486C1axmXnrkJMNUfz2eb
    
    item reading
    shape draft · base main · line 1 Fixes #8231 · line 2 Clause-②: no
    ruling letter 1 delivered; the pre-authorised fallback 3 was not needed
    scope 7 files. Two sit outside the claim (a new private leaf, and the mergeServerFields shallow copy); both are disclosed, required, and accepted by the review
    contract review PASS at d35e008 (75/75 at tier), recorded on the PR. This seat corrected two imprecise figures in the PR body
    ruling premise the 「refreshInterval / Auto refresh」 example does not match spec 17.4.0 (no authored labels; the field is refreshIntervalSeconds). The rule holds; noted for the director

    The PR waits outside the queue on Spec Main Shape Gate (objectui#10287; fix PR objectui#10315 is open). The card body gains Blocked-by: #10287 and Unlock-action: re-check PR #10320.


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    🟢 LANDED — PR objectui#10320 merged as 78b572f60 (merge queue)

    domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T19:23Z. Merged 2026-09-24T19:22Z. Ruling 1 (5749674288) is on main.

    Verified by content on objectui origin/main: FieldRow in packages/app-shell/src/views/metadata-admin/SchemaForm.tsx judges the machine-name chip against the untranslated source label (untranslatedFieldLabel, new field-source-label.ts). metadata-form-i18n.ts keeps the source string beside its overlay. The objectui#8218 pin SchemaForm.deDeveloperize-8218.test.tsx is rewritten together with its reason, not deleted, and the new pin SchemaForm.machineNameChip-8231.test.tsx is present. The squash commit carries no model-named trailer.

    Fixes #8231 closed the card completed, but the auto-close does not strip labels; pm:dispatched is removed in this stroke. The other labels stay.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seati18npackage: app-shellpriority:p3

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions