fix(tooling): check-i18n-coverage 逐 config 收集失败,第一个失败不再吃掉其余十一个 (#6033) - #6310
Merged
Merged
Conversation
#6032 让「CLI 未构建」这一个前置有了结论式回答,但 example 自身 workspace 依赖未构建那条路径原样保留:`countI18nIssues` 里三处裸 throw,per-config 循环没有 try/catch,于是第一个 lint 不了的 config 以一段 node 栈中止全程, 后面十个根本没被尝试过 —— 它们各自的成因(未必与第一个相同)也就从未出现。 `errors[]` 收集器在这条路径上永远走不到。 改为:`measureI18nIssues` 返回 `{count}` 或 `{failure}`,永不为单个 config 抛异常;`measureAllConfigs` 跑完整轮并收集失败;整轮结束后 `reportUnmeasuredConfigs` 一次性报告 **每个不同成因**(按结论去重),每条带 它覆盖的 config、CLI 原话读数与一句可行动修法。#5217 立的规矩(一个原因不 要报成 N 个结果)因此在反方向上同样成立:N 个不同原因不被第一个吃掉,而一个 共有原因也只说一次。 失败时抢在 `--update` 写盘与比对之前退出:半轮测不出结论 —— 没测到的 config 与被删掉的 config 无法区分,`--update` 会冻结幸存者、悄悄丢掉其余,把真实债务 从 baseline 里棘轮出去。与 `reportPrerequisiteNotMet` 同一条不变量:没测就不写。 CLI 未构建那条前置仍然在第一个 config 就中止 —— 那是有意的,每个后续 config 都会因同一个环境原因失败,继续跑只会把一个事实印十二遍。 `--self-test` 增加第三组断言:真实语料(仅构建 CLI 的树上录得的 report.error) 须点名缺失包并给出 `pnpm build`;真正坏掉的 config 不得被判成「没构建」;无法 识别的成因仍须产出完整判词;注入式 `measure` 证明一个 config 失败不中止整轮, 且不同成因分开、共有成因合一。CI 永远走不到这条路径(lint.yml 先构建),所以 这些断言只可能钉在这里。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
hotlong
marked this pull request as ready for review
August 7, 2026 13:50
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6033
前提复核(行号漂移核对)
分诊评论(2026-08-07T06:56Z)的落点是按
ede5a8e给的行号。在本 PR 的基点6131d90上按内容重新定位,四处全部命中,且行号几乎没有漂移::178无输出裸 throw:178if (!raw.trim()) throw …:183JSON 不可解析裸 throw:183:185report.error裸 throw:185:367-369per-config 循环无 try/catch:367-370(const current = {}占 367):380-411errors[]收集器:380-409premise 成立,并且是实测复现的,不是读代码推断的。在一棵
pnpm install之后只跑了
pnpm exec turbo run build --filter=@objectstack/cli的树上,跑origin/main版本(逐字节原样,存放在 worktree 之外):注意它死在第 2 个 config:第 1 个(app-crm)已经量到了数,第 3 到第 12 个
从未被尝试。这正是本单要修的形状 —— 诊断没说谎,但它吃掉了其余十一个。
改动叙述(单文件:
scripts/check-i18n-coverage.mjs)countI18nIssues改名measureI18nIssues,返回{count}或{failure},不再为单个 config 抛异常。原来三处裸 throw 各自变成一条带成因的失败:
无输出(附 stderr 读数 —— 此前 stderr 只喂给签名网,其余丢弃)、JSON 不可
解析(附截断后的载荷头)、
report.error(交给分类器)。改名是因为职责变了;头注释里保留了含旧函数名的那段栈,grep 旧名仍能落到本文件。
explainConfigFailure:把 CLI 原话变成「结论 + 一句修法」。唯一值得做的判别是模块解析 —— 指向某个包
dist/的Cannot find module⇒ 「已安装但没有构建产物」+
pnpm build;只点名包 ⇒pnpm install && pnpm build;其余一律保留 CLI 原话并给出手动重跑命令。真正坏掉的 config 不会被判成
「没构建」——那会是 check-i18n-coverage 也是「声明了但没强制」的构建前置 —— 未构建时抛未捕获异常,并把成因指向 examples/app-crm 的配置 #5862 那个「自信却指向无辜处」的缺陷换个层级重演。
measureAllConfigs(configPaths, measure):跑完整轮、收集失败;measure是注入的,所以
--self-test能在没有 CLI、没有构建的情况下证明「一个 config失败不中止整轮」——而这恰恰是 CI 永远观察不到的行为。
reportUnmeasuredConfigs:整轮结束后一次性报告,按结论去重。check-i18n-bundles 在工作区未构建时把「CLI 没 build」报成 9 个包各自的 bundle 问题 #5217 的规矩(一个原因不要报成 N 个结果)在这里必须继续成立:十二个 config
因同一个包没构建而失败,是同一个事实说了十二遍,只能算一条成因、列出它覆盖的
config。反方向(本单)则是 N 个不同成因各占一条,不被第一个吃掉。
--update写盘与比对之前退出。这一条不是省事,是安全性:半轮没有结论可给 —— 没测到的 config 与被删掉的 config 无法区分,DOWN 方向会
叫读者去
--update,而--update跑在任何比对之前,会冻结幸存者、悄悄丢掉其余,把真实债务从 baseline 里棘轮出去。与
reportPrerequisiteNotMet同一条不变量:没测就不写。
都会因同一个环境原因失败,继续跑只会把一个事实印十二遍。
用户可见面为零(仓内工具链,不发布任何包)⇒ 无 changeset,PR 打
skip-changeset。反向验证(先申报预期,后执行)
声明 A(回归面)—— 申报
完整构建后(等价 lint.yml 的 Build workspace packages),
origin/main版与本版在同一棵树上跑,stdout/stderr 与退出码逐字节一致。
实测:一致。
--update写盘路径同样未变:完整构建后跑--update,baseline 的 md5 与提交态相同。唯一的合理差异(申报在此,不算违反 A):
--self-test的成功摘要行按惯例列出它证明了哪些分类器,本 PR 新增了一个,所以那一行文字变了:
… the missing-CLI-build and i18n-rule classifiers both go red, and stay distinct.… the missing-CLI-build, i18n-rule and per-config-failure classifiers all go red, stay distinct, and a failing config does not end the round.声明 B(目标面)—— 申报
树的状态:
pnpm install后仅pnpm exec turbo run build --filter=@objectstack/cli(55 个 task;
packages/connectors/*四个包确认无 dist)。N = 12 个 config(3 个 example + 9 个 package extract config),先数出来再跑。
预期:① 输出中不出现 node 栈;② 12 个 config 全部被尝试;③ 恰好 1 个失败 ——
examples/app-showcase,成因点名@objectstack/connector-mcp「已安装但没有构建产物」,修法
pnpm build,其余 11 个量到数;④ 末尾一次性报告,退出码 1;⑤ baseline 不被改写。
(为什么只预期 1 个:app-crm / app-todo 只 import
@objectstack/spec(已构建),9 个 extract config 只 import
../src/*与 spec,唯有 app-showcase import 了四个未构建的 connector。这是数出来的,不是猜的。)
实测:五条全中。
md5sum -c确认 baseline 未被改写;同状态下再跑--update,同样是这条判词、EXIT=1、baseline 未动(改动第 5 点的安全性,实测过,不是只写在注释里)。
声明 B2(多 config / 共有成因)—— 申报过、结果与预期不符,如实记录
申报:把
packages/spec/dist移走,12 个 config 应全部以同一个成因失败,报告应显示 1 条成因覆盖 12 个 config(而不是 12 条判词)。
实测:不符,原因是探针选错了,不是实现不符预期。 移走 spec 的 dist 会让
CLI 自己跑不起来,于是 #5862 的签名网先一步命中,整轮在第 1 个 config 就以
PREREQUISITE NOT MET — the built CLI cannot resolve the command this gate runs中止 —— 这正是那条前置应有的行为,顺带成了「本 PR 没弄坏 #5862 那条路径」的
回归证据。
第二次尝试:改为移走三个叶子包的 dist(
observability/trigger-record-change/
plugin-sharing),预期这些 extract config 会各自失败。实测仍只有 1 个失败—— 因为 9 个 extract config 只 import
../src/下的对象与译文定义文件,那些文件只依赖
@objectstack/spec,压根不碰这些运行时包。同一棵树上跑origin/main版,它照旧只报 app-showcase 一条然后带栈死掉。
结论(如实申报,不改预期迁就结果):今天这棵仓库的拓扑下,不动
examples/**就构造不出「多个 config 因不同成因失败」的自然状态 —— 唯一能这样失败的是
app-showcase。所以「N 个成因不被第一个吃掉」这条性质由
--self-test里的注入式measure确定性地钉住(4 个 config、2 个不同成因、其中一个还是 throw 出来的),而不是靠一次凑出来的实跑。这些断言不是摆设,已用变异体验证会红:
measureAllConfigs的 try/catch#6033 one cause is stated once — got 3 cause(s) for one missing package等 2 条红 → EXIT=1门禁 EXIT 表(均在
git add之后跑)pnpm check:nul-bytesnode --check scripts/check-i18n-coverage.mjspnpm exec eslint scripts/check-i18n-coverage.mjs --no-inline-configpnpm check:i18n-coverage(self-test && 实跑)pnpm check:i18ngrep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'package.json未改(check:i18n-coverage早已接好--self-test,新断言直接进既有自测,不需要新文件、不需要新接线)。
不在本 PR 里
.github/workflows/**、examples/**、packages/**、content/**;文件面就是
scripts/check-i18n-coverage.mjs一个文件。scripts/cli-build-prerequisite.mjs,也不改 check-i18n-coverage 也是「声明了但没强制」的构建前置 —— 未构建时抛未捕获异常,并把成因指向 examples/app-crm 的配置 #5862 那条前置的措辞与时机(它在第一个 config 中止是对的)。
dev脚本没有任何一步确认工作区已构建 —— 前置检查只有 check:console-sha,未构建时放行进入 12 段噪音现场 #5795(根dev入口)/ datasource fail-fast 不认识「工作区未构建」这个成因 —— 对 ERR_MODULE_NOT_FOUND 仍建议改配置或设 OS_ALLOW_DRIVER_CONNECT_FAILURE=1(两条都是有害建议) #5794(datasource fail-fast)只读参考,未顺手修。.changeset/:仓内工具链,不发布任何包 ⇒ 走skip-changeset标签。Generated by Claude Code