fix(spec): docs-gen 把嵌套花括号整对包进行内代码,不再切成半对 (#5452) - #5550
Merged
Conversation
…ting it (#5452) The reference-docs escaper located a fragment's closing delimiter with `indexOf` — the first closer, not the matching one — so a nested pair was wrapped only up to its inner closer and the outer one leaked outside the inline-code span. `{{var}}` published as `` `{{var}` `` plus a stray `}`, on exactly the rows documenting template-variable syntax. The matcher now counts nesting depth. Escaping moves to `scripts/lib/escape-mdx.ts` (same reason `format-type.ts` was extracted in #4912: the generator is a side-effecting top-level script, so its output could only be asserted on by grepping emitted `.mdx`), pinned by `scripts/escape-mdx.test.ts` — unit cases plus a corpus gate asserting inline-code brace balance across all 224 generated pages. Regenerating fixes 5 rows on 4 pages: model-registry.mdx ×2, flow.mdx, solution-blueprint.mdx, analytics.mdx. The issue counted 3; the last two start with a single brace, so the `` `{{ `` grep could not see them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
…#5452) The reverse verification turns FIVE unit cases red, not four, and the defect spanned five rows of four reference pages, not three pages — both numbers were written before the corpus was measured. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
os-zhuang
marked this pull request as ready for review
August 5, 2026 19:16
This was referenced Aug 5, 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 #5452
排版说明:GitHub 会把 body 里的引号和尖括号在存储时转义,所以本文刻意不写裸引号/尖括号,证据改用表格与列表呈现(#5452 实施中踩到,已回读存储后的 body 校验过)。
前提复核(先于实现)
issue 的基线是
ed0d2aac0,已前移大半天。在origin/main@5acb93add上重新核对,3 处存量仍在,前提成立:根因
packages/spec/scripts/build-docs.ts的escapeMdxDescription把带定界符的片段包进行内代码,让 MDX 按字面渲染而不是当成 JS 表达式 / JSX 标签。它用indexOf找收尾定界符 —— 取的是第一个闭合符,不是配对的那个:所以任何嵌套对都只被包到内层闭合符为止,外层那个漏在代码跨度之外。
{{var}}于是输出成反引号只吃掉前半截、后面跟一个游离的}—— 恰恰落在「教读者怎么写模板变量」的那几行上,而成对双花括号本身就是被文档的东西。修法
改成按嵌套深度配对(新增
packages/spec/scripts/lib/escape-mdx.ts的findMatchingClose),整对落进同一个代码跨度。转义逻辑同时从
build-docs.ts抽到scripts/lib/escape-mdx.ts—— 和 #4912 抽format-type.ts是同一个理由:生成器是带副作用的顶层脚本,在此之前想断言它的输出只能整跑一遍再 grep 产物,这正是本 bug 能在 main 上活下来的原因。实测:是 5 处,不是 3 处
重新
gen:docs后改动 4 个页面共 5 行。除 issue 点名的 3 处外,另有两处同一根因但起手只有单个花括号,因此 issue 用的`{{grep 天然看不见它们:ai/model-registry.mdx`{{var}`+ 游离}`{{var}}`automation/flow.mdx`{{nodeId.field}`+ 游离}`{{nodeId.field}}`ai/solution-blueprint.mdx`{ status: { $in: [...] }`+ 游离}api/analytics.mdx`{ cube, query: {...}`+ 游离}issue(及派单)把验收写成「
grep -rn反引号加两个左花括号,应归零」。实测这条判据是错的:该 pattern 匹配「反引号后面跟两个左花括号」,而修好之后的正确输出`{{var}}`同样以反引号加两个左花括号开头。所以它修前修后都是 3 命中,内容变了、计数不动。归零只有在「参考文档里根本不许出现模板变量语法」时才成立,而那恰恰与本单目的相反。两条判据在同一语料(main 的 224 页 vs 本 PR 重生成后)上的实测:
下面的门禁测试用的是第二条判据的等价不变量,不是 issue 那条字面 grep。
门禁测试 + 反向验证
packages/spec/scripts/escape-mdx.test.ts(12 例)= 单元 pin + 全语料门禁。门禁判据是行内代码跨度里的花括号必须配平 —— 配平才是缺陷真正破坏的不变量(包裹把一对切成了两半),所以它能抓住上表那两处非双花括号开头的,并且在措辞改写后依然抓得住。实测语料信噪比:main 的 224 页 5 处违例(即上表 5 行),本 PR 重生成后 0 处,零噪声。
尖括号故意不设语料门禁:尖括号同时是比较运算符,校验规则里的比较式与 SemVer 区间前缀合法地带着不配平的一只,实测 11 处全部正确,设门禁纯属噪声。尖括号的嵌套改由正向单元用例 pin。反斜杠转义过的花括号(模块 JSDoc 那条路径写成反斜杠加花括号)在计数前先剔除。
反向验证 —— 两个方向都实跑了,方向为常规「还原缺陷 → 转红」。 把
findMatchingClose换回raw.indexOf(close, i + 1):方向 1:5 条嵌套定界符单元用例立刻转红,各自报出被切开的形状:
PromptTemplate的`{{var}}`—— 跨度只吃到内层右花括号,外层漏出flow的`{{nodeId.field}}`—— 同上`{ status: { $in: [...] } }`—— 单层起手的嵌套,同样被切方向 2:再跑
gen:docs让语料回到旧转义之下,语料门禁转红,报出的 5 行与 main 上那 5 行逐行一致:之所以是常规方向而非反转方向:这些用例断言的是修复产生的正向输出形状,不是某个 finding 的消失。单定界符与不配对的用例两个方向都是绿的 —— 这正是 bug 活下来的原因:凡是有人想到去看的形状,转义器都是对的。
还原修复后重跑
gen:docs,工作树与本 PR 提交逐字节一致(git status干净),生成物可复现。验证
刻意不做的三件事
email-config.zod.tsappNamedescribe 里的绕写保持原样。那段注释引用本单、历史成立,且措辞是作者面的选择,不是本单验收项。packages/spec/authorable-surface.base.json未纳入。跑gen:schema会顺手把baseRev推到当前 main 并吸收 spec: EmailServiceConfigSchema 未声明 CLI 实读的 queueDelivery / appName / defaultTemplateContext(与 #5104 同族,不同键) #5307 / fix(cli):OS_APP_NAME压过config.email.defaultTemplateContext.appName—— 恢复「env 逐项覆盖」契约 (#5448) #5498 新增的键 —— 那是gen:schema的副作用,与本单无关,且是并行 agent 的冲突热点,已还原。\{转义痕迹 #5553:模块 JSDoc 走的是另一条路径(getFileDescription按源码行join成段落),跨行的行内代码跨度被段落边界切断,5 张参考页正文露出裸反引号;根因与本单无关。变更集
@objectstack/specpatch。包导出与运行时行为零变化(改的是scripts/,不进dist),但参考文档是该包生成器的读者可见产物,被切坏的又正是教语法的那一列,所以走 changeset 而非 skip 标签。