GSD 1.39.0-rc.7 发布解析:版本列车同步、二十余项缺陷修复与源码级验证
【免费下载链接】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
本篇文章围绕 RELEASE-v1.39.0-rc.7.md 展开,解读 get-shit-done(GSD)1.39.0 发布列车的首个实质性 RC(release candidate)版本:它为何要让 release 分支与main重新同步、新增的手动 canary 发布流水线如何工作,以及 1.39.0 全家桶(rc.4 / rc.5 / rc.7)在 ROADMAP 解析、UAT 审计、SDK 命令行解析、Codex/OpenCode 集成等方向修复的具体缺陷。读完本文,你将掌握这些修复的底层原理与对应源码位置,并能在 sdk/src/query/roadmap.ts、.github/workflows/canary.yml 等文件中逐一复现、验证每个结论。
一、rc.7 的定位:一次"让代码真正到达仓库"的版本列车同步
GSD 以get-shit-done-cc为 npm 包名发布,预发布版本挂载在nextdist-tag 下。rc.7 是 1.39.0 发布列车中第一个滚入 rc.5 之后main分支全部修复的 RC。
一个关键的发布流程背景:rc.6 与 rc.5 内容完全一致,原因是release/1.39.0分支在没有先与main合并的情况下就完成了版本号 bump,导致 rc.5 之后提交到main的大量修复并未真正进入发布产物;rc.7 通过把 release 分支与main重新同步,才让下面的所有工作真正到达 npm registry(文档以 issue #2856 记录了该流程缺陷)。
对于发布工程本身,这正是 RC 列车常见的分支管理教训:版本号 bump 必须建立在"内容已合并"的前提之上,否则产物与代码库会出现内容漂移(content drift)。
二、新增能力:按需触发的手动 Canary 发布流水线
rc.7 在"Added"中引入的是一套新的发布流:
- 新增
.github/workflows/canary.yml,发布{base}-canary.{N}版本的get-shit-done-cc; - 通过
workflow_dispatch仅支持手动触发,并在canarydist-tag 下发布; - 提供可选的
dry_run布尔输入,用于只跑完整验证而不实际发布。
对照工作流源码,可以看到它定义的三条发布流策略(文件头部注释):
dev → @canary (本工作流,供长期存在的集成分支 preview 使用) main → @next (RC 列车,见 release.yml) main → @latest (稳定版,见 release.yml)三条流互不混用:publish/tag 步骤都以refs/heads/dev作为门禁。也就是说,在任意其他分支(包括main)手动触发该工作流,只会完成构建、测试与 dry-run 校验,不会真正发布或打 tag。
流水线的核心步骤包括:
- 从
package.json读取版本并去掉预发布后缀(如1.39.0-rc.4 → 1.39.0)得到 base 版本; - 轮询已有 tag(
git tag -l "v${BASE}-canary.${N}")计算出下一个顺序 canary 序号; - 对根包与
sdk/子包同时执行npm versionbump; - 执行
npm ci与npm test,再构建 SDK dist 并运行scripts/verify-tarball-sdk-dist.sh校验 tarball 确实携带sdk/dist/cli.js(对应 bug #2647); npm publish --dry-run做发布前校验;- 仅在
refs/heads/dev且非 dry-run 时打 tag、推送并用--provenance --access public --tag canary发布; - 发布后以递增延时轮询
npm view,确认主包与@gsd-build/sdk均已上线,失败则报错退出。
安装 canary 版本的方式为npx get-shit-done-cc@canary。这套设计让集成分支(dev)可以随时产出与主版本序列正交的预览构建,而不污染next/latest发布节奏。
三、ROADMAP 里程碑解析修复:代码块内的"标题行"不再截断切片
核心缺陷(issue #2787):extractCurrentMilestone在 ROADMAP.md 中定位"当前里程碑结束位置"时,采用"扫描下一个同级或更高级别标题"的策略。如果某个里程碑切片内部(fenced code block)含有以#开头的行——例如代码示例里的# Ops runbook (v1.0 compat)——旧实现会把它误判为里程碑边界,导致当前里程碑的内容被提前截断,后续阶段全部丢失。
源码级修复:在 sdk/src/query/roadmap.ts 中,extractCurrentMilestone(定义于 L182)现在逐行扫描并跟踪 GFM 围栏状态:遇到```或~~~围栏即进入代码块,遇到与开始符相同的闭合符(且不带 info string)才退出;当某个正则命中的"标题行"位于围栏内部时,直接continue忽略(见 L312-L320 注释)。配套的isInsideFencedCodeBlock(content, offset)(L393)实现了完整的 GFM 围栏语义:
- 开 fence:以至少 3 个反引号或 3 个波浪号开头的行,可带 info string(如
```bash); - 闭 fence:必须以与开 fence相同字符开头且不带 info string,因此
```js不会关闭一个已开启的```text围栏。
该函数从内容起点走到目标 offset,用fenceChar游标记录当前围栏字符,游标非空即表示命中点位于代码块内。需要注意,这里讲的extractCurrentMilestone还有另外两套互补的锚定策略值得一并理解:
- Markdown 标题锚定:优先用包含活动版本号(
^#{1,3}\s+.*vX.Y…)的标题定位当前里程碑起点;阶段标题(### Phase N: …,见 #2619 的(?!Phase\s+\S)前瞻)与"同版本子标题"(如## v2.0 Phase Details,见 #2422/#2455)都不会被误判为新边界。 <details><summary>折叠块兜底(#2641):很多项目用 GitHub 折叠语法包裹活动里程碑的阶段细节,<summary>是 HTML 而非标题,旧的正则匹配不到。兜底逻辑会捕获<summary>文本并合成一个##标题拼在返回切片前,同时通过"非空 body"与"无嵌套<details>"两个守卫避免返回被截断或空壳内容。
版本来源方面,函数优先从planningPaths(projectDir, workstream).state(STATE.md)的milestone:frontmatter 读取并剥离 YAML 引号,失败时回退到 ROADMAP 中的🚧/🟡 **vX.Y…进行中标记。这块 TS 实现源自core.cjs第 1102-1170 行,且已先后经历 Backlog 泄漏(#2422)、phase-vX.Y截断(#2619)、围栏跟踪(#2787)与折叠块兜底(#2641)等多轮加固,是整个 GSD 规划文件解析链路中相当"高频返修"的模块。
四、UAT 审计解析修复:human_verification支持从 frontmatter 数组读取
核心缺陷(issue #2788):audit-uat的解析器原先只用正则匹配文档正文中的## human_verification或## Human Verification小节,格式过于严格。当合法的 UAT 条目被gsd-verifier写在 YAML frontmatter 中(而不是正文)时,解析结果为空数组,于是在每次里程碑完成审计时都会产生误报的开放缺口(false-positive open gaps)。
源码级修复:在 sdk/src/query/uat.ts 中新增了parseVerificationFrontmatterItems(L198):当文档状态为human_needed时,先检查 frontmatter 中的human_verification字段(L235-L240),只有该数组为空时才回退到正文小节的兼容解析(##\s*human[_\s-]verification...,兼容下划线/空格、大小写与可选括号)。frontmatter 解析逻辑能处理两种条目形态:
- 纯字符串条目:
human_verification: ["手动验证 A", "手动验证 B"],每条生成一个result: 'human_needed'、category: 'human_uat'的审计项; - 对象条目:支持
test(或name)作为名称键,并可选携带expected与why_human字段,进一步解释"人为何要介入"。
该修复与同版本另一项 UAT 改动(见下文 rc.7 的audit-open修复)共同补齐了审计工具链的两类漏报。仓库中以 bug-2788-audit-uat-frontmatter.test.cjs 对该回归场景做了专项覆盖。
五、Skill 描述卫生:三类反模式清除 + 100 字符预算
背景(issue #2789):commands/gsd/*.md的 frontmatterdescription:中残留三类反模式:
- 已在
argument-hint:中记录的 flag 说明被重复写进 description; - 用
Triggers:拼凑关键词列表来"蹭"触发匹配; - 在 description 中使用编号列举。
rc.7 在清理这些反模式的同时,把description 长度上限 100 字符固化为新的 CI lint 门禁:npm run lint:descriptions,任何超长描述都会令其失败。
源码级验证:scripts/lint-descriptions.cjs 实现了该门禁的完整逻辑——从 frontmatter 中解析description:(同时支持带引号与纯文本两种写法),超过 100 字符即在 stderr 输出违规文件、实际长度与描述预览并exit 1;不传参数时默认扫描整个commands/gsd/目录。实测仓库内的命令文件已遵循这一约定,例如 audit-fix.md 的 frontmatter 中:
--- type: prompt name: gsd:audit-fix description: Autonomous audit-to-fix pipeline — find issues, classify, fix, test, commit argument-hint: "--source <audit-uat> [--severity <medium|high|all>] [--max N] [--dry-run]" ---description 只做一句功能概括,而--source、--severity、--max、--dry-run等参数语义全部下沉到argument-hint:——这正是该 lint 想引导的"职责分离"写法。
六、gsd-sdk 命令行与配置体系的一批回归修复
rc.7 修复了多条 SDK(TypeScript 查询/配置层)侧的回归缺陷,均与工具(gsd-tools)与 SDK(@gsd-build/sdk)解耦迁移有关:
1.roadmap update-plan-progress重新接受--phase旗标(issue #2796)
SDK v0.1.0 的参数解析回归会静默丢弃--phase/--name/--plans旗标,进而导致 STATE.md 中的计划进度被错误改写(corruption)。修复后该命令恢复了对--phase旗标形式的支持。回归测试见 bug-2796-arg-parsing-regression.test.cjs。
2.context_window进入VALID_CONFIG_KEYS白名单(issue #2798)
/gsd-settings-advanced之前无法设置context_window,原因在于config-set的校验白名单里根本没有这个键。修复方式是把它补进VALID_CONFIG_KEYS。从源码看,配置 schema 的数据源是单一声明的 manifest:config-schema.ts只是从 sdk/src/configuration/index.ts 做 thin re-export,而后者读取 sdk/shared/config-schema.manifest.json(其中确实收录了"context_window"),以此保证 SDK 与 CJS 两侧使用同一份白名单(对应 #3536 的单一事实来源改造)。相关测试见 bug-2798-context-window-config-key.test.cjs。
3.config-get尊重--default <value>(issue #2803)
针对"键不存在时返回兜底值"的 CJS 既有行为,补齐了 SDK 侧的缺口,使/gsd-config-get在缺键场景下能按--default返回值而不是空结果。见 bug-2803-config-get-default-flag.test.cjs。
4.find-phase对已归档阶段返回null(issue #2805)
当当前里程碑的阶段目录尚不存在时,init.plan-phase/init.execute-phase旧实现会错误返回上一个已归档里程碑的目录,导致在错误阶段上执行工作。修复后返回null,让上层安全回退。见 bug-2805-archived-phase-fallback.test.cjs。
5.gsd-tools init派发ingest-docs处理器(issue #2801)
v1.38.5 中/gsd-ingest-docs损坏的根因是:工作流调用的是新工具(gsd-tools init),但 init 处理器注册表里没有ingest-docs条目。补齐注册后命令恢复可用。见 bug-2801-ingest-docs-handler.test.cjs。
6.gsd-sdk与@gsd-build/sdk二进制冲突解决(issue #2791)
之前gsd-sdk二进制与@gsd-build/sdk的二进制存在同名冲突。修复方案是让 workstream 感知的查询注册中心(query registry)尊重GSD_WORKSTREAM环境变量,并新增gsd-toolsbin 别名。测试见 bug-2791-sdk-workstream-env.test.cjs。
7. 本地安装模式下gsd-sdk可解析(issue #2829)
旧实现中isLocal短路提前返回,导致 PATH 探测与自链接逻辑从未执行。修复后,只要sdk/dist/cli.js存在,本地安装就会走与全局安装相同的 probe-and-link 流程。见 bug-2829-local-install-sdk-path.test.cjs。
七、Skill 命名统一:frontmattername:迁移到连字符形式
背景(issue #2808):仍在使用已废弃冒号形式(如gsd:cmd)的 SKILL.md frontmattername:,会让 IDE/Agent 的自动补全把命令补成/gsd:command而不是/gsd-command。
rc.7 将全部 skill 的name:迁移为连字符形式,并同步修订 CR-INTEGRATION 测试(issue #2835):测试改为结构化解析Skill(skill="...")调用并拒绝旧冒号形式,而不是靠文本正则,从而在语法层杜绝该类回归。对应测试见 bug-2808-skill-hyphen-name.test.cjs。仓库中所有命令文件当前的命名形式也印证了该约定,例如上面展示的name: gsd:audit-fix属于历史文档遗留写法,规范目标是gsd:audit-fix全部落为name: gsd:audit-fix对应的连字符命令行/gsd-audit-fix形式。
八、OpenCode 与 Codex 集成修复
1. OpenCode agents 嵌入model_profile_overrides.opencode.<tier>(issue #2794)
通过/gsd-settings-advanced设置的逐层(per-tier)模型覆盖,原先不会传导到生成的 OpenCode agent 文件中。修复后,生成 agent 时会把model_profile_overrides.opencode.<tier>正确写入。覆盖测试见 bug-2794-opencode-model-profile-overrides.test.cjs。
2. OpenCode@file引用在所有平台统一使用绝对路径(issue #2831)
OpenCode 在任何平台上都不会对@file引用做$HOMEshell 展开。此前只有 Windows 应用了该守卫(#2376 的修复),导致 macOS/Linux 上生成的是字面量@$HOME/...字符串。rc.7 将该守卫改为对所有平台无条件生效。见 bug-2831-opencode-home-path-prefix.test.cjs。
3.gsd-sdk auto正确识别 Codex 运行时(issue #2832)
auto模式此前会忽略runtime: codex配置,仍然路由到@anthropic-ai/claude-agent-sdk,导致 autonomous 运行出现典型症状:[FAILED] $0.00 0.1s。修复包含两层:
- 新增
runtime-gate:对非 Claude 运行时给出明确的错误信息,而不是默默走错 SDK; resolveModel()尊重GSD_RUNTIME环境变量的优先级,且在非 Claude 运行时绝不注入 Claude profile id。
九、SUMMARY 抢救与 review-fix 清理的事务化
1. SUMMARY rescue 处理被 gitignore 的.planning/(issue #2838)
quick.md/execute-phase.md的 SUMMARY 抢救逻辑原先使用git ls-files --exclude-standard枚举文件。当.planning/目录被.gitignore排除时,该命令会静默空转,抢救块什么也找不到——随后 worktree 被删除,SUMMARY 也随之丢失。修复改为文件系统层的find+ 幂等cp,不再依赖 git 索引视图。对应回归测试为 bug-2838-summary-rescue-gitignored-planning.test.cjs。这一改动也体现了 GSD 的一个设计原则:.planning/等规划产物可能在 git 视角下"不存在",但文件系统层面必须可靠抢救。
2./gsd-code-review-fix清理尾部事务化,支持崩溃自愈(issue #2839)
代码评审修复(worktree 工作流)在崩溃后可能遗留孤儿 worktree。修复把回收流程改造为事务化:
- JSON 恢复哨兵文件
.review-fix-recovery-pending.json写在${phase_dir}/下; - 哨兵在
git worktree add成功之后才写入,且只在git worktree remove成功返回后才删除; - 新一次运行若发现已存在的哨兵,会强制移除孤儿 worktree,使 agent 具备跨崩溃的自愈能力。
测试见 bug-2839-review-fix-transactional-cleanup.test.cjs。
3.audit-openquick-task 扫描器兼容${quick_id}-SUMMARY.md(issue #2836)
旧实现只认裸文件名SUMMARY.md,导致每个有文档的 quick task 都被误报为status: missing。修复后扫描器接受${quick_id}-SUMMARY.md命名;同时 UAT 的 terminal-status 枚举新增resolved(与execute-phase.md中 gap 关闭后的终态语义对齐)。见 bug-2836-audit-open-summary-uat-drift.test.cjs。
十、回顾 rc.6 与 rc.5:Codex hooks 迁移器的正确性加固
rc.6:一次"空转"重发布
rc.6 仅是 rc.5 的无新增内容重发布,唯一提交为388118d8 chore: bump to 1.39.0-rc.6:
$ git log v1.39.0-rc.5..v1.39.0-rc.6 --pretty='%h %s' 388118d8 chore: bump to 1.39.0-rc.6完整背景见 RELEASE-v1.39.0-rc.6.md。这正是本文第一节所述分支同步问题在版本历史中的直接体现。
rc.5:[[hooks.<Event>]]→[[hooks.<Event>.hooks]]嵌套 schema 迁移的五连修(issue #2809)
Codex hooks 迁移器在把旧的两级嵌套 TOML schema 迁往新结构时,经五轮 code review 发现并修复了五个边界情况:
| Finding(发现的问题) | Fix(修复方式) |
|---|---|
parseHooksBody使用裸正则/^([\w.]+)\s*=/,会静默丢弃status-message这类连字符键以及带引号的 TOML 键 | 替换为parseTomlKey()——项目已有的完整 TOML 键解析器 |
buildNestedBlock无条件输出[[hooks.TYPE.hooks]],即使没有任何 handler 字段,产生有type = "command"却无command的空条目 | 增加守卫:只有 matcher / 无 handler 字段的小节只输出事件条目块 |
legacyMapSections过滤器用section.path.startsWith('hooks.')但没检查段数,导致[hooks.SessionStart.hooks]这类三段表被误判为事件条目并重发出成伪造的嵌套事件 | 改用section.segments.length === 2(与此前staleNamespacedAotSections的修复一致) |
缺少针对含点号的带引号事件名的回归测试——[[hooks."before.tool"]]有 2 段路径但 3 个点号,split('.')检查会误判 | 补充回归测试;带引号的含点名被正确当作单一两段命名空间 |
安装测试中的 handler 命令路径断言用的是正则(/gsd-check-update\.js/)而非精确绝对路径 | 强化为assert.strictEqual+path.join(codexHome, 'hooks', 'gsd-check-update.js') |
这组修复展示了"迁移器"类代码的高风险本质:schema 形态改写涉及路径段计数、键名解析与断言强度三处细节,任何一处放宽都会在用户机器上产生损坏的 TOML。
十一、回顾 rc.4:最小安装模式与 Codex 配置写入安全
--minimal(别名--core-only)安装旗标(issue #2762)
只写入维持主工作流循环所需的六个核心 skill:new-project、discuss-phase、plan-phase、execute-phase、help、update;不安装任何gsd-*子代理(subagents)。两种模式在冷启动 system-prompt 上的开销差异显著:
| 模式 | 冷启动 system-prompt 开销 |
|---|---|
| full(默认) | ~12k tokens |
| minimal | ~700 tokens |
安装 manifest 会记录mode: "minimal" | "full"。若日后需要扩充,随时不带--minimal运行一次gsd update,即可扩回完整 skill 集。这对于上下文预算敏感的使用场景(例如在较小上下文窗口的模型上使用 GSD)是一个实用入口。
Codex 安装不再损坏~/.codex/config.toml(issue #2760)
旧安装器可能破坏既有config.toml。修复后的安装流程包含五个动作:
- 剥离遗留
[agents]块; - 以用户现有的形状输出 hooks;
- 把遗留
[hooks.<Event>]map 格式迁移到[[hooks.<Event>]]; - 通过临时文件 +
renameSync原子写入; - 用严格 TOML 解析器对写入后的字节做校验。
十二、如何安装本预发布版本
在 npm 上以nexttag 安装,两种方式任选:
# npm 全局安装 npm install -g get-shit-done-cc@next # npx 一次性运行 npx get-shit-done-cc@next若需锁定到本精确 RC:
npm install -g get-shit-done-cc@1.39.0-rc.7十三、发布展望:从 RC 到 latest
按 RELEASE-v1.39.0-rc.7.md 的说明,rc.7 soak(观察期)结束后,将运行发布工作流的finalize步骤,把1.39.0提升到latestdist-tag。对使用者而言,这意味着上述在 ROADMAP 解析、UAT 审计、SDK 参数解析、Skill 命名与 OpenCode/Codex 集成层面的全部修复将随稳定版一同落地。
小结:一份 RC 文档可以承载多少工程信息
从这份 rc.7 文档可以看出,GSD 的发布说明并不只是"改了哪些功能"的流水账,而是带 issue 追踪、带源码细节、带回归测试指引的变更单。它既记录了版本列车同步这类流程性教训,也沉淀了围栏感知的 Markdown 切片、frontmatter UAT 解析、TOML 迁移五连修等可复用的实现经验。若你想在代码中复现某个修复,建议按如下路径对照阅读:
- ROADMAP 里程碑切片与围栏跟踪 → sdk/src/query/roadmap.ts
- UAT frontmatter 审计项解析 → sdk/src/query/uat.ts
- description 100 字符 lint 门禁 → scripts/lint-descriptions.cjs
- 配置键白名单单一事实来源 → sdk/shared/config-schema.manifest.json
- Canary 发布流水线 → .github/workflows/canary.yml
- 各类回归测试 → tests/ 下
bug-2788-audit-uat-frontmatter.test.cjs、bug-2796-arg-parsing-regression.test.cjs、bug-2831-opencode-home-path-prefix.test.cjs等对应文件
【免费下载链接】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),仅供参考