深入解析 `gsd-sdk query validate.health`:get-shit-done 规划目录完整性体检如何消除三类误报
本文围绕 get-shit-done(GSD)SDK 查询处理器
validate.health的最新修复展开:它针对真实工作流中高发的三类“健康体检误报”——999.X待办清单(backlog)阶段目录、里程碑归档(milestone-archive)布局下的阶段目录,以及带描述符的 PLAN/SUMMARY 文件名配对——逐一修正判定逻辑。读完本文,你将理解 GSD 规划体系(.planning/)的目录布局约束、validate.health的十余项检查清单与输出契约,并能直接通过gsd-sdk query validate.health对项目做准确、无噪声的完整性诊断。
该修复记录于 changeset 片段 .changeset/graceful-geese-tumble.md(PR 3479,类型 Fixed),其对应的全部实现与回归测试均可在此仓库内直接核对。
一、背景:为什么需要一个“会体检”的查询处理器
get-shit-done 是一个面向 Claude Code 的元提示(meta-prompting)与上下文工程系统,它以“规格驱动开发”为核心:每个项目在 .planning/ 目录下维护一套高度结构化的规划产物,包括 PROJECT.md、ROADMAP.md、STATE.md、config.json,以及 phases/(活动阶段目录)、milestones/(归档阶段目录)等。
由于这套规划文件既是 Agent 决定“下一步做什么”的依据,也是各 slash 命令(/gsd-plan-phase、/gsd-execute-phase 等)的输入,规划目录一旦出现缺文件、错命名、编号断档,就会向下游放大为执行错误。因此仓库提供了验证类查询处理器(validate 家族)做“体检”,其中:
validate.consistency——跨文件一致性扫描(编号断档、PLAN/SUMMARY 配对、frontmatter 完整性等);validate.health——最综合的 10+ 项完整性检查,支持--repair自动修复,是本文主角;validate.agents、validate.context——分别校验 Agent 文件安装与上下文窗口利用率。
它们被统一注册在 validate.* 命令清单中(见 sdk/src/query/command-manifest.validate.ts),并由 sdk/src/query/command-family-handlers.ts 映射到实际执行函数。validate.health 的实现位于 sdk/src/query/validate.ts(TypeScript 原生实现,由旧版 verify.cjs 移植而来),其注册名 canonical: 'validate.health'、别名 validate health,mutation: false、outputMode: 'json',即它是一个只读的 JSON 查询命令。
调用方式:SDK 查询、CLI 与 slash 命令
validate.health 有三种等价入口,均输出结构化 JSON:
# 1. SDK 查询(推荐,注册表中的规范化点号名或空格别名皆可)
gsd-sdk query validate.health
gsd-sdk query validate health
# 2. 旧版 CJS 入口(gsd-tools.cjs)
node gsd-tools.cjs validate health
# 3. Claude Code 内的 slash 命令
/gsd-health # 仅体检
/gsd-health --repair # 体检并自动修复可恢复问题
其中 --repair 是 validateHealth 处理器的核心参数(args.includes('--repair')),用于自动修复可恢复缺陷(见下文的修复清单)。若在本仓库源码目录内直接调用 SDK,可先构建 SDK 后执行 node ./sdk/dist/cli.js query validate.health(SDK 用法详见 sdk/README.md)。
二、validate.health 的检查清单与输出契约
2.1 输入前置:.planning/ 与 CWD 守卫
处理器首先执行“家目录守卫”(E010):若解析后的 projectDir 等于用户主目录,说明当前工作目录错误,此时体检会读到错误的 .planning/,处理器直接返回 status: 'error' 并给出修复建议 cd into your project directory and retry(见 sdk/src/query/validate.ts)。
随后建立两类路径基线:
planBase = .planning/;roadmapPath = .planning/ROADMAP.md。
2.2 逐项检查(Check 1–10)
处理器按固定顺序执行以下检查(源码中的代码即各 Check 的注释锚点,全部位于 sdk/src/query/validate.ts):
| 检查 | 判定项 | 输出 issue |
|---|---|---|
| Check 1 | .planning/ 目录是否存在 |
缺失报 E001 |
| Check 2 | PROJECT.md 是否存在,是否含 ## What This Is / ## Core Value / ## Requirements 三个必需小节 |
缺失报 E002,缺小节报 W001 |
| Check 3 | ROADMAP.md 是否存在 |
缺失报 E003 |
| Check 4 | STATE.md 是否存在及其引用的阶段是否合法 |
缺失报 E004(可修复);引用未声明阶段报 W002 |
| Check 5 / 5b | config.json 是否为合法 JSON、schema 校验、workflow.nyquist_validation 键是否存在 |
解析错误报 E005(可修复),model_profile 非法报 W004,缺文件报 W003,缺键报 W008 |
| Check 6 | 阶段目录命名是否符合 NN-name 格式 |
不符报 W005 |
| Check 7 | 孤立 PLAN(有 PLAN 无 SUMMARY) | 报 I001(仅信息) |
| Check 7b | RESEARCH 中含 ## Validation Architecture 但缺 VALIDATION.md |
报 W009 |
| Check 8 | ROADMAP 与磁盘阶段目录双向同步 | ROADMAP 有、磁盘无报 W006;磁盘有、ROADMAP 无报 W007 |
| Check 9 | STATE.md 当前阶段与 ROADMAP 完成状态交叉校验 | 状态不同步报 W011 |
| Check 10 | config.json 字段取值合法性(branching_strategy、context_window、分支模板占位符) |
分别报 W012–W015 |
其中 Check 4、Check 8 正是本文修复的三类误报中“里程碑归档目录”与“999.X 目录”所涉及的核心逻辑所在。
2.3 输出契约与状态推导
处理器把所有 issue 收集为 errors / warnings / info 三个数组,并按以下规则推导整体 status(sdk/src/query/validate.ts):
- 存在任何
error→broken; - 无 error 但有
warning→degraded; - 全部干净 →
healthy。
输出 data 中还包含 repairable_count(可修复错误 + 可修复警告之和)以及 repairs_performed(执行过 --repair 时列出实际修复动作)。同时每个 issue 项带 code、message、fix 字段,便于 Agent 直接消费后给出处理建议。
2.4 --repair 的三种自动修复动作
当传入 --repair 且检测到可修复问题时,处理器会执行:
createConfig/resetConfig:写入一组“只含安全默认值”的config.json(默认model_profile: 'balanced'、branching_strategy: 'none'、workflow.nyquist_validation: true等,见 sdk/src/query/validate.ts);regenerateState:根据 ROADMAP 结构重新生成最小化STATE.md(sdk/src/query/validate.ts);addNyquistKey:为已有config.json的workflow补写nyquist_validation: true(sdk/src/query/validate.ts)。
值得注意的是:修复程序只写“已知安全”的默认值,绝不臆造项目语义,因此设计上避免把体检工具变成数据破坏者。
三、三类误报的本质与修复原理
Changeset 片段明确指出,本次修复让 validate.health 规避了三类误报(false-positive):
- 接受
999.X待办清单阶段目录; - 在做“ROADMAP 是否存在”类检查时识别里程碑归档阶段目录;
- 规范化带描述符的 PLAN/SUMMARY 文件名配对。
下面结合源码逐一展开。
3.1 第一类:接受 999.X backlog 阶段目录(消除错误 W005)
为什么会产生误报。 get-shit-done 的“待办清单停车区”(Backlog Parking Lot)设计规定:backlog 项使用 999.x 编号,使其天然落在活动阶段序列(01、02…)之外(需求文档见 docs/FEATURES.md)。当一个 backlog 项被捕获时,会立刻创建对应的阶段目录,例如 .planning/phases/999.1-backlog-sweep/(目录布局在 docs/FEATURES.md,命令用法见 docs/COMMANDS.md)。
在早期版本的 Check 6 阶段目录命名检查中,目录名若不以“两位数字 + - + 描述”的形态出现,就会触发 W005(“doesn't follow NN-name format”)。但 999.1-backlog-sweep 这类目录由 /gsd-capture --backlog 合法产生,命名单本身满足仓库约定,却被误判为“格式异常”,导致健康项目始终处于 degraded 状态。
修复方式。 Check 6 的命名正则被放宽为兼容多位整数 + 可选小数段的形态:
// Check 6 阶段目录命名校验(W005),现接受 999.X backlog 目录
if (e.isDirectory() && !e.name.match(/^\d{2,}(?:\.\d+)*-[\w-]+$/)) {
addIssue('warning', 'W005', `Phase directory "${e.name}" doesn't follow NN-name format`, ...);
}
即:段首要求“两位及以上数字”,随后允许 (?:\.\d+)* 的任意小数扩展(.1、.2…),再跟 -[\w-]+。999.1-backlog-sweep 完全匹配,而真正不规范的 bad_name 依旧会被正确拦截。
回归测试锚点。 sdk/src/query/validate.test.ts 的用例 does not emit W005 for 999.X backlog phase directory naming (#3473) 创建了 .planning/phases/999.1-backlog-sweep 后断言不出现指向该目录的 W005;同文件上方还保留了对 bad_name 触发 W005 的对照组用例,确保修复没有把命名检查“一刀切放掉”。
3.2 第二类:ROADMAP 存在性检查识别里程碑归档目录(消除错误 W006/W007)
背景:里程碑归档布局。 当一个里程碑 vX.Y 完成时,milestone.complete(或 SDK 专属的 phases.archive)会把活动阶段目录整体搬移到归档布局 .planning/milestones/<milestone>-phases/ 下(归档行为可见 tests/milestone-archive.test.cjs,v1.0-phases 目录即归档产物)。也就是说,历史里程碑的阶段(例如 .planning/milestones/v1.7-phases/64-secondary-grader-fix/)不再位于扁平的 .planning/phases/ 下。
为什么会产生误报。 Check 8 做“ROADMAP ↔ 磁盘阶段目录”双向同步检查时,若只扫描扁平的 .planning/phases/,那么 ROADMAP 中已发货(SHIPPED)里程碑声明的阶段会找不到对应磁盘目录,从而误报 W006(“Phase in ROADMAP.md but no directory on disk”)。反向同理,只有归档目录、没有扁平目录的阶段可能误报 W007。
修复方式(源码级三层配合):
- 枚举归档目录:
listMilestoneArchiveDirs扫描.planning/milestones/下所有匹配/^v\d+.*-phases$/i的目录,并按版本号数值排序(v1.10排在v1.2之后),见 sdk/src/query/validate.ts。 - 收集归档阶段 token:
forEachArchivedPhaseToken遍历每个归档目录下的阶段子目录,用PHASE_TOKEN_FROM_DIR_RE(兼容CK-64-...这类项目代码前缀命名)提取规范阶段号并回调,见 sdk/src/query/validate.ts。 - 并入“磁盘有效阶段”集合:Check 8 在统计
diskPhases后,把归档 token 一并并入(await forEachArchivedPhaseToken(planBase, (token) => diskPhases.add(token))),于是 ROADMAP 声明的历史阶段被认为“存在”,不再触发W006;而W007仍只针对扁平活动目录中的“真孤儿”,不会把归档目录里的历史阶段当作当前多余阶段,见 sdk/src/query/validate.ts。
同样的归档识别思路也用于 Check 4(W002,STATE.md 阶段引用合法性):forEachArchivedPhaseToken 把归档阶段的 token 视为合法引用,避免跨里程碑引用历史阶段被误报(sdk/src/query/validate.ts)。
回归测试锚点。 单元测试层:sdk/src/query/validate.test.ts 构造 ROADMAP 中“Phase 7”仅存在于 .planning/milestones/v1.0-phases/07-old-shipped-phase/ 的场景,断言不出现指向 Phase 7 的 W006;sdk/src/query/validate.test.ts 反向验证纯归档阶段目录不触发 W007。集成测试层:tests/milestone-archive.test.cjs 直接通过 gsd-tools validate health 在归档布局项目上断言 Phase 64 无“no directory”类 W006。这些测试覆盖了 #3164(validate 家族识别归档布局)与 #3473 两个历史 issue 的回归诉求。
3.3 第三类:规范化 PLAN/SUMMARY 文件名配对(消除错误 I001)
为什么会产生误报。 GSD 的规划产物允许阶段文件带“描述符”(descriptor)。例如某阶段目录同时存在:
68-01-scaffolding-PLAN.md(带描述符scaffolding的计划文件)68-01-SUMMARY.md(不带描述符的总结文件)
两者本质属于同一对 68-01。但 Check 7 的“孤儿计划”检查在早期版本按字面文件名比对,会认为 68-01-scaffolding-PLAN.md 没有对应的 68-01-scaffolding-SUMMARY.md,从而产生信息级误报 I001(“has no SUMMARY.md, may be in progress”)。虽然只是 info,但噪声化的 info 会污染状态推导与 Agent 决策。
修复方式。 引入规范化配对函数 canonicalPlanStem:把文件名主干中“阶段号-计划号”之后的描述符剥离,得到规范主干:
/**
* Canonical plan stem used for PLAN/SUMMARY matching.
* Example: `68-01-scaffolding` -> `68-01`.
*/
function canonicalPlanStem(stem: string): string {
const m = stem.match(/^(\d+[A-Z]?(?:\.\d+)*-\d+)/i);
return m ? m[1] : stem;
}
Check 7 先为每个 SUMMARY 记录其主干与规范主干,再判断每个 PLAN 是否存在与自身字面主干或规范主干匹配的 SUMMARY:
summaryBases.add(summaryBase); // e.g. "68-01"
summaryBases.add(canonicalPlanStem(summaryBase)); // 归一化后仍为 "68-01"
// 对每个 plan,只要 PLAN 主干或其规范化主干命中 SUMMARY 集合即视为成对
const canonicalBase = canonicalPlanStem(planBase2);
if (!summaryBases.has(planBase2) && !summaryBases.has(canonicalBase)) {
addIssue('info', 'I001', `${e.name}/${plan} has no SUMMARY.md`, 'May be in progress');
}
这样 68-01-scaffolding-PLAN.md(规范主干 68-01)能与 68-01-SUMMARY.md 正确配对,I001 不再误报。这一规范化逻辑同时服务于 validate 家族另一处理器的一致性检查(sdk/src/query/validate.ts 定义了该函数,Check 7 使用之)。
回归测试锚点。 sdk/src/query/validate.test.ts 构造 .planning/phases/68-bug-surface/ 下仅含 68-01-scaffolding-PLAN.md 与 68-01-SUMMARY.md 的场景,断言不产生指向该 PLAN 文件的 I001。
四、从实现看设计:validate 家族为何值得信赖
把上述三处修复放在一起看,能提炼出 get-shit-done 验证体系的几个设计原则,这些都直接体现在代码与测试中:
- “目录布局变体”是被显式建模的一等公民。 归档布局、backlog 的
999.X编号、项目代码前缀(如CK-64-...)都不是异常,而是合法状态。验证器通过listMilestoneArchiveDirs、forEachArchivedPhaseToken、collectPhaseRoots等辅助函数把布局变体显式纳入集合计算,而不是靠“忽略警告”来掩盖问题。 - 阶段号比较一律“规范化”后做。 无论是 STATE.md 引用(Check 4)、ROADMAP 同步(Check 8),还是文件名配对(Check 7),比较时都会把
64/64A/06.1/0064等变体归一到规范 token 或补零变体再比较(phaseVariants与补零逻辑可见 sdk/src/query/validate.ts 与 sdk/src/query/validate.ts),既消除格式误报,也保证22A不会被误折叠成22(有专门测试守护:does not alias 22A to 22 when suppressing W006)。 - issue 带 code + fix 建议,Agent 可直接执行。 每条 issue 都携带稳定的错误码(
E001–E015、W001–W015、I001、I010等)与修复指引字符串,配合repairable_count/--repair,形成了“体检 → 归因 → 自动修复 → 再验证”的闭环。
4.1 相关文档与进一步阅读
- 查询处理器注册契约与路由总表:sdk/src/query/QUERY-HANDLERS.md(validate 家族位列“Registered”)。
- validate 命令清单定义:sdk/src/query/command-manifest.validate.ts。
- validate 家族处理器映射:sdk/src/query/command-family-handlers.ts。
- slash 命令用户文档(
--repair/--context参数):docs/COMMANDS.md。 - backlog 捕获与 999.x 编号约定:docs/COMMANDS.md 与 commands/gsd/capture.md。
- 归档布局集成测试(
#2684/#3164/#3600):tests/milestone-archive.test.cjs。
五、实战:如何验证你的项目已“健康”
以源码仓库或任意 GSD 项目为例,确认修复生效与整体体检无噪声:
# 在项目根目录执行体检(必须包含 .planning/)
gsd-sdk query validate.health
# 期望输出中 status 为 healthy / degraded,并给出结构化 issue 列表
# 若 ROADMAP 存在已发货里程碑,而其阶段已归档到 .planning/milestones/vX.Y-phases/,
# 则不应再出现指向这些历史阶段的 W006
对三类误报场景做针对性自检:
| 场景构造 | 修复前 | 修复后 |
|---|---|---|
存在 .planning/phases/999.1-backlog-sweep/ |
错误 W005 |
无 W005 |
ROADMAP 声明 Phase 7,磁盘上仅有 .planning/milestones/v1.0-phases/07-old-shipped-phase/ |
错误 W006 |
无 W006 |
同目录下仅 68-01-scaffolding-PLAN.md + 68-01-SUMMARY.md |
错误 I001 |
无 I001 |
如需自动修复可恢复缺陷(重建缺失的 config.json / STATE.md、补 nyquist_validation 键),在确认这些文件确属丢失后执行:
gsd-sdk query validate.health --repair
若想对运行环境做上下文窗口利用率体检,可使用 /gsd-health --context(阈值规则:小于 60% 为 healthy,60%–70% 为 warning,达到 70% 及以上为 critical,见 sdk/src/query/validate.ts)。
需要提醒的适用前提:validate.health 的判定模型面向 GSD 自身生成的 .planning/ 结构,检查是否命中 .planning 目录、ROADMAP 阶段编号与目录命名约定等均以本仓库 sdk/src/query/validate.ts 中实现为准;若项目启用了 phase_naming: custom 或其它非默认规划布局,检查口径会相应走自定义分支(例如编号断档检查会整体跳过,见 sdk/src/query/validate.ts)。
结语
validate.health 的三类误报修复,是 get-shit-done “用工程化手段治理 AI 工作流”思路的缩影:验证器必须深刻理解自身的目录布局语义,把“归档”“backlog”“描述符命名”这些真实状态都当作合法输入,才能真正成为值得 Agent 信赖的体检仪。从 changeset 一行描述出发,本次修复在 sdk/src/query/validate.ts 中落实为命名正则放宽、归档 token 并入磁盘阶段集合、以及 PLAN/SUMMARY 规范化配对三处逻辑变更,并由 sdk/src/query/validate.test.ts 与 tests/milestone-archive.test.cjs 中的多组回归用例钉死,确保此后任何改动都不会让这三类误报“复活”。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K638- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python380
cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端TypeScript2 K146
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python48167
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go20743
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java34251