Skip to content

fix(lint): 声明了 label 却没有 name 的表单段落 —— 永远无法被翻译的标题,现在会告警 (#5417) - #5455

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-5417-lint-nameless-section
Aug 5, 2026
Merged

fix(lint): 声明了 label 却没有 name 的表单段落 —— 永远无法被翻译的标题,现在会告警 (#5417)#5455
baozhoutao merged 1 commit into
mainfrom
claude/issue-5417-lint-nameless-section

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#5417

前提核验(先证后写)

按操作规程,先在 origin/main(5a45b9bf1)上用最小 fixture 实测,确认 issue 的前提今天仍然成立 —— 两侧都对无名段落零告警:

两个核验脚本是临时文件,已删除,未进入提交。前提成立,继续实现。

做了什么

新增规则 validateTranslatableSections(规则 id translation-section-name-missing),加入 reference-integrity 套件,因此 os validate / os lint / os compile 三个命令同时生效。

判据即 PM 裁定的口径:声明了 label(或 detail 段落的 title)而没有 name 的段落,在其所属对象有任何翻译时告警

为什么这是第三条腿:_sections 以段落 name 为键,渲染器也只按 name 查找(sectionLabel(objectName, section.name, authored),无 name 时回落到作者写的 label)。于是无名段落 —— 既不产生任何「孤儿键」让 validateTranslationReferences 报,也不产生任何「期望键」让覆盖率走查要 —— 是构造上不可翻译,而我们所有门禁对它结构性失明。

锚集对齐

规则覆盖的 section 面与两处已落地实现严格一致(#5416 的 walker section 双源、#5422 的校验器锚集):

射程外且有理由:fieldGroups 派生段落的键就是 fieldGroups[].key,天然有 name;sections 写成按名索引的 map 时,map 的键就是 name。

为什么是生产端诊断,而不是消费端容忍

诱人的「修法」是在渲染器把 label 转成 slug 去捞第二把键。那正是 Prime Directive #12 禁止的宽松回落:它会把 _sections.[label 的 slug] 固化成与已声明契约并存的第二套事实契约,而且作者一改标题文案键就漂移。objectui#3373 已经把渲染器的 name 守卫钉为正确。所以缺陷在生产端,规则就放在生产端。提示里给出的 snake_case 名字只是建议作者写下来的名字,不是任何东西会去解析的键 —— 这句话写进了 hint 本身,免得 AI 作者反过来假设 label 就是键。

严重级别与 opt-in

  • warning,与同族规则同档(ADR-0072 D1):不崩、不死,只是一个标题留在源语言。方案 2(收紧 packages/specFormSection.name 为必填)未搭车,按 PM 裁定属破坏性授权变更,留待维护者表态;本 PR 不动 packages/spec、不动 packages/cli
  • 单语项目零告警:只有当段落所属对象在某个 stack.translations bundle 里有节点时才告警,与 computeI18nCoverage 的 opt-in 不变量一致。

仓内真实锚(实测,如实报告)

PM 点名的 showcase formViews.create 无名段落确实命中。实跑 pnpm --filter @objectstack/example-showcase validate:

✓ Validation passed (1109ms)
⚠ object "showcase_task" · formViews.edit · section "Task": ...
⚠ object "showcase_project" · page "showcase_project_detail" · record:details · section "Overview": ...

全配置共 14 条(6 条来自 form view,8 条来自 record:details 页面),退出码 0 —— 只有 error 会 gate,warning 走 advisory 通道,CI 不会因此变红。

一处如实更正:ContactViews(那个 { label: 'Who is this?' })从 views barrel 导出,但并未登记进 showcase config 的 views: 数组,所以它不在这 14 条里。测试里仍按 #5415 同款方式直接组合它(它在一个文件里同时给出「默认 form 四个段落都有 name」与「稀疏 create 覆写没有 name」的对照),另外补了一条用确实登记在 config 里TaskViews 的用例,免得「真实元数据」的说法被夸大。

测试

27 条新用例,含:HotCRM 形状逐条告警断言、opt-in 门四态、8 个锚点各自双向断言(无名 → 本规则报 1 条;有名 + 已翻译 → 本规则静默且 validateTranslationReferences 也接受,这就是「覆盖面必须一致」的共导断言,任一侧漂移就恰好红一半)、射程外四态、真实 showcase 两组。

反向验证(先判方向后跑):预判为 。删掉 collectViewSites 里的默认 form 锚点分支 → 4 条用例转红,全部是 expected [] to have a length of 3 but got +0 / length of 1 but got +0,即该分支承重、断言不是靠「什么都没产生」而绿。已还原。

消费半径扫查

规则跑在哪里,就扫哪里的 fixture(#5046 的教训:改在 packages/lint,坏的 fixture 在 packages/cli 的命令对等测试里)。packages/cliauthoring-rule-command-parityformView 用的是 sections: [] 且无 translations;i18n-section-coverage 走的是 fieldGroups 面。9 个消费半径内的 cli 测试文件全绿。


Generated by Claude Code

…e heading no key can address (#5417)
`_sections` is keyed by the section's `name`, and every renderer resolves a
section heading that way (`sectionLabel(objectName, section.name, authored)`),
falling back to the authored label when there is no name. So a section
authored with a `label` and no `name` is untranslatable by construction — and
both existing gates are structurally blind to it: there is no orphan key for
`validateTranslationReferences` to report, and the coverage walker (#5405)
emits one expected key per `sections[].name`, so a nameless section demands
nothing and the report reads 100%.
Measured: 70/70 HotCRM form-view sections are in that state at full declared
coverage; `os validate` over examples/app-showcase reports 14 (6 form views,
8 record:details pages) and still exits 0.
`validateTranslatableSections` (`translation-section-name-missing`) joins the
reference-integrity suite, so it runs on validate/lint/compile at once, and
reads exactly the anchors #5416's walker and #5422's fact set already agree
on — a container's `sections`, its DEFAULT `form.sections`, every
`listViews.*`/`formViews.*` sub-container, the same three on object-embedded
views, and `record:details` sections anywhere in a page's component tree via
the shared `walkPageComponents`. fieldGroups-derived sections are out of range
by construction (their key IS the name), as is a name-keyed `sections` map.
Warning, and opt-in: a section warns only when the object it renders under
carries some translation of its own, so a monolingual stack stays silent
exactly as `computeI18nCoverage` does. The fix is a diagnostic at the
producer — slugifying the label into a lookup key would fossilize a second
de-facto contract next to the declared one (Prime Directive #12, and the
renderer guard objectui#3373 pinned as correct).
Per the PM ruling on the issue, requiring `name` on `FormSection` outright
(the schema route) is a breaking authoring change left to the maintainer;
packages/spec is untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
@vercel

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 5, 2026 12:45pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/l labels Aug 5, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx(via @objectstack/lint)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint)
  • content/docs/releases/v17.mdx(via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: a form section with a label but no name can never be translated and nothing warns — 70/70 HotCRM form sections are in that state

2 participants

@baozhoutao@claude