Skip to content

fix(cli,spec): i18n coverage gates view labels — the defineView() container is no longer skipped (#4123) - #4132

Merged
os-zhuang merged 1 commit into
mainfrom
fix/i18n-view-coverage-4123
Jul 30, 2026
Merged

fix(cli,spec): i18n coverage gates view labels — the defineView() container is no longer skipped (#4123)#4132
os-zhuang merged 1 commit into
mainfrom
fix/i18n-view-coverage-4123

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4123.

i18n/missing-view had 零产出'view'CoverageIssue 的类型里,walker 里有对应代码,check-i18n-coverage 的头部注释也声称覆盖 listViews —— 但这个仓库里没有任何一个视图 label 被门禁看到过,而门禁一路报绿。

根因

collectExpectedEntries 认两种视图形状,编译产物两种都不是

  1. 对象内嵌 listViews —— 编译后的 object 上没有 listViews(三个 example 实测全 0;只有手写的 platform object 用这种形状)。
  2. 顶层具名视图 —— 被 if (!view?.name) continue 挡住。

defineView() 产出的是聚合 容器{ list, listViews, formViews }。按 spec(view.zod.ts)它没有顶层 name —— 它由目标对象隐式定键,对象名在 list.data.object,正是 objectql resolveMetadataItemName 的解析方式。所以那个 guard 拒绝的是 spec 自己的容器形状,也就是每个 example 声明的全部视图。

度量(同 commit、同配置,修前 → 修后)

example total source=view
app-crm 1402 → 1420 0 → 18
app-showcase 2210 → 2250 0 → 40
app-todo 1343 → 1349 0 → 6

其它 source 一条没动,总数正好等于视图增量。

键名依据

容器遍历沿用既有约定 objects.<object>._views.<view>.*,三处独立佐证对齐:

  • 运行时解析器读的就是它(viewLabel in @object-ui/i18n
  • 已发布的 platform bundle 就是这个形状(sys_user._views.all_users.label
  • 对象名回退链与 resolveMetadataItemName 一致

匿名的默认 list 落在 _views.list,对应控制台的 primary.name || 'list'。具名 list view 可以通过自己的 data 改指别的对象,此时以它自己的对象为准。

formViews 有意不覆盖:表单视图在 viewLabel / _views.* 这套解析约定里没有对应物,为它们发键等于期待没人会读的翻译。

附带的类型修正

StrictObjectTranslation 补上 ObjectTranslationDataSchema 本就允许的 _views。它无法从对象类型推导(视图是分开声明的),所以是可选、松散键控,不像 fields 那样枚举。不补的话,satisfies StrictObjectTranslation<…> 会拒绝门禁新要求的那些翻译(app-todo 命中)。

新增的 35 条:翻译,不抬 baseline

沿用 object-less action 那次立的先例 —— 抬 baseline 等于把这个门禁存在的意义又放宽一次。

验证

门禁是真活的,不是碰巧绿的:删掉一条视图翻译 → app-todo 120 → 121 并失败;恢复 → OK。

  • check-i18n-coverage:665,none new
  • os lint:三个 example 各 0 errors
  • spec 7022 测试 / cli 877 / showcase 60 / crm 20 / todo 78 全过
  • tsc、eslint 干净

发现于 objectui#2960 的验证过程(#4101 加 showcase specimen 时顺带查证 i18n 门禁会不会变红,结果发现它根本不会)。

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 11:04am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tooling labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

…tainer is no longer skipped (#4123)

`i18n/missing-view` had ZERO producers. The `'view'` source exists in
`CoverageIssue`, the walker has code for it, and check-i18n-coverage's header
claims listViews is covered — but no view label in this repo had ever been
gated, and the ratchet reported green the whole time.

`collectExpectedEntries` recognized two view shapes; the compiled config is
neither:

  1. Object-nested `listViews` — objects do not carry `listViews` once
     compiled (0 across every example; only hand-authored platform objects
     use that shape).

  2. Top-level NAMED views — guarded by `if (!view?.name) continue`.

`defineView()` emits the aggregated View CONTAINER — `{ list, listViews,
formViews }` — which per spec (`view.zod.ts`) has no top-level `name`: it is
keyed implicitly by its target object at `list.data.object`, exactly as
objectql's `resolveMetadataItemName` resolves it. The guard therefore rejected
the spec's own container shape, i.e. every view every example declares.

Measured on the real compiled configs, same commit, before → after:

  app-crm       total 1402 → 1420  |  view 0 → 18
  app-showcase  total 2210 → 2250  |  view 0 → 40
  app-todo      total 1343 → 1349  |  view 0 →  6

No other source moved; the totals rise by exactly the view counts.

The container walk emits under the same `objects.<object>._views.<view>.*`
convention the runtime resolver reads (`viewLabel` in @object-ui/i18n) and the
shipped platform bundles already carry (`sys_user._views.all_users.label`). An
unnamed default `list` resolves under `_views.list`, matching the console's
`primary.name || 'list'`. A named list view may retarget another object via its
own `data`, so its own object wins over the container's. `formViews` stays
uncovered on purpose: form views have no counterpart in that resolver
convention, so emitting keys for them would expect translations nothing reads.

`StrictObjectTranslation` gains the `_views` slot `ObjectTranslationDataSchema`
already permits — it is not derivable from the object type (views are declared
separately), so it is optional and loosely keyed rather than enumerated like
`fields`. Without it, `satisfies StrictObjectTranslation<…>` rejects the very
translations the gate now asks for (app-todo).

The 35 newly surfaced strings are TRANSLATED, not ratcheted — the precedent
from the object-less action: raising the baseline makes the gate green by
widening the debt it exists to prevent.

Verified the gate is genuinely live on view keys rather than green by accident:
deleting one view translation moves app-todo 120 → 121 and fails; restoring it
returns to OK. check-i18n-coverage: 665, none new. os lint 0 errors on all
three examples. spec 7022 tests, cli 877 tests, showcase 60, crm 20, todo 78.
tsc + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xuyushun441-sys
xuyushun441-sys force-pushed the fix/i18n-view-coverage-4123 branch from a98c8cc to 1f976b7 Compare July 30, 2026 11:04
@os-zhuang
os-zhuang merged commit 9a4932a into main Jul 30, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the fix/i18n-view-coverage-4123 branch July 30, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n coverage never gates view labels: every defineView() bundle is skipped by the !view.name guard

1 participant