Files
deepseek-harness/.agents/notes/implemented/process/2026-07-28-per-subsystem-cordis-surface-regions.zh.md
T

6.9 KiB

Agent Note: 按子系统生成的 cordis-surface 区块

Status: implemented

English | 中文

问题

一个子系统的文档过去分散在三个归属:手写的 subsystems 页面(介绍、数据结构、动词)、平铺生成的 docs/cordis-catalog/services.md 中属于它的 ctx.<key> 切片,以及平铺的 docs/cordis-catalog/events.md 中属于其事件作用域的切片。bash.md 的读者必须再打开两份文档,才能看到该页面正在描述的服务接口与事件;除了手工维护的链接,没有任何机制把这三个视图联系在一起。平铺目录还游离在双语语料之外(生成输出只有英文,故被排除在配对之外),于是这个参考面完全没有中文入口。

生成式目录决策本身(从源码生成、@mode 标签交叉校验、失败关闭的类型链接覆盖、ts cordis-catalog 围栏)不在质疑之列;改变的只是生成输出「落在哪里」。

决策

gen-cordis-catalog.ts 把每个子系统的服务与事件参考注入到该子系统自己的页面内部,置于 <!-- BEGIN GENERATED cordis-surface … --> / <!-- END GENERATED cordis-surface --> 标记之间;平铺的 services/events 目录随之删除。现在每个子系统由一个页面同时承载介绍、数据结构和生成的接线表面。

  • 人工维护、响亮失败的划分。 SERVICE_PAGE 把发现的每个 ctx.<key> 映射到恰好一个页面;EVENT_SCOPE_PAGE 映射每个事件作用域。生成器在两个方向上都会直接报错(既有被发现却未映射的服务或作用域,也有已映射但遍历不再发现的键或作用域),因此划分不可能相对源码表面发生漂移。独立的 AST 扫描读取 packages/*/*/src/** 下每一个 declare module 'cordis' merge 块,为投影在服务与事件两侧的盲区兜底:投影渲染不了的已声明 Context key 或 Events 成员必须在 SERVICE_WALK_EXEMPTIONS/EVENT_WALK_EXEMPTIONS 中带着点名理由,陈旧豁免直接报错,且投影渲染的一切也必须对扫描可见(扫描约定归事件兜底决定所有);教会投影渲染接口类型条目的后续工作由 TODO(cordis-catalog-interface-services) 标记。
  • 区块在配对两侧按字节一致。 生成器把同一份英文区块字节写入 foo.md 和 foo.zh.md,是对「围栏代码块在配对两侧逐字节一致」这一既有规则的延伸。verify-translation-pairing 新增了专门的区块一致性检查(标记语法归 translation-pairing.ts 中的 partitionGeneratedRegions 所有),能精确点名出现分歧或格式错误的区块;整篇文档的结构签名仍会把区块内容再覆盖一遍。
  • 带防护的配对自动记录。 一次改变区块字节的重新生成会让每个被触及的配对失去同步,因此生成器会自行重新记录配对的 .i18n.yaml,但仅限本次写入完全限定在区块内的情况:两侧记录的 blob hash 必须与写入前的字节相符,且两侧剥离区块后的内容必须没有变化。人工行文若有漂移,记录就保持陈旧,配对门禁因此仍会强制走正常翻译流程;全新的配对绝不自动记录(那归作者经评审的 --write 所有)。这样 .i18n.yaml 保持为纯粹的 git hash-object 值:不引入任何「剥离后 hash」的语义变化。
  • 继承层搬了家,而非消亡。 vendor 的 ctx 成员与 internal/*/loader/hmr/timer 事件渲染到 docs/cordis-api/inherited.md,紧邻迁移后的 Cordis 核心 API 页面(docs/cordis-catalog/core/ → docs/cordis-api/)。框架表面落在框架自己的归属之下;harness 页面仍是仓库自有的词汇。
  • 页内链接。 签名的 Types: 行链接到兄弟页面(core.md、bash.md);若某个类型的主要页面就是正在渲染的页面,该类型会从该行去掉,而不是链接到自身。页面用 #cordis-surface 或 #ctx<key>--<class> 锚点引用自己的区块:每个生成标题前都有一个显式 <a id>,携带 GitHub slug(即平铺目录时期的历史锚点),因此这些片段在 GitHub 与 VitePress 站点上解析一致——后者自带的 slugger 对含大量标点的标题会得出不同结果。

曾考虑的替代方案

  • 平铺目录与区块并存、两者都生成:否决。每次 JSDoc 编辑都会产生双份 diff 噪音,而本次变更本要消除的分散状况(一个子系统、三份文档)也将延续。
  • 整页归生成器所有、手写介绍放进片段文件:否决。叙述性行文占每个现有页面的大部分,应当留在被评审的文档本身;标记只花一条语法规则的成本,还让作者继续编辑真实文件。
  • 本地化区块(生成器同时输出中文):推迟,与 i18n README 中针对其余生成文档的长期备注同属一个状态:教会生成器输出中文意味着要翻译源码 JSDoc,而那是本次变更并不需要的机制。zh 页面里的英文区块,与「英文 JSDoc 出现在逐字节一致的围栏代码块内」这一既有现状相符。
  • 在 .i18n.yaml 中对剥离区块后的内容做 hash:否决。记录将不再是文件的 git hash-object,这会破坏「还原上次确认文本」的性质,也会破坏每个自行重算 hash 的消费方。

后果

  • 一个子系统的完整故事就在一个页面上:docs/subsystems/<name>.md(及其配对文件)承载介绍、数据结构/动词,以及生成的服务/事件表面;docs/cordis-catalog/ 不复存在。
  • 新的服务或事件作用域无法在未记录、未映射的状态下落地:在 SERVICE_PAGE/EVENT_SCOPE_PAGE 点名其所属页面之前,生成器一直失败,而且该页面必须已经存在,并在两个语言侧都带有标记。
  • 源码 JSDoc 变更后的重新生成会触及两种语言的受影响页面,外加(当写入限定在区块内时)它们的配对记录:一份机械、可评审的 diff。行文编辑仍然要走翻译流程,因为自动记录防护会拒绝它们。
  • 网站的子系统导航列出每个页面(每个 locale 38 条路由:35 个已翻译配对,加上仍为英文镜像的 goal/pty/commands 三页),取代两个平铺目录导航项;Cordis API 一节新增 inherited.md。
  • packages/typert/generator/tests/cordis-catalog-contract.spec.ts 固定区块渲染器(renderPageRegion)、同页链接去除规则,以及响亮失败的 JSDoc 与类型链接校验;scripts/translation-pairing.spec.ts 固定标记语法与 blob hash 原语;scripts/gen-cordis-catalog-record.spec.ts 证明自动重录守卫拒绝每一种非法状态(陈旧记录、格式错误或键被改名的伴随记录、多余条目、行文漂移、记录缺失、快照缺失)。