implemented(除 4 篇超长文档随后补)、proposed、rejected 全树配对; 同一流水线 + 二遍校验(paraphrase-back + 仓库上下文一致性)产出。 docs/rfc/implemented/AGENTS.md 与其 CLAUDE.md 符号链接列入排除 (agent 指令文件,与根 AGENTS.md 同策略)。
5.4 KiB
5.4 KiB
RFC:生成式插件配置目录
Status: implemented
English | 中文
问题
仓库此前没有以源码为后盾的插件配置参考。各 package 的 README 对字段的记录方式不一致,没有列举哪些包可被加载,也没有校验运行时 schema 是否与声明的配置类型一致。
决策
scripts/gen-config-catalog.ts 从每个插件声明的配置类型与 JSDoc 生成 docs/config-catalog.md,包含注入要求、引用类型链接和源码指针。package 内部类型被传递性地包含;workspace 和外部类型以链接或名称引用。确定性的 --write 和 --check 模式使提交到仓库的页面成为一个生成产物(artifact)。
此处采用纯 AST 生成是正确的,原因与事件/服务目录相同,也与工具目录不同:配置类型是静态声明,仓库中每个 schemastery schema 都是静态的 z.object/z.intersect 字面量,因此源码就是全部真相——配置表面没有任何部分是运行时组合的。
具体选择:
- 配置类型取自第二参数类型。 目录记录的是
apply(ctx, config)/ 服务构造函数(ctx, config)的声明参数类型——即 Cordis 实际传入的值——而非按命名约定定位的Config导出。这使得遍历是全量的:无论接口名为AcpConfig还是BasicCompactConfig,无论类型声明在兄弟文件中,还是插件根本没有校验 schema,都能正常工作。 - 分类是全量的。 每个
packages/<group>/<pkg>条目都会被解析(镜像 Loader 的unwrapExports(exports.default ?? exports)),归入以下之一:可配置插件、无配置插件、抽象 seam 类或库——各自渲染在独立章节中——无法归类的条目会硬错误。新 package 不可能被静默地遗漏。 - 逐字段 JSDoc 强制要求。 粘贴的声明中每个属性(包括嵌套的类型字面量)都需要非空的 JSDoc 描述,否则生成失败。粘贴本身就是文档,因此这与事件目录通过
@mode施加的强制函数相同:源码文档不足时门禁失败,而非产出一份单薄的目录。 - Schema 键与声明类型交叉检查。 生成器通过本地和 workspace 类型解析嵌套的对象与数组路径。确定缺失的路径会失败;无法枚举的外部或动态形状则跳过。检查有意设计为单向的,因为声明类型可能包含从 loader 配置中排除的运行时专用字段。
- 专用围栏。 粘贴的声明使用
```ts config-catalog信息字符串,doc-typecheck会跳过它(引用了导入类型的孤立声明无法独立编译),并将其排除在 opt-out 比例之外——与cordis-catalog和persistence-catalog围栏的处理方式相同。 - 单文件
docs/config-catalog.md,而非一个单文件目录:该页面服务于单一受众(cordis.yml的编写者),只有一个维度,不同于cordis-catalog/(它包含两个并列页面)。
各 package README 的 ## Config 章节保留。这种重叠是有意接受的:README 是精心策划的逐 package 契约(部署上下文中的配置语义,连同限制与扩展点),目录则是穷举式的生成枚举。因为目录是生成的,二者之间的分歧说明 README 有误,修复方式是编辑 README——目录不会漂移。
曾考虑的替代方案
- 合成式逐字段渲染:为每个字段生成一个项目符号列表、表格或带注释的 YAML 片段,由解析后的 JSDoc 加 schema 元数据组装。否决,改用逐字粘贴:带 JSDoc 的接口本身就是以其原始形式撰写的契约,合成渲染器会重新格式化它不拥有的行文,增加一层可能歪曲原意的渲染。
- 运行时启动 + schema 内省(如工具目录的做法):否决。此处没有任何内容是运行时组合的,而且 schema 本身对配置表面的文档化不足(行文记录的默认值、运行时专用字段、完全没有 schema 的插件)。启动只会增加脆弱性而不增加真相。
- 双向 schema/接口相等性检查:否决,改用子集检查。声明类型合理地包含 schema 拒绝从配置接受的成员(运行时专用的 seam)。
- 在同一变更中废弃 README 的
## Config章节:否决。接受的重叠使逐 package 契约在原地可读,而一次清扫需要先把每个 README 的额外事实折入字段 JSDoc——这是可分离的工作,目录不依赖它。
后果
- 目录不会漂移:源码变化而提交的文件未反映时,
verify-config-catalog在 pre-push 和 CI 中失败。未记录的配置字段、无法解析的引用类型名称、或 schema 键在配置类型中缺失,都会导致生成器直接报错。 - 配置行文现在在声明处有了强制函数:编写新的配置字段意味着编写其 JSDoc,而 JSDoc 会逐字成为目录条目。
- 生成器对无法静态遍历的形状硬错误——别名化的 package 内部配置导入、非
object/intersect组合构建的 schema、未列入的全局类型名。引入这样的形状就必须同时教会生成器(否则该形状不能进入仓库),这正是设计意图:目录始终是全部真相。 gen-cordis-catalog.ts导出其 JSDoc/指针辅助函数与LINK_MAP供复用,因此两个目录以相同方式交叉链接类型,新增一条 link-map 条目同时服务于两者。