get-shit-done #3605 修复清除 Agent 契约中的废弃斜杠命令引用及其回归守卫【免费下载链接】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-doneGSD仓库中的变更记录 .changeset/3605-stale-agent-command-refs.md 展开完整还原 Issue #3605 的修复内容五个 agent 契约文件中残留的 6 处废弃斜杠命令引用/gsd-research-phase、/gsd-insert-phase如何被替换为现行命令/gsd:plan-phase --research-phase N、/gsd:phase insert以及新增的回归守卫测试tests/bug-3605-stale-research-insert-phase-agent-refs.test.cjs如何保证这类命令退役后引用残留问题不再复发。读完后你将掌握 GSD 斜杠命令命名空间演进的来龙去脉、命令整合consolidation清理中的目录盲区成因以及如何为文本即部署产物的 Markdown 契约编写可运行的回归守卫。变更记录原文一条 Fixed 类型的 Changeset 碎片GSD 采用 per-PR 的 changeset 碎片管理 CHANGELOG每个有用户可见变更的 PR 向.changeset/目录投下一个带 frontmatter 的.md片段发布时由scripts/changeset/下的工具汇总进 CHANGELOG.md 并删除已消费的碎片机制见 .changeset/README.md。#3605 的碎片内容如下原文完整保留--- type: Fixed issue: 3605 --- **Agent contracts no longer reference retired /gsd-research-phase or /gsd-insert-phase** — six surviving references in agents/gsd-executor.md, agents/gsd-phase-researcher.md, agents/gsd-planner.md, agents/gsd-research-synthesizer.md, and agents/gsd-roadmapper.md are replaced with /gsd:plan-phase --research-phase N and /gsd:phase insert. Adds a regression guard (tests/bug-3605-stale-research-insert-phase-agent-refs.test.cjs) that fails when any retired command name reappears in agents/*.md — covers the gap that let #3029, #3044, and #3131 miss the agents/ directory.拆解这条记录它包含三个关键事实修复对象agents/目录下的 5 个 agent 契约文件executor、phase-researcher、planner、research-synthesizer、roadmapper合计 6 处废弃命令引用替换目标/gsd-research-phase→/gsd:plan-phase --research-phase N/gsd-insert-phase→/gsd:phase insert防回归手段新增一个测试守卫任何退役命令名再次出现在agents/*.md中都会让测试失败并且该守卫恰好堵上了此前三次清理#3029、#3044、#3131都漏掉agents/目录的盲区。背景为什么这些命令被退役GSD 的斜杠命令经历了一轮命名空间与功能整合命令从旧式/gsd-name连字符形式迁移到/gsd:name冒号命名空间形式同时部分独立命令被合并进更通用的命令。与 #3605 直接相关的两次整合是/gsd-research-phase被删除由/gsd-plan-phase --research-phase N取代。现行命令定义在 commands/gsd/plan-phase.md其中明确写道Research-only 模式--research-phase N会为指定阶段N派生gsd-phase-researcher、写出RESEARCH.md然后在 planner 运行前退出适用于跨阶段研究、规划前的文档评审、以及只迭代研究而不重新派生 planner的纠正循环该模式即替代了被删除的 research-phase 命令上游 Issue #3042。/gsd-insert-phase被并入/gsd-phasev1.40.0 将独立命令整合为子命令形式。现行 commands/gsd/phase.md 中的--insert标志负责把紧急工作以小数阶段如 72.1插入到已有阶段之间底层复用 insert-phase workflow。这两次整合都做了全局搜索替换式的清理但各自的清理范围scrub scope没有把agents/目录纳入退役名替换的覆盖列表于是 6 处引用在多次 PR 之后依然存活。修复落地被替换的 6 处引用长什么样修复后的agents/目录中相关引用已全部指向现行命令仓库现状可逐一验证agents/gsd-phase-researcher.md 第 17 行Spawned by/gsd:plan-phase(integrated) or/gsd:plan-phase --research-phase(standalone).—— 说明该 agent 由整合模式或独立的--research-phase模式派生agents/gsd-planner.md 第 186 行针对利基领域3D/游戏/音频/shader/ML先建议/gsd:plan-phase --research-phase Nagents/gsd-executor.md 第 195 行依赖包不存在时的兜底步骤是重新运行/gsd:plan-phase --research-phase N以查明正确包名agents/gsd-research-synthesizer.md 第 115 行研究综合时评估哪些阶段在规划期可能需要/gsd:plan-phase --research-phase N。这些文本不是普通文档注释——如守卫测试头部注释所强调的agents/*.md文本本身就是部署出去的契约the deployed contract。Claude Code、Codex 等运行时会在运行时加载这些文件并把内容呈现给真实用户因此其中写错的命令名会直接误导用户去敲一个不存在的命令。这正是 #3605 必须作为Fixed类型而非纯内部重构进 CHANGELOG 的原因。盲区成因命名空间转换器为什么救不了退役名要理解为什么三次清理都能漏掉agents/需要看仓库里唯一的批量命令改写工具 scripts/fix-slash-commands.cjs。该脚本是双向 GSD 斜杠命令命名空间归一器其SEARCH_DIRS列表明确包含agents/目录第 22–31 行const SEARCH_DIRS [ path.join(__dirname, .., get-shit-done, bin, lib), path.join(__dirname, .., get-shit-done, workflows), path.join(__dirname, .., get-shit-done, references), path.join(__dirname, .., get-shit-done, templates), path.join(__dirname, .., get-shit-done, contexts), path.join(__dirname, .., commands, gsd), path.join(__dirname, .., agents), path.join(__dirname, .., hooks), ];但它的转换逻辑是按存活命令名注册表驱动的readCmdNames()从commands/gsd/*.md读取当前存在的命令名transformContent()只做/gsd-cmd→/gsd:cmd的命名空间改写第 46–66 行且刻意不触碰非命令标识符如gsd-sdk、gsd-tools。这意味着对仍然存活的命令连字符旧写会被自动纠正为冒号形式对已退役的命令commands/gsd/下已无对应.md文件/gsd-research-phase、/gsd-insert-phase这类名字根本不在注册表里任何方向都不会被改写——它们既不是合法的旧写法也没有对应的替换目标脚本只能让它们原样存活。从源码结构看这不是 bug 而是职责边界该脚本的定位是命名空间形式的归一化而非命令生命周期管理。命令被删除后旧引用的清除必须依靠每次整合 PR 各自的全局搜索替换——而 #3029、#3044、#3131 三次整合的 per-name scrub 范围都没有包含agents/才留下了 #3605 这 6 处残留。回归守卫tests/bug-3605-stale-research-insert-phase-agent-refs.test.cjs 的实现剖析本次修复最有价值的部分是把一次性清理固化成了可运行的不变量。守卫测试 tests/bug-3605-stale-research-insert-phase-agent-refs.test.cjs 的完整机制如下1. 退役命令黑名单const RETIRED_COMMANDS [ /gsd-research-phase, /gsd-insert-phase, /gsd-add-phase, /gsd-remove-phase, /gsd-analyze-dependencies, ];黑名单不止包含 #3605 直接涉及的两个命令而是把agents/契约中任何已知退役的命令名都纳入使守卫具备前向防御能力。2. 逐文件、逐行的带词边界扫描scanForRetired()第 43–66 行对每个agents/*.md文件逐行查找黑名单命中并刻意做了词边界判定只有当命中位置之后紧跟的字符不属于[A-Za-z0-9-_]时才计为一次真实调用避免把更长命令名的前缀例如假想的/gsd-research-phase-x误报为退役命令引用。实现上用charCodeAt显式枚举数字、大小写字母、-与_四类名字字符const next lines[i].charCodeAt(idx cmd.length); // Only count if the match is a real invocation, not a prefix of a longer name. // The next char must be a non-name char (anything outside [A-Za-z0-9-_]). const isWordBoundary Number.isNaN(next) || !((next 48 next 57) || // 0-9 (next 65 next 90) || // A-Z (next 97 next 122) || // a-z next 45 || // - next 95); // _3. 对全部 agent 文件生成断言describe块第 68–86 行先列出agents/下所有.md文件附带一个 smoke 断言目录非空防止守卫在错误路径上空跑通过然后为每个文件生成一条独立断言命中列表必须为空数组失败信息会打印出具体行号、命中的命令名和该行原文定位成本极低test(${rel} contains no retired slash commands, () { const hits scanForRetired(file); assert.deepEqual( hits, [], ${rel} contains retired command references:\n hits.map((h) line ${h.line}: ${h.cmd} — ${h.text}).join(\n), ); });文件头部还保留了审计豁免注释allow-test-rule: source-text-is-the-product说明这条在源码文本上 grep的测试之所以成立是因为被 grep 的文本就是用户最终看到的产品表面。测试使用node:testnode:assert标准库实现无外部依赖直接扫描仓库根下的agents/目录。如何验证与复现在仓库根目录下运行该守卫测试即可验证当前状态node --test tests/bug-3605-stale-research-insert-phase-agent-refs.test.cjs预期结果smoke 测试通过 每个agents/*.md文件一条contains no retired slash commands断言全部通过。若有人向 agent 契约中重新引入/gsd-research-phase等退役名对应文件的断言会失败并输出行级定位。如果你想理解这类问题是如何漏进来的可以配合阅读三处源码整合清理时依赖的 scripts/fix-slash-commands.cjs只做存活命令的命名空间改写、现行命令定义 commands/gsd/plan-phase.md--research-phase模式即/gsd-research-phase的替代者与 commands/gsd/phase.md--insert子命令即/gsd-insert-phase的归宿。对Markdown 即契约类项目的工程启示#3605 虽然只是 6 处文本替换但它揭示了一类通用问题当项目的行为定义存在于 Markdown 文本agent 契约、命令文档、workflow 脚本而非编译型代码中时命令重命名/合并这类破坏性变更的回归测试不能只覆盖代码必须把文本表面也纳入不变量检查。get-shit-done 的解法有三个可迁移的要点给退役命令维护显式黑名单而不是靠新名字必须出现的正向断言——黑名单能同时拦截旧引用复活和新文件带入旧引用两种回归匹配必须带词边界否则前缀误报会让守卫失去可信度为守卫加 smoke 断言确认至少扫到了文件防止目录重命名等结构性变更让守卫静默空转。从本次修复的边界看它只保证agents/*.md这一表面干净这也是三次历史清理共同遗漏的表面仓库中其他表面如get-shit-done/workflows/、commands/的命令一致性由其他守卫如lint-no-source-grep系列脚本与bug-2950-stale-command-refs.test.cjs各自负责。理解这条分工就能准确把握 #3605 守卫在整体测试版图中的位置。【免费下载链接】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),仅供参考