get-shit-done 中 config-ensure-section 的双路径契约恢复:Phase 6 路由迁移下的 SDK 行为对齐实践
2026/9/5 17:18:39 网站建设 项目流程

get-shit-done 中 config-ensure-section 的双路径契约恢复:Phase 6 路由迁移下的 SDK 行为对齐实践

【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done

本文围绕 get-shit-done(GSD)项目 changeset3577-config-ensure-section-parity展开,讲解 Phase 6 SDK 路由迁移中config-ensure-section命令的回归事故是如何发生的、为什么采用“carve-out(保留 CJS 旧路径)”策略恢复旧契约,以及config-defaults.manifest.json作为默认值唯一事实来源、错误文案与 CJS 对齐这三项 parity 修复的具体实现。读完后,你将掌握该项目 CJS CLI 与 TS SDK 双实现共存时“行为契约”如何被维护,以及如何在源码层面验证这些契约。

背景:Phase 6 路由迁移引发的 config-ensure-section 回归

GSD 的工具链存在两套实现路径:一套是随 CLI 分发的 CJS 实现(get-shit-done/bin/lib/config.cjs),另一套是 TypeScript SDK 中的 Query Handler 实现(sdk/src/query/config-mutation.ts)。Phase 6 的“router migration”将部分config-*命令从 CJS 路径切到新的 SDK 处理器,但这一迁移打破了四个 CLI 回归测试,典型症状是测试中意外捕获到:

Usage: config-ensure-section <section>

问题根源在于两套实现对该命令的语义理解不一致

  • SDK 侧的configEnsureSection处理器把args[0]视为必选的位置参数<section>,缺失时直接抛出上述 Usage 错误(见 sdk/src/query/config-mutation.ts);
  • 但 CLI 实际调用config-ensure-section从不传递 section 参数——对 CLI 而言,这个命令的语义是“确保完整配置文件存在”,而不是“确保某个 section 存在”。

因此,把无参调用路由到一个期待位置参数的新处理器上,必然触发 Usage 错误,tests/config.test.cjs、tests/agent-skills.test.cjs、tests/ai-evals.test.cjs 等测试随之失败。changeset 3577-config-ensure-section-parity.md 记录的修复策略是:将config-ensure-section作为 parity carve-out,不再经由新 SDK 的configEnsureSection处理器路由,恢复旧契约

旧契约:CJS 路径 cmdConfigEnsureSection → ensureConfigFile → buildNewProjectConfig

被恢复的 CJS 调用链在 get-shit-done/bin/lib/config.cjs 中完整可见:

  1. ensureConfigFile(cwd)(第 301 行起):确保.planning目录存在;若.planning/config.json已存在则直接返回{ created: false, reason: 'already_exists' };不存在则调用buildNewProjectConfig({})生成完整的默认配置,并以 2 空格缩进的 JSON 写入,返回{ created: true, path: '.planning/config.json' }
  2. cmdConfigEnsureSection(cwd, raw)(第 333 行起):仅作为命令入口,调用上面的ensureConfigFile,并按created/exists两种状态输出结果。

从源码结构看,CJS 侧这个函数虽然命名为 “EnsureSection”,实际行为是“幂等地物化整个默认配置文件”,完全不涉及 section 参数——这正是 CLI 从未传参的原因,也解释了为什么新 SDK 处理器与 CLI 调用方式天然不兼容。

值得注意的是,SDK 仓库中configEnsureSection处理器依然保留(sdk/src/query/config-mutation.ts),其文档注释明确定义为“幂等地确保 config.json 中存在某个顶层 section:不存在则创建为空对象,存在则保留内容”,返回{ ensured: true, section }。它与 CJS 路径是两个不同粒度的操作(section 级 vs 文件级),carve-out 策略选择的是暂时保留文件级旧契约,而不是删除新处理器。命令在 SDK 命令目录中的注册信息(mutation: trueoutputMode: 'json')见 sdk/src/query/command-manifest.non-family.ts。

默认值唯一事实来源:configNewProject 对齐 config-defaults.manifest.json

changeset 的第二项修复是:SDK 的configNewProject默认值现在与 sdk/shared/config-defaults.manifest.json 保持一致,并像 CJS 一样报告项目根相对路径.planning/config.json

manifest 的规范结构

