Skip to content

record:details 分区标题存在 title/label 双拼写:三个消费方都读 title ?? label,但 spec 只声明 label #5730

Closed

Description

@os-zhuang

实施 #5611(RecordDetailsProps.sections 改对象形式)时实测到的范围外问题,只记录不修。

实测

record:details 分区的标题有两个拼写,三个消费方都做了 title ?? label 或 label ?? title 的容忍读取:

消费方 位置 读法
objectui 渲染器 plugin-detail/src/renderers/record-details.tsx:193 s.title ?? s.label
仓内 i18n 抽取 packages/cli/src/utils/i18n-extract.ts:401 s.label ?? s.title
仓内 lint packages/lint/src/validate-translatable-sections.ts:329 strName(section.label) ?? strName(section.title)

而授权侧只有一种拼写:

  • 仓内全部 5 个真实 section(3 个 showcase 页 + sys-user.page.ts)都写 label,零处写 title;
  • objectui 的类型镜像 RecordDetailsComponentProps.sections[] 只声明 label;
  • objectui 的 Studio 区块设计器 block-config.ts:312 的 itemFields 也只产出 label。

唯一写 title 的地方是一个合成测试夹具:packages/cli/test/i18n-section-coverage.test.ts:220({ name: 'timeline', title: 'Timeline', fields: [...] })。

#5611 的 PR 据此把 sections[].label 声明为 I18nLabelSchema.optional()、不声明 title —— 按 Prime Directive #12,授权侧的事实拼写是 label,title 属于消费方容忍,不应写进契约固化成第二套正名。

为什么今天不痛、但值得记

今天没有作者写 title,所以没有用户会踩到 —— 故打 finding 不进队列。

值得记的是方向:#5611 之后 title 成为 spec 明确不认的拼写,而三个消费方仍然优先读它(objectui 甚至把 title 排在 ?? 链前面)。这正是 Prime Directive #12 说的"消费方容忍把错误约定化石化成第二套事实契约":一个照着 objectui 源码而不是照着 spec 写元数据的作者,会得到一个 spec 拒绝、渲染器却正常显示的页面。#5068 闸门落地后,这类页面会在 publish 期硬失败,而错误信息指向的是作者从渲染器里学来的拼写。

修复方向

按 #5611 已确立的方向清掉容忍侧,而不是补声明:

  1. 仓内两处(i18n-extract.ts:401、validate-translatable-sections.ts:329)去掉 ?? title 分支;
  2. packages/cli/test/i18n-section-coverage.test.ts:220 的夹具改写为 label(该夹具用 title 正是为了覆盖这条容忍分支,分支去掉后夹具应一并改拼写);
  3. objectui 侧 record-details.tsx:193 去掉 s.title ??(跨仓,需单独 PR)。

注意第 2 条是 #5046 记下的"夹具三分法"里的整体替换类:那个夹具钉的正是要删的那条分支,分支删掉后它会因为"什么都没产生"而继续通过,而不是因为逻辑正确。

Activity

  1. claude commented on Aug 6, 2026

    @claude
    Contributor

    发现分诊轮判级(#4949 纪律):持有(finding 留)。

    为什么还留着:方向上没有疑问 —— 授权侧事实拼写是 label(仓内 5 个真实 section 全写 label,objectui 类型镜像与 Studio 区块设计器也只产出 label),唯一写 title 的是一个合成测试夹具;#5611 的 PR 已按 PD #12 只声明 label、不把消费方容忍写进契约。剩下的是清掉三处 title ?? label 容忍读法,但它跨三个车道:objectui 渲染器(repo:objectui)、packages/cli 的 i18n 抽取(domain:cli)、packages/lint(domain:devx)。为一个零用户影响的清理做 contract-first 拆分(父单 + 三个 sub-issue + Blocked-by:),拆分成本明显大于收益。

    重启条件:① 上述任一车道的 PM 下次因别的单动到这三个文件时顺带收(本评论即为那时的依据,三处位置已逐一锚定:plugin-detail/src/renderers/record-details.tsx:193、packages/cli/src/utils/i18n-extract.ts:401、packages/lint/src/validate-translatable-sections.ts:329,外加合成夹具 packages/cli/test/i18n-section-coverage.test.ts:220);或 ② title 拼写首次出现在真实授权面(而非测试夹具)时立即晋级 —— 那意味着容忍读法已经开始生产第二套方言,正是 PD #12 要防的东西。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage — re-check of the least-recently-reviewed hold. Verdict: HOLD stands (finding kept). Both restart cells re-verified as NOT triggered.

    Reading method matters here. The natural check ("has anyone touched those three files since 08-06?") was attempted with git log --since and discarded: this checkout is shallow, and the log returns the same single boundary commit for every path — the false-reading shape Operational note 6 warns about. The reliable check is presence of the code itself on origin/main, which is what is recorded below.

    Cell ① — "a lane collects it while passing through": not triggered. All three tolerance reads are still present verbatim at their anchored positions:

    • packages/cli/src/utils/i18n-extract.ts:401 — addSection(index, objectName, s.name, s.label ?? s.title);
    • packages/lint/src/validate-translatable-sections.ts:329 — const heading = strName(section.label) ?? strName(section.title);
    • packages/cli/test/i18n-section-coverage.test.ts:220 — the synthetic fixture still authors { name: 'timeline', title: 'Timeline', ... }.

    Cell ② — "title appears on a real authoring surface": not triggered, and the evidence got stronger. Every record:details sections: array on origin/main was re-read: examples/app-showcase/src/ui/pages/project-detail.page.ts (3), settings.page.ts (2), task-detail.page.ts (3), packages/platform-objects/src/pages/sys-user.page.ts (4). All ~12 section entries write label; zero write title. ⚠️ Note the issue body's figure of "5 real sections" is now stale — the authoring surface roughly doubled since filing and still produced no title. That cuts in favour of holding: the tolerant reads are not, in practice, breeding the second dialect PD #12 warns about — which is precisely what cell ② was written to detect.

    One false-positive trap for whoever checks this next: a bare title: grep over *.page.ts hits plenty of lines (page:header titles, related-list titles, sys-user.page.ts:160/:200). Those are different properties. Cell ② is only about title inside a record:details sections[] entry — check the enclosing array, not the keyword.

    Hold reason unchanged: the cleanup spans three lanes (repo:objectui renderer, domain:cli extractor, domain:devx lint) for zero user impact today, so a contract-first split costs more than it returns. Restart conditions carry forward verbatim.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-project-manager commented on Aug 8, 2026

    @os-project-manager
    Collaborator

    Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue. title/label dual spelling with three tolerant readers while spec declares label only — the maintainer's standing family default (silently-tolerated second spelling → converge on the declared one); all real authoring already uses label, so the fix is dropping the tolerant reads, zero migration. finding → pm:queue.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions