fix(spec): 基线编码统一为裸字符,写盘端与文件共用一个序列化器 (#5990) - #6096
Merged
Merged
Conversation
`--update-import-baseline` 用 JSON.stringify 写裸字符,而仓内 `docs-import-surface.baseline.json` 落盘是 \uXXXX 转义。两者 JSON.parse 后逐条等价,门禁只读 parse 后的 entries,所以一直是绿的 —— 代价全落在复核: 每跑一次该命令,136 条 entry 行被原样重写一遍,真实的 1 行决定被埋掉。 选路 2(基线统一为裸字符 + 一次性转换提交),判据是该 ratchet 的设计意图: 只减不增、变化应当显眼。实测支撑 —— 本仓 305 个 tracked .json 中 151 个带 非 ASCII,只有这一个用转义;同类手工棘轮与全部生成物都落裸字符,且 lib/sharded-artifacts.ts 的 serializeShard 已把「2 空格 + 结尾换行」的裸字符 写法确立为分片规范字节。选路 1 会让这个文件成为全仓唯一的转义孤例。 两侧一起改,不只改一侧: - IMPORT_BASELINE_COMMENT 与新的 serializeImportBaseline() 移入 lib/docs-import-surface.ts,写盘端改为调用它; - 基线文件由生成器重写(内容 100% 来自生成器),entries 在 parse 层与 origin/main 逐条相同(136 条不变),唯一语义变化是 #6069 有意留下的 过期路径 api-surface.json -> api-surface/; - 新增字节级往返 pin:读committed 文件 -> JSON.parse -> 用同一个序列化器 重新序列化 -> 要求字节相同。它同时钉住编码、缩进、键序、结尾换行,以及 _comment 的新鲜度,任一侧再分叉即红。 复现修复:连续两次跑 --update-import-baseline,文件 sha256 不变(修复前每次 都产生 137 增 137 删)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 7, 2026 01:38
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 #5990
选路:方案 2 —— 把基线统一为裸字符,并留一次性转换提交
分诊要求二选一并在 PR 里写明选择与判据,这里明写。
判据 = 该 ratchet 文件的设计意图:只减不增、变化应当显眼。 两条路都能消除震荡,但只有方案 2 让这个文件与仓内其余部分服从同一条规则 —— 而「显眼」恰恰是靠复核者能一眼认出反常来实现的,让唯一一个文件用只有它自己用的编码,等于每次都要求复核者先记住这条例外。
选路的实测依据(不是偏好,是数据)
在本 PR 的 base(
e3ef52b5a)上逐文件量过:.json总数\uXXXX转义的也就是说,issue 标题里的「仓内 ASCII 转义编码惯例」这个前提本身是不成立的 —— 不存在这样一条惯例,这个文件是全仓孤例。同类的手工 shrink-only 棘轮(
dual-source-exports.baseline.json、react-declaration-parity.baseline.json、test-typecheck-debt.json、variant-docs.json)和全部生成物(api-surface/、authorable-surface/、json-schema.manifest/、spec-changes.json)一律落裸字符。更直接的一条:
packages/spec/scripts/lib/sharded-artifacts.ts已经有一个名为serializeShard()的函数,注释写着 "Canonical bytes of every shard: 2-space JSON, trailing newline",实现就是裸JSON.stringify(doc, null, 2) + '\n'。规范字节的写法在仓内已经有名有姓地确立过了,而且是裸字符。 走方案 1 等于让build-docs.ts去生产一种全仓没有第二处在生产的编码。一次性代价如实记:本 PR 的基线 diff 是 137 增 / 137 删。这是一次,换永久干净。
两侧一起改,没有只改一侧
分诊的 ⛔ 说得很明确,所以修法是把「写盘端的编码」和「文件的编码」从两个可以各自漂移的事实,变成同一个函数:
IMPORT_BASELINE_COMMENT与新增的serializeImportBaseline()移入packages/spec/scripts/lib/docs-import-surface.ts。移出build-docs.ts是必要的而不是顺手 —— 那个脚本 import 即执行,常量留在里面就没有任何测试能读到它。build-docs.ts的--update-import-baseline写盘改为调用serializeImportBaseline(gaps),不再内联JSON.stringify。基线 diff 的性质:确实只有那一处有意变化
该文件是
NOT_DRIVER_MANAGED的手工棘轮,所以按 #6069 的口径做了 parse 层比对:136 条 entries 在 parse 层与
origin/main逐条相同,137 行里 136 行是编码翻转,唯一的语义变化是_comment里api-surface.json->api-surface/—— 也就是 #6069 有意留给本单的那处过期路径(该文件在 #5837 分片后已不存在)。按分诊要求,编码归一化和路径修正落在同一个有意的 diff 里。字节层复核:转换后 138 个裸非 ASCII 字符、0 个
\uXXXX、0 个控制字节、单个结尾换行。修复的复现证明:命令现在是幂等的
这是本单真正的验收 —— 修复前每跑一次就产生一次满额 churn,修复后第二次跑是空操作:
防震荡的 pin(新增 4 个用例)
packages/spec/scripts/docs-import-surface.test.ts新增docs-import-surface.baseline.json bytes一组。核心是字节级往返:读 committed 文件 ->JSON.parse-> 用同一个serializeImportBaseline()重新序列化 -> 要求字节相同。它不需要 build、不需要json-schema/树(用文件自己的 entries 重序列化,比的是编码而不是 gap 分析),一次钉住编码、缩进、键序、结尾换行,外加_comment的新鲜度。_comment新鲜度这一条值得单独说:该字段不被任何门禁比对(entries才是判据),这正是 #6069 能改了源常量却留下过期文件的原因。以前不值得为一行散文去红,因为修它要付 137 行噪声;编码统一之后那次重跑就是 1 行 diff,所以现在值得让它红。 这是本单顺带买到的东西。另有一条把 #6069 那类漂移钉在源头:
IMPORT_BASELINE_COMMENT必须含API_SURFACE_DIR_NAME + '/'、且不得含LEGACY_MONOLITH_NAMES[API_SURFACE_DIR_NAME](即api-surface.json)—— 两个拼法都从sharded-artifacts.ts这个同时拥有它们的模块里取,所以将来再分片一次也不会悄悄留下描述旧布局的散文。反向验证(方向是事前预测的:三条回归路径都应转红)
预测:红。三种可能的复发方式各试一次,并确认红的是对应的那几条而不是全体:
\uXXXX(还原被删的那条肢体)_comment相关两条绿_comment改回api-surface.json_comment新鲜度 + 字节往返红;裸字符与常量那条绿serializeImportBaseline()里加回 ASCII 转义包装(写盘端回归)(c) 是关键的一条:它证明这个 pin 管的是两侧,而不是只把文件钉死、任由写盘端漂走。三次反向验证后已还原,
grep REVERSE-VERIFICATION无残留。Changeset:
skip-changeset标签,不写具名 patch按 #6049 / #6057 的口径实测了发布面,而不是凭印象:
packages/spec的files白名单是dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json。本 PR 改动的 4 个文件逐个比对:packages/spec/scripts/build-docs.tsscripts/不在白名单)packages/spec/scripts/lib/docs-import-surface.tspackages/spec/scripts/docs-import-surface.test.tscheck:published-files本身就禁止测试文件进产物)packages/spec/docs-import-surface.baseline.json且
dist/、json-schema/、api-surface/、authorable-surface/、json-schema.manifest/、content/docs/references/全部零 diff(gen:schema与写盘模式的build-docs都跑过,232 个生成文件原样重写)。发布面 delta 为零,即本 PR 不发布任何东西,按pr-automation.yml的路线 2(preferred)取标签而非空 changeset。验证
控制字节自查(本 PR 的散文与注释多处提到转义):
node scripts/check-nul-bytes.mjs绿,另按门禁盲区做了grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'逐文件自查,四个文件均无命中 —— 文中的\uXXXX一律是转义文本,不是裸字节。对同文件后续单的影响
#5853 / #5553 / #5059 都排在
build-docs.ts本单之后。三者都不碰基线写盘路径(分别是getCategoryTitle大小写、JSDoc 段落切断、参考页正文被内部注释顶替,全在页面渲染侧),语义上互不相干;本 PR 从build-docs.ts净删 12 行、只在 import 与写盘一行处留下改动,合并冲突面极小。要说影响,是轻微变容易:那三单一旦顺带动到IMPORT_BASELINE_COMMENT,或它们的改动让某个 schema 的 gap 增减而需要重跑--update-import-baseline,拿到的都会是 1 行 diff 而不是 137 行。🤖 Generated with Claude Code
https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5