该 manifest 文件头部注释声明了自己是“Configuration Module 的规范 CONFIG_DEFAULTS”,并明确了若干关键约定:

  • 嵌套结构(git.*workflow.*planning.*等)是规范形态;CJS 的扁平投影(如顶层的branching_strategysub_repos)由消费者在边界处处理;
  • 安全类键(security_enforcementsecurity_asvs_levelsecurity_block_on)与post_planning_gaps的规范位置在workflow.*下;
  • brave_searchfirecrawlexa_search在 manifest 中默认为false,但运行时由buildNewProjectConfig检测 API key 决定实际值
  • plan_checker(CJS 扁平名)与workflow.plan_check(规范嵌套名)的命名分歧以规范名workflow.plan_check为准。

与 CJS 行为对齐的两个关键默认值在 manifest 第 4–5 行:commit_docs: trueparallelization: true

configNewProject 的合并逻辑

SDK 处理器侧的实现(sdk/src/query/config-mutation.ts)展示了 manifest 如何被消费:

  1. manifest 净化:遍历净化后的 manifest,按TOP_LEVEL_OMITTED_FROM_INITGIT_KEYS_OMITTED_FROM_INIT两个集合剔除不应出现在 init 配置中的键;
  2. 运行时探测brave_searchfirecrawlexa_searchhasBraveSearch/hasFirecrawl/hasExaSearch的运行时检测结果覆盖 manifest 默认值,与 CJSbuildNewProjectConfig检测 API key 的行为一致;此外还硬编码了features: {}顶层槽位——源码注释说明这是为了在 manifest 未更新前维持与 CJS 的 parity;
  3. 三层深度合并hardcoded defaults ← globalDefaults ← userChoices,对gitworkflowshiphooksagent_skillsfeatures各分组逐层展开合并(源码中标注为 D11 决策);
  4. 写盘与校验:对ship.pr_body_sections调用validateShipPrBodySections,通过atomicWriteConfig原子写入;
  5. 返回形态对齐:返回{ data: { created: true, path: '.planning/config.json' } }——源码注释明确写道 “Match CJSensureConfigFileshape: report the relative project-rooted path so output stays workspace-portable”,即与 CJS 侧ensureConfigFile返回的相对路径形态完全一致,保证工作区可移植性。

错误词汇对齐:让遗留测试继续可匹配

changeset 的第三项修复是错误文案(error vocabulary)与 CJS 对齐,目的是让匹配错误字符串的遗留回归测试继续通过:

  1. 未知配置键:SDK 侧抛出Unknown config key: <key>(键名不带引号),见 sdk/src/query/config-mutation.ts,其中<key>后可附带建议(${suggestion}拼接);
  2. config-get 的坏 JSON 错误:以Failed to read config.json:开头。对应实现见 sdk/src/query/config-query.ts,源码注释直接说明动机:“Lead the message withFailed to read config.json— matches the CJS”。

这两处看似琐碎的对齐,实质上是把错误字符串当作契约的一部分来管理:项目的回归测试按错误文案匹配来断言失败路径,SDK 迁移时任何措辞漂移都会造成测试回归。

修复的验证面:四个 CLI 回归测试

changeset 明确列出本次修复关闭的回归来自tests/{config,agent-skills,ai-evals}.test.cjs,对应仓库中真实存在的 tests/config.test.cjs、tests/agent-skills.test.cjs、tests/ai-evals.test.cjs。这些测试在 CJS 路径上运行,断言config-ensure-section的输出形态(created/exists状态、.planning/config.json相对路径)与默认配置内容不被 SDK 迁移改变。

小结:路由迁移中的 parity 方法论

从 3577-config-ensure-section-parity.md 这次修复中可以提炼出 GSD 在 CJS→SDK 渐进迁移中复用的几条实践:

  • 契约先行于实现:命令的可观测行为(参数要求、输出 JSON 形态、路径是绝对还是相对)才是契约;新处理器若改变了契约,即使“功能上等价”也算回归。
  • carve-out 优于强行适配:当新处理器语义与既有调用方不匹配时,保留旧 CJS 路径(cmdConfigEnsureSection → ensureConfigFile → buildNewProjectConfig)比修改所有调用方风险更低,新处理器可以留给未来语义收敛。
  • 单一事实来源 + 边界投影:默认值集中在sdk/shared/config-defaults.manifest.json,CJS 扁平形态由各实现自行投影;manifest 头部的_comment甚至把每一处已知的命名分歧都登记在案。
  • 错误字符串是测试契约Unknown config key: <key>(无引号)、Failed to read config.json:前缀等细节都按“遗留测试可匹配”标准对齐,源码中以注释显式声明了这一点。

【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询