【免费下载链接】gsd-core
Git. Ship. Done - Core
本篇文章基于 .changeset/archived/fix-3381-init-verify-work-ws.md(type: Fixed,PR #3386)展开,剖析 gsd-core 中
/gsd-verify-work --ws <name>的缺陷根因、修复实现与回归验证。读完本文,你将理解工作流(workstream)模式下阶段验证的目录作用域问题,掌握init.verify-work、MVP 模式查询与阶段目标查询如何接收到选中工作流,从而不再错误回退到根目录.planning/。
一、变更速览:一条 changeset 背后的问题
该 changeset 全文只有一句话,却指向一个真实且影响面明确的缺陷:
/gsd-verify-work --ws <name>now resolves workstream phases through the SDK—init.verify-work, MVP-mode lookup, and phase-goal lookup all receive the selected workstream instead of falling back to root.planning/.
拆解这句话可以得到三个事实:
- 修复对象是命令
/gsd-verify-work --ws <name>,即在指定工作流(workstream)中对某个阶段执行验证; - 修复方式是"通过 SDK 解析工作流阶段"——即由 gsd-core 的查询命令(
query init.verify-work等)承担阶段解析,而不是让工作流脚本自行猜测路径; - 修复涉及三个查询点:
init.verify-work(初始化阶段上下文)、MVP 模式查询(phase.mvp-mode)、阶段目标查询(roadmap.get-phase);修复前它们会回退到根目录.planning/,修复后都接收到用户选中的工作流。
这条修复之所以成立,需要先理解两个背景:/gsd-verify-work命令本身,以及 gsd-core 的工作流(workstream)目录作用域模型。
二、背景:/gsd-verify-work 与工作流作用域
2.1 verify-work 命令是做什么的
命令契约定义在 commands/gsd/verify-work.md 中,其 frontmatter 声明:
name: gsd:verify-work description: Validate built features through conversational UAT argument-hint: "[phase number, e.g., '4'] [--ws <name>]" requires: [execute-phase, phase]它的职责是"通过会话式 UAT 验证已构建的功能":一次一个测试、纯文本回答、不搞审问式提问;发现问题后自动诊断、规划修复并为执行做准备。输出为{phase_num}-UAT.md测试结果追踪文件,若发现问题则产出已诊断的差距与可直接交给/gsd:execute-phase的修复计划。命令本身可带两个参数:阶段号(如4)与--ws <name>工作流名。
2.2 工作流模式的目录作用域
gsd-core 在项目根.planning/之外支持多工作流布局。从 src/init.cts 的相关注释与实现可以看到:
- 每个工作流拥有自己的规划根:
.planning/workstreams/<ws>/,其中phases/、ROADMAP.md、STATE.md、REQUIREMENTS.md都是工作流作用域的(workstream-scoped); - 例外是
PROJECT.md,它是跨工作流共享的("PROJECT.md is shared across workstreams",见 src/init.cts 中cmdInitCompleteMilestone相关注释); planningDir(cwd)是"工作流感知"的:当存在激活的工作流时,它解析到该工作流的规划目录,而不是根.planning/。
这里的关键机制是GSD_WORKSTREAM环境变量。从 src/init.cts 的注释可以确认:显式传入--ws会设置GSD_WORKSTREAM,从而满足工作流模式的检查;而没有激活工作流又没有--ws时,planningDir(cwd)会解析到根.planning/——这正是"静默报告一个过期的根里程碑"的危险路径。
三、缺陷根因:--ws 未被 SDK 查询消费
修复前的缺陷链条如下:
- 用户在
/gsd-verify-work --ws <name>中显式指定了工作流; - 但工作流脚本(
gsd-core/workflows/verify-work.md)没有从$ARGUMENTS中提取--ws并转发给下游 SDK 查询; - 于是
GSD_WORKSTREAM从未被设置,planningDir(cwd)继续解析到根.planning/; - 结果是:
init.verify-work的阶段查找、phase.mvp-mode的 MVP 模式判定、roadmap.get-phase的阶段目标读取,全部落在错误的目录上——要么找不到工作流内的阶段(阶段在.planning/workstreams/<ws>/phases/下),要么读到根目录的同名阶段/目标,产生"张冠李戴"的验证上下文。
从 src/init-command-router.cts 的注释也能印证这条边界:--ws在发布的工作流中指向独立的query init.verify-work查询接口(seam),并在到达init verify-work命令族之前被剥离。也就是说,--ws的消费点本来就在查询层,工作流若不在调用查询时把它带上去,它就彻底丢失。
四、修复的工程实现:三层收口
修复将--ws从"用户参数"一路收口到"SDK 查询参数",共三层。
4.1 工作流层:从 $ARGUMENTS 提取 --ws 并派生阶段参数
gsd-core/workflows/verify-work.md 的initialize步骤现在包含以下解析逻辑:
GSD_WS="" echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[A-Za-z0-9._-]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[A-Za-z0-9._-]+') PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[A-Za-z0-9._-]+//g' | xargs) INIT=$(gsd_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})要点:
GSD_WS默认置空;只有当$ARGUMENTS中出现--ws <name>形态时才提取整对 token(--ws连同工作流名)作为GSD_WS;PHASE_ARG通过sed删除--ws <name>后经xargs去空白得到,保证阶段号参数纯净;- 随后
query init.verify-work "${PHASE_ARG}" ${GSD_WS}把工作流名原样转发给 SDK 查询(GSD_WS展开后即--ws <name>两个 token,未加引号以正确分词)。
值得注意的细节:--ws值的工作流 slug 字符类被刻意收窄为[A-Za-z0-9._-]+(而非宽泛的[^[:space:]]+),因为工作流 slug 由这些字符构成,收窄可以避免误吞后续参数,也保证了GSD_WS能可靠到达每一个工作流敏感的查询。
4.2 SDK 查询层:init.verify-work 接收工作流并解析阶段
query init.verify-work由 src/init.cts 的cmdInitVerifyWork实现。收到带工作流作用域的cwd后,它通过共享原语解析阶段:
const config = loadConfig(cwd); let phaseInfo = guardedFindPhase(cwd, phase, config.project_code); const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code); phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => { /* 合成回退对象 */ });guardedFindPhase(src/init.cts)封装findPhaseInternal,并附加#2056外来前缀防护:当查询携带了项目代码前缀而阶段不匹配时返回null;guardedGetRoadmapPhase是对路线图阶段读取的同等防护封装;applyRoadmapFallback(src/init.cts)是归档/未命中回退:若磁盘阶段已归档而路线图仍有记录,则置空;若磁盘未命中而路线图命中,则用路线图合成阶段信息(phase_number、phase_name、phase_slug、空plans/summaries等)。该共享回退同样被execute-phase、plan-phase、code-review、review等命令复用,保证行为一致。
在拿到阶段信息后,cmdInitVerifyWork继续组装验证上下文:
phase_dir、phase_number、phase_name、has_verification;state_path/roadmap_path:经由工作流感知的planningDir(cwd)解析(STATE.md、ROADMAP.md),供verify-work.md的plan_gap_closure步骤读取,而不是硬编码根.planning/字面量(对应#2376的修复);phase_completion:内含buildPhaseCompletionProjection的结果(implementation_complete、verification_status、verification_passed、phase_complete、verification_next_action、verification_next_command等),以及 UAT 状态(uat_passed、uat_blockers、ready_to_transition);ui_phase_active与section_manifest:供 UI 验证与"mvp-uat-framing"小节门控使用。
正因为GSD_WS(即--ws <name>)被传到了这条查询链路,GSD_WORKSTREAM得以生效,planningDir(cwd)解析到.planning/workstreams/<ws>/,findPhaseInternal才能在正确的作用域下找到阶段目录——修复前这一步恰恰回退到了根.planning/。
4.3 作用域查询:MVP 模式与阶段目标
除了初始化查询,工作流还把工作流转发给另外两个查询:
# MVP 模式检测:集中式 phase.mvp-mode 解析器 MVP_MODE=$(gsd_run query phase.mvp-mode "${phase_number}" ${GSD_WS} --pick active)以及回归测试断言中的阶段目标查询:
gsd_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goalphase.mvp-mode判定"当前阶段是否为 MVP 模式"。修复前它读取根.planning/下路线图中的**Mode:** mvp标记,工作流内阶段会得到错误答案;修复后工作流被转发,模式判定按工作流作用域进行。工作流注释还说明:verify-work没有--mvp命令行开关,模式从已规划阶段继承,因此查询省略--cli-flag,走 roadmap → config → false 的回落链。roadmap.get-phase提取阶段目标(goal)。其解析依据位于 src/roadmap-parser.cts,通过**Goal:**区块正则提取目标文本。该查询在verify-work的verify_phase_goal步骤中被消费——验证器(gsd-verifier)需要拿到正确的阶段目标与需求 ID 才能核对实现是否达标。
三个查询全部拿到GSD_WS,正是 changeset 中"init.verify-work、MVP-mode lookup、phase-goal lookup all receive the selected workstream"的落地形态。
五、验证结果如何被路由:verification 状态机
阶段解析正确之后,验证结果本身由 src/verification.cts 统一裁决。该模块是验证状态路由的唯一事实源,定义在VERIFICATION_ROUTING_TABLE(src/verification.cts):
| status | 语义 | 推荐下一步 |
|---|---|---|
passed | 验证通过 | 继续 |
gaps_found | 发现差距 | 运行plan-phase <N> --gaps规划修复,重新执行后再发布 |
human_needed | 需要人工验证 | 完成*-UAT.md中的手工测试后重跑verify-work |
stale | 覆盖的源文件在验证后发生了变化 | 重跑execute-phase(在验证门处恢复并重新运行验证器,刷新 VERIFICATION.md 与摘要) |
missing | 无验证报告 | 运行execute-phase是安全的(不会重跑已有 SUMMARY.md 的计划) |
unparseable | 报告 frontmatter 不是合法 YAML | 直接修复报告中的语法错误(重跑 execute-phase 无法修复) |
unknown | 非标准的自定义状态 | 若是手工标记则无需处理,否则运行execute-phase重新生成验证 |
其中passed/gaps_found/human_needed是验证器(gsd-verifieragent)真正会写出的值(VERIFIER_STATUSES);stale/missing/unparseable/unknown是内部构造的哨兵状态。next_command统一经过formatGsdSlash投影为当前运行时的命令形态(如/gsd-execute-phase、$gsd-execute-phase),避免硬编码废弃的/gsd:冒号形式。
readVerificationStatus只读取*-VERIFICATION.mdfrontmatter中的status(解析器锚定在文件字节 0,正文里的status:不会误读),并支持#4155引入的覆盖输入指纹(covered_files+covered_digest)作为比 mtime 更强的过期判定依据。这些路由结果最终通过cmdInitVerifyWork的phase_completion暴露给工作流,驱动 UAT 会话与差距闭环。
六、回归测试:--ws 转发链路被锁死
修复不是只改脚本了事,还配了回归测试。当前测试位于 tests/verify-work-auto-transition.test.cjs,标题即bug #3381: verify-work forwards workstream context,测试注释说明它由tests/bug-3381-verify-work-workstream.test.cjs折叠而来(合并史诗 #1969)。
该测试用正则逐条断言工作流源码中必须存在的转发契约:
assert.match(workflow, /GSD_WS=""/, 'verify-work must initialize GSD_WS'); assert.match(workflow, /grep -qE -- '--ws[[:space:]]+[A-Za-z0-9._-]+'/, 'verify-work must detect --ws in $ARGUMENTS'); assert.match(workflow, /grep -oE -- '--ws[[:space:]]+[A-Za-z0-9._-]+'/, 'verify-work must extract the --ws flag pair from $ARGUMENTS'); assert.match(workflow, /PHASE_ARG=\$\(echo "\$ARGUMENTS" \| sed -E 's\/--ws[[:space:]]+[A-Za-z0-9._-]+\/\/g' \| xargs\)/, 'verify-work must derive PHASE_ARG after removing --ws'); assert.match(workflow, /gsd_run query init\.verify-work "\$\{PHASE_ARG\}" \$\{GSD_WS\}/, 'init.verify-work must receive GSD_WS so phase_dir resolves in workstreams'); assert.match(workflow, /gsd_run query phase\.mvp-mode "\$\{phase_number\}" \$\{GSD_WS\} --pick active/, 'phase.mvp-mode must receive GSD_WS so roadmap mode is workstream-scoped'); assert.match(workflow, /gsd_run query roadmap\.get-phase "\$\{phase_number\}" \$\{GSD_WS\} --pick goal/, 'roadmap.get-phase must receive GSD_WS so goals are workstream-scoped');这份断言同时锁死了三类东西:
- 解析逻辑:
GSD_WS初始化、--ws检测与提取、PHASE_ARG的派生(sed删除--ws <name>对); - 转发对象:三个工作流敏感查询必须无一遗漏地收到
GSD_WS; - 语义承诺:注释明确写明了每条断言的动机——
init.verify-work收到GSD_WS才能让phase_dir在工作流内正确解析;phase.mvp-mode收到它路线图模式才是工作流作用域;roadmap.get-phase收到它目标才是工作流作用域。
任何未来的重构若删掉其中一条转发,测试就会立刻失败,从而防止缺陷 #3381 以"回归"形式复活。
七、使用方式与适用前提
修复后的命令形态与使用方式如下:
# 在根项目上验证阶段 4 /gsd-verify-work 4 # 在指定工作流 my-ws 中验证阶段 4(本次修复的核心场景) /gsd-verify-work 4 --ws my-ws工作流 slug 由[A-Za-z0-9._-]字符构成,--ws与工作流名之间需以空白分隔。适用前提与限制:
- 该命令依赖 gsd-core 的 SDK 查询能力(
gsd_run query ...),需要已完成 gsd-core 安装并在$ARGUMENTS中携带阶段号或存在活跃会话; - 工作流模式要求显式工作流:无活跃工作流且未传
--ws时,planningDir(cwd)解析到根.planning/是旧行为,请务必显式指定; - 本修复保证的是"工作流内阶段解析正确",验证的裁决仍遵循 src/verification.cts 的状态机(
passed/gaps_found/human_needed等),UAT 输出仍是{phase_num}-UAT.md; - 差距闭环路径不变:发现问题后
plan_gap_closure步骤使用{state_path}、{roadmap_path}(二者已由工作流感知的planningDir(cwd)解析)拉起gsd-planner --gaps生成带gap_closure: true与gap_ids的计划,供后续verify-work恢复时对账。
结语
fix-3381-init-verify-work-ws.md是一条"小而完整"的修复:它把一个命令参数(--ws)通过工作流脚本的提取、SDK 查询的转发、GSD_WORKSTREAM的作用域生效,最终传导到三个查询点(init.verify-work、phase.mvp-mode、roadmap.get-phase),彻底消除了"用户明明指定了工作流、SDK 却读根.planning/"的静默错误。若你的项目使用 gsd-core 的多工作流模式并在工作流内执行阶段验证,请确认版本包含此修复(PR #3386),并在调用时始终显式传递--ws <name>。
【免费下载链接】gsd-core
Git. Ship. Done - Core
相关推荐
gsd-core 修复 3135:`/gsd-capture --backlog` 路由与 add-backlog 工作流的完整实现
gsd core 修复 3135: /gsd capture backlog 路由与 add backlog 工作流的完整实现 导读 /gsd capture
gsd-core 修复实战:`init.milestone-op` 与 `roadmap.analyze` 的 Workstream 作用域解析修复(PR 3196)
gsd core 修复实战: init.milestone op 与 roadmap.analyze 的 Workstream 作用域解析修复(PR 3196)
gsd-core 嵌套 Git 仓库检测修复解析:`/gsd-new-project` 与 `/gsd-ingest-docs` 如何通过 `git rev-parse` 语义避免误建 `.git`
gsd core 嵌套 Git 仓库检测修复解析: /gsd new project 与 /gsd ingest docs 如何通过 git rev parse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考