fix(spec): 参考页开篇只认「不属于任何符号」的模块级 doc block (#5059) - #6134
Merged
Conversation
`getFileDescription()` 取整个文件里第一个 `/** */` 块原样做参考页开篇。 这不是一条关于「描述」的规则,而是一条关于「顺序」的规则:哪个声明碰巧排 在文件最前面,它的注释就被搬上公开文档页。#3746 陷阱 1 早就写明这一点, 而它已经落到 main 上两次,`check:docs` 全程绿 —— 那道门比对的是生成物与 源码是否一致,而生成物确实忠实复制了那个错误的块。 改为按 TSDoc 自己的规则选块(`lib/file-description.ts`):一个 doc block 属于它紧邻其后的那个声明 —— 这正是编辑器 hover 该符号时显示的文本。所以 只有同时满足三条的块才是模块描述:顶层(不在声明体内缩进)、位于首个声明 之前(import / re-export 不算声明)、且其后不紧跟声明。取不到就不输出描述 —— 宁可缺,不要错。 规则本身即门禁:issue 正文提议的「首句模式」检查只能覆盖 history 常量那 一个子类,且只能在发布之后发现;实测的 6 张受害页里有 4 张附近根本没有 history 常量。选块规则从结构上取不到符号注释,整个类别就不可能再发生。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
纯生成物提交,`gen:schema && gen:docs` 的输出,284 行删除 / 0 行新增。 每一页移除的都是某个符号的 JSDoc 被当作模块描述发布的那一段;没有任何一页 丢失真正的模块级文件头(178 张带 Source 行的页面描述一字未动)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
…erence-page-descriptions
|
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). ✅ |
This was referenced Aug 7, 2026
os-zhuang
marked this pull request as ready for review
August 7, 2026 03:24
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 #5059
前提复核(对 origin/main)
issue 正文与 08-06 08:31Z 实测评论的前提全部成立,在 #6069 分片与 #6096 之后依然如此。6 张受害页在合并基线上逐字复现:
data/mappingShared history for this file (#4001).system/translationShared history sentence for every shape in this file (#4001).api/contractMachine-readable semantic code (ADR-0112): a StandardErrorCode member or …api/protocolResponse for GET /api/v1/automation/actions (ADR-0018).api/realtimeTransport Protocol Enumkernel/pluginShared Plugin Types按派发指示采用实测评论的方向:改生成器的取块规则,不动
packages/spec/src/**/*.zod.ts。本 PR 的 diff 一个 zod 文件都没碰。修法:按 TSDoc 自己的规则取块
getFileDescription()取整个文件里第一个 doc block 原样发布。这不是一条关于「描述」的规则,而是一条关于顺序的规则 —— 哪个声明碰巧排在文件最前面,它的注释就被搬上公开页。check:docs结构上看不见:它比对生成物与源码是否一致,而生成物确实忠实复制了那个错误的块。新规则就是把 TSDoc 的语义读回来:一个 doc block 属于它紧邻其后的那个声明 —— 那正是编辑器 hover 该符号时显示的文本。所以模块描述必须同时满足:
api/contract发布的就是ApiErrorSchema.code的注释),永远不可能是文件头;const/export const/ … 关闭;// ═══横幅把 JSDoc 和它文档的符号隔开,所以横幅(或另一个 doc block、或一条 import)出现在中间,就说明前面那块写的是模块而不是那个声明。取不到就不输出描述。宁可缺,不要错:一段缺失是读者看得见的空白,一段自信渲染的内部注释是一张说谎的页面。
规则本身即门禁。issue 正文提议的首句模式检查(
#\d{3,}/Shared history/Until #)只能覆盖 history 常量那一个子类、且只能在发布之后发现 —— 6 张受害页里它只盖得住 2 张。取块规则从结构上取不到符号注释,整个类别不可能再发生。代码落在
packages/spec/scripts/lib/file-description.ts(从build-docs.ts抽出,沿用format-type.ts#4912 与escape-mdx.ts#5452 的既有路子),pin 套件packages/spec/scripts/file-description.test.ts,14 条用例含一条对真实packages/spec/src全树的语料门。描述发生变化的全部 20 张页面(逐张归因)
纯删除:284 行删除 / 0 行新增,20 张页面,除此之外
content/docs/references/**一字未动。A. #4001 history 常量类(2 张) —— 块文档的是文件里私有的
const *_HISTORY:data/mappingconst MAPPING_HISTORY(L19)system/translationconst TRANSLATION_HISTORY(L24);另外export const LocaleSchema(L12)已先关闭头部区B. 靠前的内部声明注释类(18 张) —— 与 #4001 无关,只是某个符号的 JSDoc 排在了最前:
api/contractApiErrorSchema.code(缩进在对象字面量内;export const ApiErrorSchemaL12 已关闭头部区)api/protocolexport const AutomationActionsResponseSchema(L76;export const AutomationTriggerRequestSchemaL58 已关闭头部区)api/realtimeexport const TransportProtocol(L15)kernel/pluginexport const CORE_PLUGIN_TYPES(L89;export const PluginContextSchemaL8 已关闭头部区)ai/solution-blueprintconst SNAKE_CASE(L24)ai/toolconst TOOL_RETIRED_KEY_GUIDANCE(L35)api/error-code-ledgerexport const ERROR_CODE_LEDGER(L38)api/routerexport const RouteCategory(L14)automation/approvalexport const ApproverType(L30)cloud/template-manifestexport const TemplateManifestSchema(L12)data/driver-mysqlconst MYSQL_CONFIG_KEYS(L30)data/driver-postgresconst POSTGRES_CONFIG_KEYS(L28)data/driver-sqliteconst SQLITE_CONFIG_KEYS(L30)kernel/manifestexport const PluginPermissionsSchema(L33)shared/enumsexport const SortDirectionEnum(L21,单行 JSDoc)system/docexport const DocSchema(L30)system/notificationexport const NotificationChannelSchema(L19)ui/responsiveexport const BreakpointName(L73)B 类里有若干块(
cloud/template-manifest、三个 driver、system/doc、api/error-code-ledger)读起来像模块简介 —— 但它们被紧贴着写在模块首个 schema 之上,TSDoc 就把它们判给那个符号,IDE hover 该符号显示的正是这段话。把同一段文字再当作「模块描述」发布,是生成器给一段已经有归属的文本发明了第二重含义。要把开篇找回来,作者只需在 zod 文件里补一个不文档任何符号的块(zod 侧改动,不在本座位边界内,故本 PR 不做)。没有任何一张页面丢失了真正的文件头
带
Source:行的 200 张参考页里,178 张描述逐字节不变;GAINED与新增行数都是 0(本次不给任何页面新增描述),原本就没有描述的 2 张(data/hook-body、qa/testing)保持不变。规则在设计阶段被反复收窄,正是为了这一条。两个更粗的候选各自被实测否掉:
api/websocket的真文件头(它写在 import 与一条 re-export 之后),并把 178 张页面砍到 20 张;api/analytics/ai/conversation一类的真文件头 ——scripts/lazify-schemas.ts把import { lazySchema } …插在「注释+import 前导段」之后,而那个前导段会吞掉 doc block,于是真文件头被自动改写工具挪进了 import 列表中间。最终规则对
api/websocket、system/settings-manifest、api/analytics、system/migration、ui/sharing等真文件头全部保留,并有 pin 用例逐条守住。反向验证(方向:普通向,事前预测,实测吻合)
把
findModuleDocBlock换回 #5059 之前的一行取块(source.match(/\/\*\*([\s\S]*?)\*\//)),预测「四条rejects…+ 语料门 + 六页门转红,三条keeps a real module header保持绿(两种取法在真文件头上本就一致)」。实测:三条 keeps 用例全绿 —— 这正是缺陷能存活的原因:在所有人想得到去看的输入上,新旧两种取法给出同一个答案。
验证
ESLint job 里那批家族门禁本地逐条跑过,24 条全 OK(含
check:slot-lookup—— #6100 的止血 #6104 已在本分支基线里)。合并origin/main(11 commits)后重跑 build +check:generated+ 全量 spec 测试 + lint,均绿;合并未产生任何需要重生成的产物。Changeset
@objectstack/spec: patch,具名。packages/spec的 npm 导出面一字未变,但重生成的参考页是读者今天就能访问的已发布内容 —— 沿用同族先例 #5606 / PR #6058(同为 docs-gen 修复、同为 npm 面不变而参考页变,带具名 patch changeset)。body 里逐条列出了 20 张页面与「178 张未动」这条对账。并行/邻接说明
lib/format-type.ts数值字面量引号)代码文件与本 PR 不相交;它同样会重生成references/**。本 PR 触及的 20 张页面全是纯删除开篇段,与类型单元格渲染无重叠,后落地者 merge + 重生成即可。getCategoryTitle()只给 UI/AI/API 大写,qa渲染成 "Qa Protocol"(参考页标题 + meta.json + 根索引导航三处) #5853(getCategoryTitle())与本 PR 无影响:同文件不同函数、不同区段,产物面也不相交(它改分类标题 /meta.json/ 根索引)。\{转义痕迹 #5553(同函数的 JSDoc 段落切分与花括号转义)因本 PR 更容易:渲染管线已被抽到lib/file-description.ts并有 pin 套件,不必再靠跑整个生成器再 grep.mdx来断言。同时它的受害面从 5 张缩到 4 张 ——system/doc在本 PR 里丢掉了那段描述,所以那处奇数反引号自然消失;另外 4 张(automation/flow-function、security/explain、shared/expression、system/settings-client)描述保留,缺陷仍在,docs-gen: 模块 JSDoc 按行拆成段落,跨行的行内代码跨度被切断 —— 5 张参考页正文露出裸反引号和\{转义痕迹 #5553 依旧成立。packages/spec/src/ui/theme.zod.ts与chart.zod.ts里那两段//警告(「本块之上不得出现 doc block」)在新规则下已经过度谨慎但依然无害:照做仍然安全。改它们属 zod 侧,不在本座位边界内,已另开观察项。Generated by Claude Code