Skip to content

fix(devx): check:i18n 把 unknown-authoring-key 判为失败,不再只判 bundle drift (#4804) - #4847

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4804-i18n-unknown-key-gate-fails
Aug 3, 2026
Merged

fix(devx): check:i18n 把 unknown-authoring-key 判为失败,不再只判 bundle drift (#4804)#4847
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4804-i18n-unknown-key-gate-fails

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4804

采纳 issue 裁定的方案 A:失败判定落在 scripts/check-i18n-bundles.mjs。不碰 os i18n extract 的公开退出码(B),不碰 ObjectStackDefinitionSchema(C)。packages/** 一行未改。

问题不是「lint 没看见」,是「看见了也拦不住」

#4167 的 unknown-authoring-key lint 把九处全看见了。它在每次运行里逐包打印:

defineStack: stack.name: 'name' is not a declared stack key, so its value is dropped at load — did you mean 'pages'?

但它出现在一次全绿check:i18n 里,于是被人眼过滤了九次。#4736 清的是症状。

实施中发现了一个 issue 正文没写到、但决定成败的事实:这条 lint 写的是 stderr,不是 stdout(defineStackconsole.warn)。而门禁用的是 execFileSync,它只在抛错路径上交出 stderr —— 退出码为 0 时那条流直接透传到终端,没有任何代码读过它。所以当时不是「读了但没判」,是压根没读。这也解释了为什么它能安稳待在绿色运行里。

两处都修了:

  1. execFileSyncspawnSync,两条路径都拿到两条流。不属于 lint 签名的 stderr 行原样回吐,捕获不吞任何诊断信息。
  2. 门禁此前只判 bundle drift,现在多一类判定 —— 命中 unknown-authoring-key 签名即非零退出。

覆盖面不需要维护清单:findConfigs 遍历 packages/,第十份配置落地当天就在保护里。

两类失败不合并(验收第 3 条)

drift 保留原有小节和原有解法(--write);新的一类自己一节,指名哪个包、哪个配置文件、哪个键,以及那个真正要紧的后果 —— 它在 load 时被丢弃,所以它想配置的东西从来没生效过。重新生成 bundle 修不好它,错误信息也这么写了。逐包状态行还带 ← UNDECLARED KEY 标记,扫一眼就能分辨,不用读到底下的报告。

两类同时命中时的真实输出(把 plugin-audit 的配置还原到 ffab8033b^ 再破坏它的一份 bundle):

  plugins/plugin-audit           DRIFTED (1)  ← UNDECLARED KEY

check-i18n-bundles: 1 bundle problem(s)

  • plugins/plugin-audit: 1 bundle(s) drifted from the schema

Regenerate and commit: node scripts/check-i18n-bundles.mjs --write
...

check-i18n-bundles: UNDECLARED AUTHORING KEY in 1 package(s)
These keys are parsed away at load — whatever they were meant to configure is NOT
in effect, and never was. This is a separate verdict from bundle drift below/above;
regenerating bundles will not fix it.

  plugins/plugin-audit  (packages/plugins/plugin-audit/scripts/i18n-extract.config.ts)
    • 'name' at stack.name is not a declared stack key — dropped at load (did you mean 'pages'?)

反向验证(验收第 4 条)—— 这条是本 PR 的核心证据

一个只在 main 上绿过的门禁,和一个什么都匹配不到的门禁无法区分(#4690 就是那样:无 MANIFEST 时静默 skip 退 0,永远不可能红)。所以两头都跑了。

红:把九份配置还原到 ffab8033b^(#4803 删掉 name: 之前的那个 commit),pnpm check:i18n 退出 1,九处全部报出:

  platform-objects               in sync (8 bundle(s))  ← UNDECLARED KEY
  plugins/plugin-approvals       in sync (4 bundle(s))  ← UNDECLARED KEY
  ... (九个包,逐一)

check-i18n-bundles: UNDECLARED AUTHORING KEY in 9 package(s)
...
  platform-objects  (packages/platform-objects/scripts/i18n-extract.config.ts)
    • 'name' at stack.name is not a declared stack key — dropped at load (did you mean 'pages'?)
  plugins/plugin-approvals  (packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts)
    • 'name' at stack.name is not a declared stack key — dropped at load (did you mean 'pages'?)
  ... (九条)
EXIT=1

顺带纠正 issue 正文的一处描述:dedupe 是按进程内的 path 去重的,而门禁对每份配置各起一个进程,所以九个包实实在在打印九条(上面这次「合并前」运行,以及用未改动的旧门禁跑同一份语料,都能看到九条 —— 旧门禁看到九条仍然退出 0)。判定挂在每一份被发现的配置上,不是只看全局出现过一次。

绿:当前 main(即本分支的基线),干净、无新增噪音(验收第 2 条):

✓ check:i18n --self-test — bundle-drift and undeclared-authoring-key classifiers both go red, and stay distinct.
  platform-objects               in sync (8 bundle(s))
  ... (九个包全部 in sync,零 UNDECLARED KEY 标记)

check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).
EXIT=0

测试:--self-test(仓库既有惯例)

check:route-envelope / check:published-files 等的惯例加了 --self-test,并接进 check:i18n(先自检、再真跑),所以「门禁能不能红」这件事本身进了 CI,而不是只在这个 PR 的描述里被断言过一次。它不需要 build、不需要 CLI —— 纯粹拿录下来的 CLI 输出喂两个分类器。用例覆盖:

$ node scripts/check-i18n-bundles.mjs --self-test
✓ check:i18n --self-test — bundle-drift and undeclared-authoring-key classifiers both go red, and stay distinct.

另外验过:--write 路径仍然把 40 份 bundle 逐字节重新生成(git status 对所有 src/translations/** 干净);单独破坏一份 bundle 时 drift 判定行为不变(验收第 3 条);npx eslint scripts/check-i18n-bundles.mjs 干净;check:release-notes / check:doc-authoring / check:nul-bytes 均绿。

gate 语义变宽这件事,写在了脚本自己身上(A 的诚实代价)

脚本头部注释重写了:开篇就列出它现在判的两类,以及各自的修法;并写明为什么这一类落在门禁层而不是 CLI 退出码。check:i18n 此前只判 drift —— 有人被它拦下时,不该需要读源码才知道它现在管什么。最终成功行也改成 all bundles in sync, no undeclared authoring keys

修法始终指向生产者:把键从配置里删掉。如果那个键是真需要,就在 packages/spec郑重地声明它 —— 不靠消费侧 fallback,也不靠把 schema 改 strict 来堵嘴(那会连 lint 本身一起静音,metadata-authoring-lint.ts 里写明了)。

改动面

  • scripts/check-i18n-bundles.mjs
  • package.json(check:i18n 前置一次 --self-test)
  • .changeset/check-i18n-fails-on-undeclared-authoring-key.md(不发布任何包)

未触碰 packages/**content/docs/releases/,以及本会话另两位 dev 在飞的 scripts/check-durability-degradation-log-level.mjsscripts/durability-degradation.baseline.jsonscripts/bump-objectui.sh


🤖 Generated with Claude Code

https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ


Generated by Claude Code

…t on bundle drift (#4804)

The #4167 unknown-authoring-key lint saw all nine offending
`scripts/i18n-extract.config.ts` files that #4736 later cleaned. It printed
`stack.name: 'name' is not a declared stack key, so its value is dropped at
load` once per package, on every run — inside a fully green `check:i18n`, so it
read as noise nine times running. #4736 removed the symptom; nothing stopped the
tenth copy.

Two things made the warning unenforceable, and both are fixed here:

  1. The lint writes to **stderr**, and the gate used `execFileSync`, which
     surfaces stderr only on the throw path. On a zero-exit run the stream went
     straight to the terminal — seen by nobody, judged by nothing. Now
     `spawnSync`, so both streams are read on both paths; stderr lines that are
     not the lint signature are re-emitted, so capturing costs no diagnostics.
  2. The gate judged bundle drift only. It now also fails on the
     unknown-authoring-key signature, in its own verdict.

Option A per the issue: the failure lands in the gate script. `os i18n extract`
keeps its exit code (B would write an internal hygiene rule into the public CLI
contract) and `ObjectStackDefinitionSchema` stays non-strict (C would silence
the lint itself — see `metadata-authoring-lint.ts`).

The two verdicts never merge. Drift keeps its section and its remedy
(`--write`); the new class gets its own, naming the package, the config path,
the key, and the consequence that matters — the value is dropped at load, so
whatever it was meant to configure is not in effect and never was. Regenerating
bundles does not fix it, and the message says so. Coverage needs no manifest:
`findConfigs` walks `packages/`, so the tenth config is gated the day it lands.

Proven able to go red, not merely observed green — the failure mode of #4690.
`--self-test` (now wired into `check:i18n` ahead of the real run) drives both
classifiers over recorded CLI output, asserts the drift text produces no key
finding and the key text produces no drift finding, and pins the exact
ffab803^ stderr line so a reworded message breaks the test rather than
disarming the gate. A reworded signature that still matches the weak pattern
fails as `unattributed` instead of passing. Reverse-verified against the nine
configs restored from ffab803^: all nine reported, exit 1.

Fixes #4804

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 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 Aug 3, 2026 9:52am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tooling size/m labels Aug 3, 2026
@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 10:13
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit f4847a6 Aug 3, 2026
21 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4804-i18n-unknown-key-gate-fails branch August 3, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check:i18n 应把 unknown-authoring-key lint 判为失败 —— 否则第十份 extract 配置还会照抄同一个错

2 participants