【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载本文基于 docs/adr/0002-command-contract-validation-module.md状态Accepted2026-05-05展开讲解 gsd-core 如何把commands/gsd/*.md命令文件的契约集中到一个校验缝validation seam并通过 CI 前置 lint 脚本与行为回归测试双层强制执行。读完后你将掌握该契约的完整规则集、参考引用的三种形态与可达性图算法以及 lint 脚本、共享解析助手 和 契约测试 的对应实现能够自行排查和规避任何命令文件层面的契约违约。背景契约校验曾经散落且不一致在 ADR-0002 之前命令文件的契约约束分散在多个测试里且互不覆盖tests/skill-frontmatter-contract.test.cjs折叠自enh-2790-skill-consolidationconsolidation epic #1969只检查特定整合后命令的存在性与 frontmattertests/docs-update.test.cjs折叠自bug-3135-capture-backlog-workflowconsolidation epic #1969检查execution_context中 -引用的解析没有任何测试同时校验allowed-tools合法性、name:命名约定、description:非空性。后果是任何触及命令文件的 PR 都可能破坏契约而无测试捕获。ADR 中给出的具体案例是add-backlog.md缺口#3135——workflow 文件在完整整合周期中缺失直到专门写了针对性回归测试才被发现。此外还有两类具体的工程债冗余的正文 -引用65 个命令文件中 40 个存在重复的 prose -reference——同一路径既出现在execution_context真正加载文件的地方里又出现在process正文里不起作用。每次调用多加载约 900 token 的死重并形成漂移缝正文引用可以独立于可执行的execution_context引用变陈旧。两个巨型命令的内联实现最大的两个命令debug.md与thread.md把完整实现内联而非委托给 workflow 文件导致约 4,400 token 的实现细节在每次会话无论是否用到这两个命令都作为 skills 索引描述被加载。命令文件契约的完整定义ADR 定义的契约由 6 条规则组成。前 5 条是逐文件的 frontmatter/正文规则第 6 条是仓库级的可达性图规则#规则粒度1name:字段存在、非空且匹配gsd:*或gsd-*ns-命令使用gsd-逐文件2description:字段存在且非空逐文件3allowed-tools:块存在、非空所有条目来自规范工具集canonical tool set逐文件4execution_context块内每个-引用都能解析到磁盘上存在的文件逐文件5execution_context块内每个-引用独占一行行尾不得有 prose逐文件6每个gsd-core/workflows/*.md文件都能从至少一个commands/、agents/、skills/加载器出发、经由gsd-core/**传递到达#3560仓库级前 5 条规则可以直接对照一份真实命令文件理解。以 commands/gsd/execute-phase.md 为例--- name: gsd:execute-phase description: SDD phase execution — execute all plans in a phase with dependency-aware wave parallelization argument-hint: phase-number [--wave N] [--gaps-only] [--interactive] [--tdd] effort: max allowed-tools: - Read - Write - Edit - Glob - Grep - Bash - Agent - TodoWrite - AskUserQuestion requires: [phase, verify-work] ---name:使用gsd:前缀满足规则 1description:非空满足规则 2allowed-tools:列出的 10 个工具必须全部落在规范工具集中满足规则 3。而该文件正文中的execution_context块execute-phase.md#L40-L43execution_context ~/.claude/gsd-core/workflows/execute-phase.md ~/.claude/gsd-core/references/ui-brand.md /execution_context则受规则 4、5 约束两个-引用各自独占一行且规范化后必须能在gsd-core/下找到对应文件。命名规则 1 的另一种形态是 commands/gsd/ns-context.md 的name: gsd-context——ns-命名空间下的命令使用gsd-连字符前缀。共享助手模块lint 与测试的唯一事实来源契约解析逻辑集中在 scripts/command-contract-helpers.cjs其文件头注释明确说明定位这是lint 脚本与测试套件共享的契约常量与解析器的唯一事实来源single source of truth保证 lint-command-contract.cjs 与 command-contract.test.cjs 对什么算合法工具、合法 -引用、合法 frontmatter 结构永远口径一致——在此新增一个规范工具两个消费方会同时强制执行它。规范工具集规范工具集是一个Setcommand-contract-helpers.cjs#L20-L27const CANONICAL_TOOLS new Set([ Read, Write, Edit, Bash, Glob, Grep, Task, Agent, Skill, SlashCommand, AskUserQuestion, WebFetch, WebSearch, TodoWrite, mcp__context7__resolve-library-id, mcp__context7__query-docs, mcp__context7__*, ]);校验allowed-tools时的判定逻辑lint-command-contract.cjs#L113-L124对每个工具名做两层判定要么精确命中CANONICAL_TOOLS要么以mcp__context7__开头且通配项mcp__context7__*在集合中——即允许 context7 MCP 命名空间下的任意具体工具。其余任何工具名包括Task之外拼写的变体都会产生allowed-tools: unknown tool tool违规。frontmatter 解析复用唯一的围栏识别器parseFrontmattercommand-contract-helpers.cjs#L29-L47并不自写 YAML 解析而是调用构建产物gsd-core/bin/lib/frontmatter-fence.cjs中的locateFrontmatterFence源在 src/frontmatter-fence.cts#L90 起的locateFrontmatterFence。注释解释了这一选择围栏识别器能处理 BOM、CRLFWindows autocrlf 检出、相邻空块与----仿形以运行时读取器看到的样子精确读取。拿到围栏范围后解析器逐行处理key: value行写入字段- value列表行则把值按换行拼接挂到当前 key 下这正是allowed-tools:多行列表能被完整取到的原因。行按\r?\n分割保证尾随\r不会泄漏进字段值。execution_context-引用解析executionContextRefscommand-contract-helpers.cjs#L49-L66用正则execution_context(?:_extended)?([\s\S]*?)\/execution_context(?:_extended)?提取所有块对其中每行只处理以开头的行取第一个空白分隔 token 作为引用line.length token.length即判定该行有尾随 prose规则 5 的违约信号规范化时剥掉、~/或$HOME/前缀以及可选的.claude/与gsd-core/前缀得到相对gsd-core/的路径。lint 侧的check函数lint-command-contract.cjs#L95-L140把规范化路径拼回gsd-core/之下用fs.existsSync验证存在性规则 4逐文件的六项检查全部通过后返回null有违规则返回{ file, violations }汇总。仓库级可达性三种引用形态与孤儿检测规则 6 与前 5 条在性质上不同它是一次运行只算一次的仓库级可达性图。它把commands/、agents/、skills/下所有 markdown 文件作为加载器种子然后沿引用在gsd-core/**内传递遍历注意是gsd-core/**而非仅gsd-core/workflows/因为references/或templates/文件本身也能命名 workflow 路径直到所有可达 workflow 被标记仍未标记的gsd-core/workflows/*.md即被报告为孤儿。三种被识别的引用形态workflowPathRefscommand-contract-helpers.cjs#L101-L129识别三种形态形态 A急切 -include路径段中含workflows/可选地被、~/或$HOME/、.claude/、gsd-core/前缀修饰如~/.claude/gsd-core/workflows/scan.md——与前 5 条规则解析的execution_context语法同源形态 B惰性路径与 A 相同但无——只在 prose 或代码中被命名、由命令在运行时按需通过 Read/Bash 读取的路径。从源码注释看这种设计源于渐进披露#717大部分 workflow 内容被刻意放在急切路径之外以保持常见路径廉价因此命令只在正文里命名 workflow对executionContextRefs不可见但运行时依然需要该文件存在形态 C父相对子文件路径完全不含workflows/前缀的路径如execute-phase/steps/post-merge-gate.md——因为steps/、modes/、templates/子目录只存在于workflows/之下解析器隐式地把它们根在workflows/下。实现上有两个值得注意的细节两条正则都用\.md(?![A-Za-z0-9_])负向前瞻锚定扩展名.mdx、.md5会被直接拒绝而不是被静默截断成貌似合法的.md路径含..遍历段的引用被丢弃而不是上报——该解析器只可能报告workflows/之下的路径..无法把它带出。结果去重且保持首次出现顺序。种子集约束为什么被提及不等于可达unreachableWorkflowscommand-contract-helpers.cjs#L159-L177是一个带visited集与队列的迭代图遍历for (const content of loaderContents) { for (const ref of workflowPathRefs(content)) queue.push(ref); } while (queue.length 0) { const p queue.pop(); if (visited.has(p)) continue; visited.add(p); if (gsdFiles.has(p)) { for (const ref of workflowPathRefs(gsdFiles.get(p))) queue.push(ref); } } return workflowPaths.filter(p !visited.has(p));种子集刻意只来自加载器绝不含 workflow 自身内容。ADR 给出的理由在源码注释中同样明确如果从 workflow 内容本身播种会掩盖两种失败模式——一个只引用自己的 workflow 会满足自身可达性两个只互相引用的 workflow 会形成一个内部看似连通、但没有任何命令/agent/skill 真正打开的孤岛。两种情况都是外部什么都到不了意义上的孤儿都必须上报。同理docs/与测试 fixture 刻意不算加载器文档或测试里仅仅提及某条路径不等于任何运行时会加载它——只有commands/、agents/、skills/才算。visited集同时保证遇到引用环时遍历必然终止。lint 脚本侧的驱动函数checkWorkflowReachabilitylint-command-contract.cjs#L71-L91负责收集三路加载器 markdown 内容、以gsd-core/相对键构建gsdFilesMap覆盖整个gsd-core/**、枚举workflows/下全部文件后调用上述闭包。双层强制lint 脚本与行为测试如何各司其职快速 lintCI 前置、毫秒级scripts/lint-command-contract.cjs 是面向全部 65 个命令文件的快速校验器用法node scripts/lint-command-contract.cjs # 默认以脚本所在仓库为根 node scripts/lint-command-contract.cjs --root DIR # 指定根目录测试 fixture 用行为约定文件头注释 L16 与 main 函数退出码 0 表示干净退出码 1 表示有违规并输出诊断。干净时 stdout 输出两行摘要ok lint-command-contract: 65 command files checked, 0 violations ok lint-command-contract: 179 workflow files, 179 reachable, 0 unreachable有违规时 stderr 列出每个文件的具体违规项并指回契约规范文档See docs/adr/0002-command-contract-validation-module.md for the contract spec.孤儿 workflow 的报错信息额外强调运维含义Each file above ships to every runtime but is never referenced by any command, agent, or skill loader (directly or transitively). Either wire it to a loader or delete it — removing a command must sweep its orphaned workflow.在 package.json 的scripts中它被挂进lint:ci链lint:ci: npm run lint ... node scripts/lint-command-contract.cjs ...作为测试套件之前的 CI 步骤执行。这形成 ADR 所说的双层结构lint 提供毫秒级的快速失败在 CI 跑测试之前就能拦截绝大多数违约测试提供权威的行为契约对活动文件系统做全量断言。行为回归测试对整个命令面的权威契约tests/command-contract.test.cjs 替换了散落在 enh-2790 与 bug-3135 中的契约覆盖成为整个命令面的权威行为契约测试。其结构直接映射契约规则五个describe块分别对应规则 1–5且每个命令文件 × 每条规则都是一个独立node:test用例for (const { name, full } of commandFiles)循环生成因此失败信息能精确到文件与规则例如execute-phase.md: name: must start with gsd: or gsd-, got ...文件头以// allow-test-rule: source-text-is-the-product声明测试性质commands/gsd/*.md本身就是部署的技能面测试其文本即测试运行时行为测试通过process-seam助手以子进程方式真实运行 lint CLIrunNode([LINT_SCRIPT, --root, dir], { timeoutMs: PROBE_TIMEOUT_MS })验证的是脚本的实际进程行为而非函数调用。#3560 可达性规则的端到端验证测试中#3560 — unreachableWorkflows closure一节command-contract.test.cjs#L294-L441对闭包语义做了系统性单元验证植入孤儿必报、急切/惰性/父相对三种形态均可达、传递可达含深度 3、自引用不可达、互引孤岛不可达、引用环终止、仅 docs 提及不算可达Goodhart 防护提及路径 ≠ 加载路径、悬空引用、152 个文件里精确找出那 1 个孤儿、CRLF 容忍等。随后的#3560 — rule 6 fails the build on a real orphancommand-contract.test.cjs#L443-L548则构建最小 fixture 树做端到端验证buildBaseFixture在一个临时目录里铺出commands/gsd/fixture-command.md合法 frontmatter 一个急切加载live.md的execution_context块与gsd-core/workflows/live.md然后干净 fixture →exitCode 0植入无人引用的orphan.md→exitCode 1且诊断输出必须点名gsd-core/workflows/orphan.mdorphan.md仅被docs/SOMETHING.md提及 → 仍然失败orphan.md被live.md传递引用一跳之外→ 通过。这一组测试同时锁死了删除命令必须清扫其孤儿 workflow的运维纪律还有#3560 — deleted orphan workflows do not ship一节断言已删除的discovery-phase.md、plan-milestone-gaps.md不存在于gsd-core/workflows/、不出现在任何 tests/fixtures/install-tree 安装清单 JSON 中且五语言版本的docs/*/INVENTORY.md均不再提及它们。另一个真实回归案例是#3561 — /gsd-map-codebase --fast routes to a loadable workflowcommand-contract.test.cjs#L251-L277测试定位 commands/gsd/map-codebase.md 中匹配/^-\s*If it is\s*\--fast/的路由行用workflowPathRefs断言该行必须命名一个运行时可解析的workflows/scan.md路径同时用executionContextRefs断言完整 map 模式只急切加载workflows/map-codebase.md一个引用——即--fast 路由指向的文件必须真实存在而完整路径不得误加载它。决策的后果与收益ADR 的 Consequences 一节记录的落地结果与仓库现状可相互印证单一 lint 脚本毫秒级覆盖全部 65 个命令作为 CI 前置步骤先于测试套件运行行为测试取代散落覆盖command-contract.test.cjs成为整个命令面的权威行为契约测试替换 enh-2790 与 bug-3135 中的碎片覆盖token 回收40 个命令文件移除冗余正文 -引用每次调用约 900 tokendebug.md与thread.md重构为 workflow 委托模式从急切系统提示加载中移除约 4,400 token。对照当前 commands/gsd/execute-phase.md 的写法可以看到这一模式的成品形态process块只保留Execute end-to-end. Preserve all workflow gates...两行委托说明具体实现全部由execution_context指向的 workflow 文件承载命名对齐workflows/extract_learnings.md更名为workflows/extract-learnings.md与其余 workflow 文件的连字符约定一致对应命令 commands/gsd/extract-learnings.md;单一事实来源原则execution_context块是命令加载内容的唯一权威声明正文中不再出现重复的 -引用。小结把文件即产品落成可执行的契约ADR-0002 的工程价值在于把一个隐性的、散落的约定变成了两条互相印证的强制链scripts/lint-command-contract.cjs 在 CI 早期用毫秒级成本拦截 frontmatter 违约与孤儿 workflowtests/command-contract.test.cjs 则作为活动文件系统上的权威行为测试兜底两者共享 scripts/command-contract-helpers.cjs 中唯一的解析实现杜绝了lint 与测试口径漂移这种元问题。对维护者而言新增命令文件时需要对照的清单就是契约六条gsd:/gsd-前缀的name:、非空description:、规范工具集内的allowed-tools:、独占一行且磁盘存在的execution_context-引用以及确保你新增的 workflow 文件至少能被某个命令、agent 或 skill 加载器直接或传递到达——否则lint:ci会在测试之前把它作为孤儿拦下来。赞分享【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载相关推荐gsd-core 命令契约校验ADR-0002从命令文件到 CI 的双层验证体系gsd core 命令契约校验ADR 0002从命令文件到 CI 的双层验证体系 本篇指南以 gsd core 仓库中的 ADR 0002 决策记录为主线get-shit-done ADR-0002 深度解析用 lint 回归测试两层机制集中校验 commands/gsd 命令契约get shit done ADR 0002 深度解析用 lint 回归测试两层机制集中校验 commands/gsd 命令契约 本文以 get shit人工智能AI 应用提示工程开发工具工作流自动化AI Agentget-shit-done 命令契约校验ADR-0002以双层校验与前端字段规范锁定 65 个 gsd 斜杠命令的质量基线get shit done 命令契约校验ADR 0002以双层校验与前端字段规范锁定 65 个 gsd 斜杠命令的质量基线 导读 本篇文章围绕 get s人工智能AI 应用提示工程开发工具工作流自动化AI Agent上一篇Adobe Downloader终极指南macOS上高效获取Adobe全家桶的完整解决方案下一篇终极图片转PDF工具img2pdf完整指南与性能深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考