get-shit-done 贡献指南:Issue-First 流程、Changeset 机制与 node:test 测试纪律
【免费下载链接】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
GSD(get-shit-done)是一个面向 Claude Code 等 AI 编码运行时的元提示与规格驱动开发系统。它的 CONTRIBUTING.md 定义了一套严格但完整的贡献工程体系:从「Issue 先行、无批准不写码」的准入规则,到基于随机命名的 changeset 片段解决 CHANGELOG 合并冲突,再到基于node:test的行为化测试标准与 12 项 QA 矩阵。读完本文,你将掌握向 GSD 提交修复、增强或新功能的完整流程、PR 准入标准,以及仓库中可复用的测试编写与 CI 强制机制。
快速开始
本地开发环境的最小准备步骤:
# 克隆仓库并进入目录 git clone <get-shit-done 仓库地址> cd get-shit-done # 安装依赖 npm install # 运行测试 npm test需要留意运行环境前提:package.json 中声明了"engines": { "node": ">=22.0.0" },即Node 22 是兼容性下限,Node 24 是主要 CI 目标,Node 26 是前向兼容目标——即使 Node 26 的 CI 泳道尚未稳定,代码和测试也必须保持对 Node 26 的兼容。测试入口是node scripts/run-tests.cjs(npm test即调用它),默认运行tests/下全部*.test.cjs文件。
贡献类型:Fix、Enhancement 与 Feature 三道不同的门
GSD 接受三类贡献,每类都有独立流程和不同的接受门槛。原文要求「打开任何东西之前先读这一节」,因为三类贡献的驳回逻辑完全不同。
Bug 修复(Fix)
Fix 修正的是已经损坏的东西:崩溃、错误输出、或与文档行为相悖的行为。流程为四步:
- 打开一个 Bug Report issue 并完整填写;
- 等待维护者确认为 bug(打
confirmed-bug标签)——对明显可复现的 bug 通常很快; - 修复它,并先写一个本应捕获该 bug 的测试;
- 使用 Fix PR 模板 打开 PR,并链接已确认的 issue。
常见驳回理由:无法复现、按设计工作(works-as-designed)、与现有 issue 重复。
对照 Fix PR 模板 可以看出模板把流程要求固化成了检查项:必须写Fixes #NNN、必须确认 issue 带有confirmed-bug标签、必须声明回归测试是否已添加(或说明理由)、并列出验证过的平台(macOS/Windows/Linux,含 Windows 反斜杠路径处理)与运行时(Claude Code、Gemini CLI、OpenCode 等)。
增强(Enhancement)
增强改进现有功能——更好的输出、更快的执行、更干净的交互、更广的边界处理,但不添加新命令、新工作流或新概念。其门槛是:
任何代码写下来之前,必须有一个范围明确的书面提案并获得维护者批准。若链接的 issue 没有
approved-enhancement标签,PR 会不经评审直接关闭。
流程:打开 Enhancement issue(模板强制要求写明:解决的问题、具体收益、变更范围、已考虑的替代方案)→等待approved-enhancement标签→ 严格按批准范围写码(范围扩大须回到 issue 重新审批)→ 用 Enhancement PR 模板 打开 PR 并链接已批准 issue。
新功能(Feature)
Feature 添加新东西:新命令、新工作流、新概念、新集成。它门槛最高,因为 GSD 是一个由小团队维护、面向独立开发者的工具,新功能意味着永久性维护负担。流程比 Enhancement 多一步「先讨论」:
- 先查 Discussions——如果想法已被提出并被拒绝,不要重复开 issue;
- 打开 Feature Request issue 附完整规格书,模板要求:解决的独立开发者问题、添加内容、受影响文件与系统的完整范围、用户故事、验收标准、维护负担评估;
- 等待
approved-feature标签。批准不保证——「GSD 刻意保持精简,许多合理想法会因与项目设计理念冲突而被拒绝」; - 严格按批准的规格实现;
- 用 Feature PR 模板 打开 PR 并链接已批准 issue。
Issue-First 规则:无例外
No code before approval.(批准之前不写代码。)
- 修复:开 issue → 确认是 bug → 再修;
- 增强:开 issue → 拿到
approved-enhancement→ 再写码; - 新功能:开 issue → 拿到
approved-feature→ 再写码。
没有正确标签 issue 链接的 PR 会被自动关闭。文档强调这「不是官僚障碍——它保护你不把时间花在会被拒绝的工作上,也保护维护者不去评审从未被认可的变更」。
提议 ADR 或 PRD
ADR(架构决策记录)记录重大架构决策,PRD(产品需求文档)在实现前捕捉功能的 what 与 why。两者同样受 issue-first 规则约束:
- 按类型开 issue(重议既有领域的 ADR 用 enhancement,新架构面的 ADR 用 feature,策略/文档决策用 chore),完整填写;
- 等待维护者批准——issue 必须带有
approved-enhancement或approved-feature标签(chore 则需确认)才能创建文件; - GitHub 分配的 issue 编号成为文件名前缀,在以 issue 命名的分支上创建文件:
- ADR:
docs/adr/<issue#>-<slug>.md - PRD:
docs/prd/<issue#>-<slug>.md - 分支:
docs/<issue#>-<slug>
- ADR:
- 用对应模板打开 PR,并在正文写
Closes #<issue#>关闭 issue。
核心纪律:一个 issue = 一个 ADR 或 PRD = 一个 PR,不要把多个决策打包进一个文件或一个 PR;也不要本地计算「下一个序号」——任何用旧式NNNN-*顺序编号命名的新 ADR/PRD 都会在合并前被要求改名为<issue#>-<slug>.md格式。文档给出的实例是:Issue #3485 获批后成为前缀docs/adr/3485-adr-prd-naming-convention.md。仓库中确实保留了这类按 ADR 编号命名的存量文件(如 0001-dispatch-policy-module.md),新规则只约束新增文件。
PR 规范:架构标准、链接规则与 CI 强制
架构与领域标准
以下文件是维护者定义的编码标准,贡献时必须视为权威(canonical):
- CONTEXT.md — 领域语言与模块命名标准
- docs/adr/ — 已接受的架构决策记录
完整的贡献者要求(CONTEXT.md 格式、ADR 治理、AI 辅助工作标准)在 docs/contributor-standards.md 中。要点摘录:
- 命名或重构模块/接口/缝(seam)之前先读
CONTEXT.md; - 在被触及领域的代码注释、测试、issue/PR 文本和文档中一致使用
CONTEXT.md词汇,不自造同义词; - 提议或实现架构变更前先查相关 ADR;有意推翻某个 ADR 决策时,必须在 issue 和 PR 理由中明确声明;
- 不要借「顺手清理」改写
CONTEXT.md/ADR 中的维护者意图; - 若使用 AI 助手,先让它读
CONTEXT.md和相关 ADR 再写代码,开 PR 前核对它是否用对了词汇。
文档还特别点名了CJS↔SDK 接缝:修改bin/lib/*.cjs或sdk/src/**前须读 docs/agents/cjs-sdk-seam.md,它规定了 Shared Module 的规范模式(数据清单 + 权威源文件 + 生成器 + 新鲜度检查 + 适配器)以及会阻断新漂移的「hand-sync 成对 lint」。新增<name>.cjs↔<name>.ts成对文件要么迁移为 Shared Module,要么在 scripts/shared-module-handsync-allowlist.json 中加入带理由的显式白名单条目——加白名单需要维护者经 CODEOWNERS 评审。
PR 硬性清单
- 每个 PR 必须链接一个已批准 issue,无链接 PR 直接关闭,无例外;
- 禁止 draft PR——草稿 PR 会被自动关闭。工作没做完就留在本地分支;
- 用对模板——Fix、Enhancement、Feature 各有独立模板,功能 PR 用错模板或套用默认模板即为驳回理由;
- 用关闭关键字链接——PR 正文必须出现
Closes #123、Fixes #123或Resolves #123,否则 CI 检查失败并自动关闭; - 一个 PR 一个关注点——bug 修复、增强、新功能必须分开;
- 禁止顺手格式化与改动无关的代码;
- 不要把测试夹具更新塞进
docs:或无关提交——生产变更导致某条既有测试断言过期时,测试修正必须以独立的test:(或fix:)提交落地。原因在于 release-sdk hotfix 的 cherry-pick 过滤器按提交主题前缀(fix:、chore:、test:)路由:藏在docs:前缀下的测试修正在挑选器眼中不可见,会把「生产代码已改、测试断言已过期」的半状态带进 hotfix 分支。v1.42.3 就撞上了这个模式(issue #3621); - CI 必须全绿,且满足前述 Node 版本兼容矩阵;
- 范围必须与批准的 issue 一致——超出的部分会被要求移除或另开 issue。
CHANGELOG 条目:丢一个片段,别直接编辑
不要直接编辑 CHANGELOG.md。两个 PR 都往### Fixed块里追加内容必然在合并时冲突——git 无法在没有人类介入的情况下决定串行顺序。正确做法是每个有用户可见变更的 PR 在.changeset/中放一个片段文件:
npm run changeset -- --type Fixed --pr <YOUR_PR_NUMBER> \ --body "**\`/gsd-foo\` no longer drops trailing slashes** — explain the user-visible change."该命令(对应 scripts/changeset/new.cjs)写入.changeset/<形容词>-<名词>-<名词>.md——三个随机单词让并发 PR 永不碰撞。允许的type:值遵循 Keep a Changelog 约定:Added、Changed、Deprecated、Removed、Fixed、Security。
片段格式(frontmatter + 一句话正文)可在 .changeset/README.md 中查到,发布时由 release 工作流调用scripts/changeset/cli.cjs render --version vX.Y.Z --date YYYY-MM-DD统一整合进CHANGELOG.md、替换## [Unreleased]块并删除已消费片段,该过程是幂等的。仓库的 .changeset/ 目录下当前就存有大量此类片段文件,是这套机制在真实运转的直接证据。
CI 强制:Changeset Required工作流(scripts/changeset/lint.cjs)会让任何触碰bin/、get-shit-done/、agents/、commands/、hooks/或sdk/src/却没有.changeset/*.md片段的 PR 失败。
退出机制:确无用户可见影响(测试重构、lint 配置、CI 微调、纯格式化)的 PR 可加no-changelog标签,lint 会尊重它。拿不准时,加片段。
文档更新义务:改动什么就更新什么文档
如果 PR 新增、变更、废弃或移除了用户可见行为,必须更新docs/下的相关文档。CI 会对任何类型为Added、Changed、Deprecated或Removed的 changeset 片段检查:若 diff 中没有至少一个docs/文件变更则失败。Fixed与Security片段不触发此检查——bug 修复是恢复已文档化的行为,不引入需要文档化的新行为(但如果修复同时纠正了文档错误,仍应顺手改文档)。
「Which docs to update」对照表:
| 变更类型 | 必须更新的文档 |
|---|---|
| 新命令或新 flag | docs/COMMANDS.md、docs/FEATURES.md |
| 命令行为或输出变化 | docs/USER-GUIDE.md、docs/COMMANDS.md |
| 配置 / schema 变更 | docs/CONFIGURATION.md |
| 架构变更 | docs/ARCHITECTURE.md、docs/adr/ |
| Agent 或 skill 变更 | docs/AGENTS.md |
| 移除的命令、flag 或工作流 | 所有引用过它的文档 |
语言策略:docs/与根 README.md 的内容必须用英文——英文是权威源。翻译版 README(README.pt-BR.md、README.zh-CN.md、README.ja-JP.md、README.ko-KR.md)由社区维护,不要求每个 PR 同步更新。
CI 强制实现:Docs Required工作流(scripts/lint-docs-required.cjs)读取 PR diff 触碰的 changeset 片段,只要其中任一类型属于Added/Changed/Deprecated/Removed,就要求 diff 中至少包含一个docs/文件。
带纸面痕迹的豁免:当变更确实无用户可见文档影响(基础设施重写、内部重构、纯测试、CI 修复),有两条路径:
- 标签:给 PR 加
no-docs标签,并留评论说明为什么不需要文档更新; - 逐片段标记:在触发检查的每个片段正文中(通常在末尾)单独一行写入
<!-- docs-exempt: <reason> -->。理由必填且非空——裸标记会被拒绝(无审计痕迹 = 无豁免)。该标记在解析时由 scripts/changeset/parse.cjs 提取,并在序列化进 CHANGELOG.md 和 release notes 之前从正文中剥离——它留在源片段里作审计痕迹,却不会泄漏进已发布的发布说明。行内提及(例如反引号内的标记语法)被刻意忽略,解析器只对独占一行的标记生效。两条路径都留有纸面痕迹:标签是全局的,标记是针对混合 PR 的逐片段粒度。
测试标准:只用 node:test,行为优先
基本纪律
所有测试使用 Node.js 内置测试运行器(node:test)与断言库(node:assert)。禁止使用 Jest、Mocha、Chai 或任何外部测试框架。
套件分组:测试按文件名后缀分组——
foo.security.test.cjs属于security套件;无后缀的foo.test.cjs属于unit。完整策略见 docs/TESTING-SUITES.md:unit(默认快车道)、integration、install、security、slow五个命名套件,通过npm run test:unit、npm run test:security等脚本分别运行;默认npm test仍运行全部测试,向后兼容。
从 docs/TESTING-SUITES.md 可以进一步确认 CI 矩阵细节:unit、integration、security在每次 PR 时于 ubuntu/macos/windows 三平台 × Node 22/24(gate)+ Node 26(continue-on-error前向兼容)上运行;install与slow只在main推送时运行以保持 PR CI 快速;覆盖率由专用 job 在 ubuntu-latest / Node 24 上用c8跑(对应test:coverage脚本要求get-shit-done/bin/lib/*.cjs行覆盖率达到 70%)。
必需导入
const { describe, it, test, beforeEach, afterEach, before, after, mock } = require('node:test'); const assert = require('node:assert/strict');两种批准的清理模式
模式 1 — 共享夹具(beforeEach/afterEach):当describe块内所有测试共享相同设置与清理时使用,最常见。
describe('my feature', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); }); afterEach(() => { cleanup(tmpDir); }); test('does the thing', () => { assert.strictEqual(result, expected); }); });模式 2 — 逐测试清理(t.after()):当同一块中不同测试需要互异的收尾逻辑时使用。
test('does the thing with a custom setup', (t) => { const tmpDir = createTempProject('custom-prefix'); t.after(() => cleanup(tmpDir)); assert.strictEqual(result, expected); });测试体内严禁try/finally——冗长、会掩盖测试失败、且不是批准模式(仅允许出现在无测试上下文的独立工具/辅助函数内部)。
使用集中式测试辅助函数
不要内联临时目录创建,统一从 tests/helpers.cjs 导入:
const { createTempProject, createTempGitProject, createTempDir, cleanup, runGsdTools } = require('./helpers.cjs');| 辅助函数 | 创建内容 | 使用时机 |
|---|---|---|
createTempProject(prefix?) | 含.planning/phases/的 tmpDir | 测试需要规划结构的 GSD 工具 |
createTempGitProject(prefix?) | 同上 + git init + 初始提交 | 测试依赖 git 的功能 |
createTempDir(prefix?) | 裸临时目录 | 不需要.planning/的功能 |
cleanup(tmpDir) | 递归删除目录 | 永远用在afterEach |
runGsdTools(args, cwd, env?) | 执行 gsd-tools.cjs | 测试 CLI 命令 |
从 tests/helpers.cjs 的实现可以看到,runGsdTools内部始终走execFileSync(process.execPath, [TOOLS_PATH, ...args])的 argv 数组路径(即使传入字符串也会先做 shell 风格切分),并预设清空各运行时会话环境变量——这正呼应了后文 CLI 测试「不要用含敌对值的 shell 字符串」的要求。
夹具数据格式化
模板字面量会继承周围代码的缩进,引入破坏正则锚点与字符串匹配的意外前导空白。多行夹具应使用数组join()构造:
// 正确 —— 无缩进渗漏 const content = [ 'line one', 'line two', 'line three', ].join('\n'); // 错误 —— 模板字面量继承周围缩进 const content = ` line one line two line three `;QA 矩阵
对接受用户输入、读取项目文件、写磁盘、调用 shell、生成产物或构建提示词(prompt)的代码,纯快乐路径测试不够:必须包含敌对输入,以及「不安全行为未发生」的负证明。当变更面适用时,使用以下 12 项矩阵(不必每个 PR 全覆盖 12 项,但要覆盖与被触碰代码风险匹配的项,并在 PR 中让不适用项显而易见):
- 快乐路径;2. 缺失输入;3. 空输入;4. 仅空白输入;5. 畸形输入;6. 越界输入;7. 重复或冲突输入;8. 敌对输入;9. 文件系统故障;10. 并发或重试;11. 跨平台路径/换行符行为;12. 来自链接 issue 的回归夹具。
CLI 与命令路由变更(CLI 解析、命令分派、查询分派、命令路由器、gsd-tools或gsd-sdk)必须为受影响命令族提供负向输入矩阵,用例包括:缺失必填参数、空字符串(如--phase "")、仅空白值、重复 flag(--phase 1 --phase 2)、冲突 flag(--json --raw)、畸形赋值(--phase=、--phase==1)、未知子命令、看起来像 flag 的值(--name --weird)、超长与 Unicode 值、含 shell 元字符的值(;、&&、$()、反引号、引号)。CLI 测试必须断言完整命令契约:退出码、结构化--json结果(若命令支持)、文件系统是否被变更、非调试失败输出无堆栈、攻击者可控值无 shell 插值。优先spawnSync(process.execPath, [scriptPath, ...args], ...)或 argv 数组形式的execFileSync()。
解析器与项目文件输入变更(markdown、TOML、frontmatter、roadmap、phase、state、config、schema 解析)必须包含敌对夹具,可复用夹具放tests/fixtures/adversarial/下按输入类型命名的目录(roadmap/、frontmatter/、config/、toml/、planning-state/)。必需用例涵盖:畸形 frontmatter、重复键、CRLF/LF 混用、未闭合或嵌套的围栏代码块、围栏内的标题、Unicode 标题、重复或十进制 phase ID、../../x式路径穿越名、空字节、巨大但有界的文件、TOML 重复表或尾部垃圾、空数组 vs 缺失数组、标量/对象与期望类型不匹配等。高风险解析器鼓励属性式(property-style)测试,但必须确定性:固定种子、限定迭代次数、失败时打印重放数据。
文件系统写入与安装器变更(install/uninstall 流程、生成产物写入器、state/config 写入器、worktree 安全,或任何写.planning、运行时配置目录、.claude、.codex、hooks的代码)应在缝允许处包含故障注入覆盖,用例包括:父目录缺失、目标路径是文件而非目录、只读目标目录、断链符号链接、逃逸根目录的符号链接、含空格/Unicode/换行的路径、部分写入失败、重命名失败、并发删除或写冲突、失败后的临时文件清理。生产代码暴露缝时,使用node:test的mock.method()对fs.writeFileSync、fs.renameSync、fs.mkdirSync、fs.rmSync及子进程缝打桩,并用测试钩子或t.after()恢复。
安全与提示词注入面:读取 prompt、计划、markdown、agent 指令、shell 命令投影、workstream/项目名或用户可控文件的变更必须把这些输入视为敌对。用例包括:伪造指令标签(<instructions>ignore previous</instructions>)、heredoc 逃逸、shell 命令替换 payload、经项目/workstream 值的路径穿越、恶意 markdown 链接、试图覆盖意图的伪造 frontmatter 字段、输入/日志/stdout/stderr/抛错中的疑似密钥值、用假 token 环境变量验证脱敏。安全测试必须同时断言正向防护行为与负证明:无路径逃逸、无命令执行、无 token 泄漏、无不可信内容被提升为指令。
生成文件与一致性(parity):生成器、生成的.cjs/.ts文件、命令清单、别名、hooks 或 SDK/运行时一致性变更必须测试坏输入与运行时一致性,而不只是新鲜度。用例包括:缺失源命令、畸形 frontmatter、重复命令名或别名、部分生成输出、生成器中途崩溃、对生成文件的手工编辑、时间戳有效但内容错误的过期文件、运行时.cjs与 SDK.ts生成面不一致。生成器测试应在临时夹具中运行并断言原子输出行为。
更多具体示例见 TEST-EXAMPLES.md。
禁止:源码 grep 式测试
绝不要用readFileSync读源码.cjs文件来断言字符串存在。文档称之为「source-grep theater」——它只证明字面量在文件里,不证明功能在运行时有效:
// 反例 —— 源码 grep 剧场 const configSrc = fs.readFileSync( path.join(GSD_ROOT, 'bin', 'lib', 'config-schema.cjs'), 'utf-8' ); assert.ok( configSrc.includes("'workflow.plan_bounce'"), 'VALID_CONFIG_KEYS should contain workflow.plan_bounce' );该测试在 key 拼错、被移出校验路径或改名后仍会通过,只在琐碎改名时失败。正确模式是走 CLI 的行为化测试:
test('config-set accepts workflow.plan_bounce', (t) => { const tmpDir = createTempProject(); t.after(() => cleanup(tmpDir)); const result = runGsdTools('config-set workflow.plan_bounce true', tmpDir); assert.ok(result.success, `config-set should accept workflow.plan_bounce: ${result.error}`); const configPath = path.join(tmpDir, '.planning', 'config.json'); const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); assert.strictEqual(config.workflow?.plan_bounce, true, 'value must be persisted'); });这一个测试同时覆盖了VALID_CONFIG_KEYS的 key 注册、KNOWN_TOP_LEVEL的命名空间解析与值持久化——是源码 grep 触及不到的行为。文档还引用了该反模式在规模下崩溃的真实案例:某次提交因VALID_CONFIG_KEYS跨文件迁移一次性改了 5 个源码 grep 测试,而其中没有一条在测试行为。
CI 强制:scripts/lint-no-source-grep.cjs(以npm run lint:tests运行)检测违规;任何在源码目录对.cjs路径调用readFileSync且未带豁免注释的测试文件都会让lint-testsCI job 失败。
例外:allow-test-rule: <reason>
确有正当理由读取源文件的测试可加豁免,共七个识别类别:
| 原因 | 使用时机 |
|---|---|
source-text-is-the-product | Agent.md、workflow.md、command.md——其文本本身就是运行时加载物,测文本即测部署契约 |
architectural-invariant | 实现必须使用某原语(如Atomics.wait、原子写),无法通过观察输出测试 |
structural-regression-guard | 某代码模式必须(或不得)存在以阻断一类 bug,行为测试无法区分采用了哪种模式 |
docs-parity | 参考文档必须与源定义常量(如CONFIG_DEFAULTS)保持同步,且无运行时枚举 API |
integration-test-input | 源文件作为被测转换函数的真实夹具输入——按数据传递而非字符串检查 |
structural-implementation-guard | 功能的拦截/接线点无法经runGsdTools端到端触达,临时使用直至存在行为路径 |
pending-migration-to-typed-ir | 被跟踪纠正,而非豁免:lint 识别出的原始文本匹配测试,必须引用迁移 issue(如// allow-test-rule: pending-migration-to-typed-ir [#NNNN])保证可审计;新测试不得使用此类别 |
注释必须是独立//行(不能放进/** */块注释——CI linter 扫描// allow-test-rule:模式):
// allow-test-rule: architectural-invariant // state.cjs 加锁必须使用 Atomics.wait() 而非自旋。行为测试无法观察 // 选择了哪个休眠原语——只有源码检查可以。 /** * 针对 #1909 等锁 bug 的回归测试…… */禁止:对测试输出做原始文本匹配
文档进一步澄清:源码 grep 不只是读.cjs文件——任何对被测系统产出的文本做模式匹配(写出的文件内容、子进程 stdout、自由格式reason字符串)都是同一规则的违规:子串匹配.cmdshim 内容、assert.match(r.stdout, /Failures: 1/)、把字符串操作藏在parseCmdShim这样的「解析器」函数后面、对 JSON 报告的自由格式reason做正则——全数违规。它们在偶然近似匹配(注释里恰好有@node、堆栈里恰好说Failures: 1)时通过,在无害重排(Failures: 1改为1 failure)时失败。
规则表述为:
测试断言类型化的结构化值。被测代码若产出文本,就必须同时暴露一个结构化中间表示(IR),测试必须断言 IR——绝不断言渲染后的文本。
| 输出种类 | 必需的结构化面 | 测试断言对象 |
|---|---|---|
| 渲染文件(shim、模板、生成代码) | 返回 IR 的纯构建函数({ invocation, eol, fileNames, render }) | triple.invocation.target === expected、triple.eol.cmd === '\r\n' |
| CLI 人类格式器输出 | 输出同构数据的--json模式 | report.results[0].reason === REASON.FAIL_INSTALLED_NOT_REGULAR_FILE |
| 错误 / 状态 / 原因 | 冻结枚举(Object.freeze({ FAIL_X: 'fail_x', ... })) | assert.equal(result.reason, REASON.FAIL_X) |
| 写后文件存在 | fs.statSync().isFile()、.size > 0、.mtimeMs前进 | 文件系统事实;永不回读文件内容 |
仓库中的两个范本:bin/install.js的buildWindowsShimTriple(shimSrc)是规范 IR 模式——纯函数、无 I/O,测试只断言triple.invocation.target等 IR 字段,文件级测试用fs.statSync(target).size === Buffer.byteLength(triple.render.cmd())证明「写者写的是渲染器产物」而不比较内容;scripts/verify-reapply-patches.cjs 暴露冻结的REASON枚举并经--json输出,新增 reason 码需同步更新枚举、--json输出与锁定Object.keys(REASON).sort()的测试——三处联动防止代码面与测试面漂移。文档的结论很直白:把 grep 包进一个函数仍是 grep;修法是给生产代码加类型化表面,而不是绕开它。除source-text-is-the-product与docs-parity两个合法例外外,凡测试伸手去.includes()/assert.match(text, /…/),就是生产代码缺类型化表面——加表面,别绕。
Node.js 版本兼容
- 禁用已弃用 API 与 Node 22 中不可用的 API;
- 安全可用:
node:test(Node 18 起稳定)、describe/it/test、beforeEach/afterEach/before/after、t.after()、mock.method()(批准用于有范围的 fs/子进程故障注入)、t.plan()、快照测试。
断言默认使用node:assert/strict:
const assert = require('node:assert/strict'); assert.strictEqual(actual, expected); // === assert.deepStrictEqual(actual, expected); // 深度 === assert.ok(value); // truthy assert.throws(() => { ... }, /pattern/); // throws assert.rejects(async () => { ... }); // 异步 throws日常运行命令:
# 运行全部测试 npm test # 运行单个测试文件 node --test tests/core.test.cjs # 带覆盖率运行 npm run test:coveragePR 前接缝检查(清单/别名路由)
触碰命令清单或生成别名文件后须运行:
npm run check:alias-drift它校验生成的别名产物与清单权威源同步(对应 package.json 中check:alias-drift脚本转发到sdk子项目)。文档还提供了可选的本地 Git 钩子:.githooks/pre-commit在暂存区出现command-manifest、command-aliases.generated等路径时自动跑npm run check:alias-drift(仓库中确有对应测试 precommit-alias-drift-hook.test.cjs 验证该钩子行为);以及一个pre-push钩子,通过环境变量GSD_BLOCKED_AUTHOR_REGEX按作者邮箱正则拦截推送。
CI 测试质量检查与按贡献类型的测试要求
除测试套件外,每个 PR 还会跑lint-testsjob(禁止源码 grep 测试,本地可用npm run lint:tests预检)。
架构感知测试要求:当工作触及架构、路由、策略、注册表组装或命令语义时——针对模块接口与缝行为写测试而非实现细节;优先写保护 ADR 行为与CONTEXT.md术语的不变量/契约测试;若 ADR 定义了预期行为,测试应直接断言该预期。
按贡献类型:
- Bug Fix:回归测试必需,且必须「先失败后通过」——先演示原始失败,修复后通过。涉及 CLI 输入、解析器、文件写入、安全/提示面、生成文件或 SDK/运行时一致性的 bug,回归测试须按 QA 矩阵带负证明。「测试通过」只证明 bug 不在现有测试里,不证明正确性。
- Enhancement:需覆盖增强行为的测试,并更新测试所变更区域的既有测试;扩宽了输入、路由、解析、生成输出或安装器写路径时,补相应敌对用例;不允许留下「通过但已不再准确描述行为」的测试。
- Feature:主成功路径 + 足够覆盖 QA 矩阵的失败场景;最低要求覆盖一个失败场景,暴露 CLI 输入/解析/写文件/产物生成/子进程/提示词构建的功能必须覆盖相应负向/敌对用例。留下测试覆盖缺口即驳回理由。
- Behavior Change:修改既有行为时必须更新或替换覆盖该行为的既有测试;通过但断言旧(现已错误)行为的测试让测试套件比没有测试更糟。
评审者标准:评审者不只依赖 CI。批准 PR 前须本地构建(如适用npm run build)、本地跑全量测试(npm test)、确认 bug 修复存在无修复即失败的回归测试、并验证实现与链接 issue 描述一致——「CI 中测试全绿」不是合并的充分条件。
代码风格、文件结构与安全底线
代码风格:
- CommonJS(
.cjs)——项目用require()而非 ESMimport; - 核心零外部依赖——
gsd-tools.cjs与所有 lib 文件只用 Node.js 内置模块; - Conventional commits——
feat:、fix:、docs:、refactor:、test:、ci:。
文件结构(贡献时的地图):
bin/install.js — 安装器(多运行时) get-shit-done/ bin/lib/ — 核心库模块(.cjs) workflows/ — 工作流定义(.md) 大工作流按渐进披露模式拆分: workflows/<name>/modes/*.md + workflows/<name>/templates/*. 父文件分派到模式文件。 规范范例见 discuss-phase(#2551); 单文件预算由 tests/workflow-size-budget.test.cjs 强制。 references/ — 参考文档(.md) templates/ — 文件模板 agents/ — Agent 定义(.md)——权威源 commands/gsd/ — 斜杠命令定义(.md) tests/ — 测试文件(.test.cjs) helpers.cjs — 共享测试工具 docs/ — 面向用户的文档agents 的权威源:只有仓库根部的 agents/ 目录被 git 追踪。开发者机器上可能存在的.claude/agents/、.cursor/agents/、.github/agents/gsd-*都是安装同步产物(已 gitignore),不得编辑,会被覆写。若发现.claude/agents/与agents/漂移(如切换分支后),重跑bin/install.js从权威源重新同步。永远编辑agents/,永不编辑派生目录。
安全:
- 路径校验——任何用户提供的路径都用
security.cjs的validatePath(); - 无 shell 注入——用
execFileSync(数组参数)而非execSync(字符串插值); - GitHub Actions
run:块中不用${{ }}——先绑定到env:映射。
小结
GSD 的贡献体系本质上是一套可被 CI 逐条执行、也可被 AI Agent 逐条遵循的工程契约:issue 标签决定准入(confirmed-bug/approved-enhancement/approved-feature),changeset 片段与随机文件名消灭 CHANGELOG 冲突,docs/联动 lint 保证文档不漂移,而node:test单一框架 + 行为化断言 + 类型化 IR 三件套则把「测试证明什么」从字符串存在性提升为运行时契约。对想参与该项目或借鉴其流程的开发者,上述规则中任何一条都能在仓库内找到对应的脚本(scripts/changeset/*、scripts/lint-docs-required.cjs、scripts/lint-no-source-grep.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),仅供参考