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

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

2026-09-04 18:12:39作者:昌雅子Ethen

本文围绕 get-shit-done(GSD)项目 changeset 3577-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.cjstests/agent-skills.test.cjstests/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 默认值,与 CJS buildNewProjectConfig 检测 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 CJS ensureConfigFile shape: 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 with Failed to read config.json — matches the CJS”。

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

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

changeset 明确列出本次修复关闭的回归来自 tests/{config,agent-skills,ai-evals}.test.cjs,对应仓库中真实存在的 tests/config.test.cjstests/agent-skills.test.cjstests/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: 前缀等细节都按“遗留测试可匹配”标准对齐,源码中以注释显式声明了这一点。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384