dsh-superpower 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +334 -0
  3. package/cordis.patch.yml +3 -0
  4. package/lib/superpowers.d.ts +44 -0
  5. package/lib/superpowers.d.ts.map +1 -0
  6. package/lib/superpowers.js +291 -0
  7. package/lib/superpowers.js.map +1 -0
  8. package/package.json +62 -0
  9. package/skills/brainstorming/SKILL.md +207 -0
  10. package/skills/brainstorming/scripts/frame-template.html +213 -0
  11. package/skills/brainstorming/scripts/helper.js +167 -0
  12. package/skills/brainstorming/scripts/server.cjs +723 -0
  13. package/skills/brainstorming/scripts/start-server.sh +209 -0
  14. package/skills/brainstorming/scripts/stop-server.sh +120 -0
  15. package/skills/brainstorming/spec-document-reviewer-prompt.md +47 -0
  16. package/skills/brainstorming/visual-companion.md +293 -0
  17. package/skills/dispatching-parallel-agents/SKILL.md +167 -0
  18. package/skills/executing-plans/SKILL.md +64 -0
  19. package/skills/finishing-a-development-branch/SKILL.md +202 -0
  20. package/skills/receiving-code-review/SKILL.md +205 -0
  21. package/skills/requesting-code-review/SKILL.md +95 -0
  22. package/skills/requesting-code-review/code-reviewer.md +169 -0
  23. package/skills/subagent-driven-development/SKILL.md +347 -0
  24. package/skills/subagent-driven-development/implementer-prompt.md +133 -0
  25. package/skills/subagent-driven-development/re-review-prompt.md +84 -0
  26. package/skills/subagent-driven-development/scripts/review-package +46 -0
  27. package/skills/subagent-driven-development/scripts/sdd-workspace +40 -0
  28. package/skills/subagent-driven-development/scripts/task-brief +41 -0
  29. package/skills/subagent-driven-development/task-reviewer-prompt.md +129 -0
  30. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  31. package/skills/systematic-debugging/SKILL.md +283 -0
  32. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  33. package/skills/systematic-debugging/condition-based-waiting.md +116 -0
  34. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  35. package/skills/systematic-debugging/find-polluter.sh +72 -0
  36. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  37. package/skills/systematic-debugging/test-academic.md +14 -0
  38. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  39. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  40. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  41. package/skills/test-driven-development/SKILL.md +322 -0
  42. package/skills/test-driven-development/writing-good-tests.md +145 -0
  43. package/skills/using-git-worktrees/SKILL.md +167 -0
  44. package/skills/using-superpowers/SKILL.md +64 -0
  45. package/skills/using-superpowers/references/antigravity-tools.md +23 -0
  46. package/skills/using-superpowers/references/codex-tools.md +108 -0
  47. package/skills/using-superpowers/references/dsh-tools.md +47 -0
  48. package/skills/using-superpowers/references/gemini-tools.md +63 -0
  49. package/skills/using-superpowers/references/hermes-tools.md +56 -0
  50. package/skills/using-superpowers/references/pi-tools.md +16 -0
  51. package/skills/verification-before-completion/SKILL.md +120 -0
  52. package/skills/writing-plans/SKILL.md +160 -0
  53. package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  54. package/skills/writing-skills/SKILL.md +679 -0
  55. package/skills/writing-skills/anthropic-best-practices.md +1146 -0
  56. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +188 -0
  57. package/skills/writing-skills/graphviz-conventions.dot +172 -0
  58. package/skills/writing-skills/persuasion-principles.md +187 -0
  59. package/skills/writing-skills/render-graphs.js +169 -0
  60. package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
@@ -0,0 +1,322 @@
1
+ ---
2
+ name: test-driven-development
3
+ description: "实现新功能或修复缺陷时,编写实现代码前先写测试的测试驱动开发完整指南与流程约束规范"
4
+ ---
5
+
6
+ # 测试驱动开发(TDD)
7
+
8
+ ## 概述
9
+
10
+ 先写测试。观察它失败。再写最少的代码让它通过。
11
+
12
+ **核心原则:** 如果你没有亲眼看到测试失败,你就无法确定它是否真的测对了东西。
13
+
14
+ **违反规则的字面要求,就是违背规则的精神。**
15
+
16
+ ## 何时使用
17
+
18
+ **始终使用:**
19
+ - 新功能
20
+ - 缺陷修复
21
+ - 重构
22
+ - 行为变更
23
+
24
+ **例外情况(需征得协作人同意):**
25
+ - 一次性原型
26
+ - 生成的代码
27
+ - 配置文件
28
+
29
+ 想着“就这一次跳过 TDD”?打住,这就是在自我合理化。
30
+
31
+ ## 铁律
32
+
33
+ ```
34
+ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
35
+ ```
36
+
37
+ 没有先写出一个失败的测试,就不写任何生产代码。
38
+
39
+ 在测试之前写了代码?删掉,重来。
40
+
41
+ **没有例外:**
42
+ - 不要留着当“参考”
43
+ - 不要在写测试时“改造”它
44
+ - 不要去看它
45
+ - 删除就是删除
46
+
47
+ 完全基于测试重新实现,没有例外。
48
+
49
+ ## 红-绿-重构
50
+
51
+ ```dot
52
+ digraph tdd_cycle {
53
+ rankdir=LR;
54
+ red [label="红色阶段\n编写失败的测试", shape=box, style=filled, fillcolor="#ffcccc"];
55
+ verify_red [label="验证是否\n按预期失败", shape=diamond];
56
+ green [label="绿色阶段\n最小化实现", shape=box, style=filled, fillcolor="#ccffcc"];
57
+ verify_green [label="验证是否通过\n全部通过", shape=diamond];
58
+ refactor [label="重构阶段\n清理优化", shape=box, style=filled, fillcolor="#ccccff"];
59
+ next [label="下一项", shape=ellipse];
60
+
61
+ red -> verify_red;
62
+ verify_red -> green [label="是"];
63
+ verify_red -> red [label="失败原因\n错误"];
64
+ green -> verify_green;
65
+ verify_green -> refactor [label="是"];
66
+ verify_green -> green [label="否"];
67
+ refactor -> verify_green [label="保持\n通过"];
68
+ verify_green -> next;
69
+ next -> red;
70
+ }
71
+ ```
72
+
73
+ ### 红色阶段 - 编写失败的测试
74
+
75
+ 写一个最小化的测试,展示应该发生什么。
76
+
77
+ <Good>
78
+ ```typescript
79
+ test('retries failed operations 3 times', async () => {
80
+ let attempts = 0;
81
+ const operation = () => {
82
+ attempts++;
83
+ if (attempts < 3) throw new Error('fail');
84
+ return 'success';
85
+ };
86
+
87
+ const result = await retryOperation(operation);
88
+
89
+ expect(result).toBe('success');
90
+ expect(attempts).toBe(3);
91
+ });
92
+ ```
93
+ 命名清晰、测试真实行为、只测一件事
94
+ </Good>
95
+
96
+ <Bad>
97
+ ```typescript
98
+ test('retry works', async () => {
99
+ const mock = jest.fn()
100
+ .mockRejectedValueOnce(new Error())
101
+ .mockRejectedValueOnce(new Error())
102
+ .mockResolvedValueOnce('success');
103
+ await retryOperation(mock);
104
+ expect(mock).toHaveBeenCalledTimes(3);
105
+ });
106
+ ```
107
+ 命名模糊、测的是 mock 而非真实代码
108
+ </Bad>
109
+
110
+ **要求:**
111
+ - 一次只测一个行为
112
+ - 命名清晰
113
+ - 使用真实代码(除非不得不 mock)
114
+
115
+ ### 验证红色阶段 - 观察失败
116
+
117
+ **强制要求,切勿跳过。**
118
+
119
+ ```bash
120
+ npm test path/to/test.test.ts
121
+ ```
122
+
123
+ 确认:
124
+ - 测试失败(而非报错)
125
+ - 失败信息符合预期
126
+ - 失败原因是功能缺失导致(而非拼写错误)
127
+
128
+ **测试通过了?** 说明你在测试已有的行为,请修正测试。
129
+
130
+ **测试报错了?** 修复错误,直到它以正确的方式失败为止。
131
+
132
+ ### 绿色阶段 - 最小化实现
133
+
134
+ 编写刚好能让测试通过的最简代码。
135
+
136
+ <Good>
137
+ ```typescript
138
+ async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
139
+ for (let i = 0; i < 3; i++) {
140
+ try {
141
+ return await fn();
142
+ } catch (e) {
143
+ if (i === 2) throw e;
144
+ }
145
+ }
146
+ throw new Error('unreachable');
147
+ }
148
+ ```
149
+ 刚好够通过
150
+ </Good>
151
+
152
+ <Bad>
153
+ ```typescript
154
+ async function retryOperation<T>(
155
+ fn: () => Promise<T>,
156
+ options?: {
157
+ maxRetries?: number;
158
+ backoff?: 'linear' | 'exponential';
159
+ onRetry?: (attempt: number) => void;
160
+ }
161
+ ): Promise<T> {
162
+ // YAGNI
163
+ }
164
+ ```
165
+ 过度设计
166
+ </Bad>
167
+
168
+ 不要添加测试未要求的功能,不要顺手重构其他代码,也不要做超出测试范围的“优化”。
169
+
170
+ ### 验证绿色阶段 - 观察通过
171
+
172
+ **强制要求。**
173
+
174
+ ```bash
175
+ npm test path/to/test.test.ts
176
+ ```
177
+
178
+ 确认:
179
+ - 测试通过
180
+ - 其他测试仍全部通过
181
+ - 输出干净(无错误、无警告)
182
+
183
+ **测试失败?** 修代码,而不是改测试。
184
+
185
+ **其他测试失败?** 立即修复。
186
+
187
+ ### 重构阶段 - 清理优化
188
+
189
+ 仅在变绿之后:
190
+ - 消除重复
191
+ - 改进命名
192
+ - 抽取辅助函数
193
+
194
+ 保持测试持续通过,不要新增行为。
195
+
196
+ ### 重复
197
+
198
+ 为下一个功能编写下一个失败的测试。
199
+
200
+ ## 好的测试
201
+
202
+ | 质量 | 好的示例 | 差的示例 |
203
+ |---------|------|-----|
204
+ | **最小化** | 只测一件事,名称里出现“和”就拆分 | `test('validates email and domain and whitespace')` |
205
+ | **清晰** | 名称描述行为 | `test('test1')` |
206
+ | **体现意图** | 展示期望的 API 用法 | 掩盖代码应有的行为 |
207
+
208
+ 在编写或修改任何测试时,请阅读 [writing-good-tests.md](writing-good-tests.md) 中让测试保持诚实的规则:
209
+ - 在编写测试前,先说清楚什么样的生产代码变更会让它失败
210
+ - 只对真实行为做断言,绝不对 mock 的行为做断言
211
+ - 仅用于测试的代码放在测试工具中,不要放进生产类
212
+ - 只有在理解依赖的副作用后,才去 mock 它
213
+
214
+ ## 常见的自我合理化
215
+
216
+ | 借口 | 实际情况 |
217
+ |--------|---------|
218
+ | "太简单了,不用测" | 简单的代码也会出错,写个测试只要 30 秒。 |
219
+ | "先实现,之后再补测试" | 后补的测试会立刻通过——这什么也证明不了。它们可能测错了东西、测的是实现而非行为,或遗漏了你忘记的边界情况。你从未看过它失败,也就从未证明它能捕获缺陷。测试先行才会迫使失败发生。 |
220
+ | "后补测试也能达到同样的目标(重精神不重形式)" | 后补测试回答的是“这段代码做了什么?”;先行测试回答的是“这段代码应该做什么?”。后补的测试受你已写代码的影响——你只会验证自己记得的场景,而非本应发现的场景。没有经过失败验证的覆盖率毫无说服力。 |
221
+ | "已经手动测过了" | 手动测试是随意的:没有覆盖记录、代码变更后无法重复执行、压力下容易遗漏场景。“我试的时候是好的”不等于全面。自动化测试每次都以同样的方式运行。 |
222
+ | "删掉花了 X 小时的代码太浪费了" | 沉没成本谬误——时间已经花掉了,无法挽回。真正的选择是:用 TDD 重写(高可信度) vs. 保留现有代码再后补测试(低可信度、很可能有缺陷)。保留不可信的代码才是浪费。 |
223
+ | "留着当参考,先写测试" | 你会忍不住去适配它,这本质还是后补测试。删除就是删除。 |
224
+ | "需要先探索一下" | 可以,先探索,探索完扔掉,再从 TDD 开始。 |
225
+ | "测试很难写 = 设计不清晰" | 听测试的,难测就意味着难用。 |
226
+ | "TDD 会拖慢我" | TDD 才是务实的路径:在提交前捕获缺陷、防止回归、让你无惧重构。所谓“务实”的捷径只会把调试推到线上——更慢,而非更快。 |
227
+ | "手动测试更快" | 手动测试无法证明边界情况,每次变更你都要重新测一遍。 |
228
+ | "现有代码本来就没测试" | 你正在改进它,给现有代码补上测试。 |
229
+
230
+ ## 危险信号 - 立即停下并重来
231
+
232
+ - 先写代码,后写测试
233
+ - 在实现之后才补测试
234
+ - 测试立刻通过
235
+ - 无法解释测试为何失败
236
+ - 测试“稍后”再补
237
+ - 为“就这一次”找合理化理由
238
+ - “我已经手动测过了”
239
+ - “后补测试也能达到同样目的”
240
+ - “重在精神,不在形式”
241
+ - “留着当参考”或“在现有代码上改一改”
242
+ - “已经花了 X 小时,删掉太浪费”
243
+ - “TDD 太教条,我这是务实”
244
+ - “这次情况特殊,因为……”
245
+
246
+ **出现以上任何一种,意味着:删掉代码,用 TDD 重来。**
247
+
248
+ ## 示例:缺陷修复
249
+
250
+ **缺陷:** 空邮箱被接受
251
+
252
+ **红色阶段**
253
+ ```typescript
254
+ test('rejects empty email', async () => {
255
+ const result = await submitForm({ email: '' });
256
+ expect(result.error).toBe('Email required');
257
+ });
258
+ ```
259
+
260
+ **验证红色阶段**
261
+ ```bash
262
+ $ npm test
263
+ FAIL: expected 'Email required', got undefined
264
+ ```
265
+
266
+ **绿色阶段**
267
+ ```typescript
268
+ function submitForm(data: FormData) {
269
+ if (!data.email?.trim()) {
270
+ return { error: 'Email required' };
271
+ }
272
+ // ...
273
+ }
274
+ ```
275
+
276
+ **验证绿色阶段**
277
+ ```bash
278
+ $ npm test
279
+ PASS
280
+ ```
281
+
282
+ **重构阶段**
283
+ 如有需要,为多个字段抽取统一的校验逻辑。
284
+
285
+ ## 验证清单
286
+
287
+ 标记完成前请逐项检查:
288
+
289
+ - [ ] 每个新增的函数/方法都有对应的测试
290
+ - [ ] 已亲眼看到每个测试在实现前失败
291
+ - [ ] 每个测试都以预期的原因失败(功能缺失,而非拼写错误)
292
+ - [ ] 仅编写了刚好让测试通过的最简代码
293
+ - [ ] 所有测试均已通过
294
+ - [ ] 输出干净(无错误、无警告)
295
+ - [ ] 测试使用真实代码(仅在不得已时使用 mock)
296
+ - [ ] 已覆盖边界情况和错误路径
297
+
298
+ 无法全部勾选?你跳过了 TDD,请重来。
299
+
300
+ ## 遇到阻塞时
301
+
302
+ | 问题 | 解决方案 |
303
+ |---------|----------|
304
+ | 不知道怎么测 | 写出你期望的 API,先写断言,找协作人讨论。 |
305
+ | 测试太复杂 | 设计太复杂,简化接口。 |
306
+ | 必须 mock 所有东西 | 代码耦合过重,使用依赖注入。 |
307
+ | 测试准备工作过于庞大 | 抽取辅助函数,依然复杂?简化设计。 |
308
+
309
+ ## 调试集成
310
+
311
+ 发现缺陷?先写一个能复现它的失败测试,再走 TDD 循环。测试既能证明修复有效,也能防止回归。
312
+
313
+ 绝不在没有测试的情况下修复缺陷。
314
+
315
+ ## 最终规则
316
+
317
+ ```
318
+ 生产代码 → 已存在对应测试且该测试曾先失败
319
+ 否则 → 就不是 TDD
320
+ ```
321
+
322
+ 未经协作人许可,没有例外。
@@ -0,0 +1,145 @@
1
+ # 编写高质量测试
2
+
3
+ **何时加载本文档:** 编写或修改测试、添加 Mock,或为测试添加清理/辅助方法时。
4
+
5
+ ## 概述
6
+
7
+ 测试的存在是为了捕获特定的缺陷。以下两条原则贯穿始终:
8
+
9
+ ```
10
+ 1. 每个测试都要明确它能捕获什么缺陷
11
+ 2. 每个测试都要验证真实对象
12
+ ```
13
+
14
+ 严格的 TDD 能自然地满足这两点:先写测试并观察其在真实代码上失败,就已经证明了它具备失败能力;只有当真实依赖被证明缓慢或涉及外部系统时,才引入 Mock。
15
+
16
+ ## 原则一:明确测试要捕获的缺陷
17
+
18
+ 在编写测试体之前,先回答:**什么样的生产代码变更应该让这个测试失败——而这个变更是缺陷还是有意决策?** 只有当测试能够捕获错误分支、缺失的副作用、错误参数、边界情况或被破坏的契约时,它才有存在的价值。
19
+
20
+ **独立推导期望值。** 使用字面量和人工校验的固件;表格驱动测试中 `want` 使用字面量是首选形式。由被测代码(或其辅助函数)计算出的期望值,无论代码做什么都能通过:
21
+
22
+ ```typescript
23
+ // ❌ Mirror assertion: the same builder computes both sides — always true
24
+ const expected = buildSearchQuery({ tag: 'urgent' });
25
+ expect(buildSearchQuery({ tag: 'urgent' })).toBe(expected);
26
+
27
+ // ✅ Hand-derived literal
28
+ expect(buildSearchQuery({ tag: 'urgent' })).toBe('tag:"urgent"');
29
+ ```
30
+
31
+ **不要写变更探测器。** 如果只有有意决策才能让测试失败——如常量的取值、文案措辞、内部结构——那么它只会在重构时触发,却在真正的缺陷面前保持沉默。应该测试依赖于该决策的行为:不要写 `expect(MAX_RETRIES).toBe(5)`,而要验证“失败调用会被重试 5 次,第 6 次绝不会发起”。
32
+
33
+ **测行为,而非文本。** 断言脚本、技能或配置包含某一行,只能证明源码就是源码本身。应在受控输入下运行脚本,并断言其输出、副作用或退出码。面向 Agent 的指令性文档,应通过消费方 Agent 的行为来测试(superpowers:writing-skills);面向人的 prose 则完全不需要测试。
34
+
35
+ **测你的代码,而非框架。** 测试你的代码在其边界上做出的契约——你注册的路由、你发出的查询、你生成的负载。上游的实现机制应由其维护者来测试(典型反例:断言你的路由会调用已注册的处理器——那是框架的测试,不是你的)。当上游行为确实出乎意料时,针对该假设写一个窄范围的特征测试(characterization test)并明确命名假设。同样边界也适用于你的内部代码:构造函数、getter、常量和简单透传,只有在它们承担校验、归一化、默认值、推导、约束或副作用时才值得测试——否则应断言首个依赖于它们的、消费者可见的结果。
36
+
37
+ ### 门禁检查
38
+
39
+ ```
40
+ 编写测试体之前:
41
+ 明确什么样的生产代码变更会让该测试失败。
42
+
43
+ 无法命名 → 围绕可观测行为重新设计
44
+ “源码文本变了” → 运行产物并断言其效果
45
+ 只有有意决策会失败 → 这是变更探测器;改为测试
46
+ 依赖于该决策的行为
47
+
48
+ 确认期望值未使用被测代码推导。
49
+ 如果复用了被测代码的逻辑或辅助函数:
50
+ 替换为字面量或人工校验的固件
51
+ ```
52
+
53
+ ## 原则二:使用真实对象
54
+
55
+ **Mock 本身不值得断言。** 对 Mock 的断言,只在 Mock 存在时通过、不存在时失败——它对组件本身一无所言。应断言真实组件的行为;如果你发现自己在检查 Mock,那就去掉 Mock 或删掉该断言。
56
+
57
+ ```typescript
58
+ // ✅ Real behavior
59
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
60
+
61
+ // ❌ Mock existence
62
+ expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();
63
+ ```
64
+
65
+ **你的搭档的提醒:**“我们在测试 Mock 的行为吗?”
66
+
67
+ **在正确的层级 Mock。** 在替换真实方法前,先了解它的所有副作用;只 Mock 缓慢或外部的操作,保留测试所依赖的部分为真实实现。不确定时,先让测试跑在真实实现上,观察究竟需要发生什么。
68
+
69
+ ```typescript
70
+ // ❌ The mock swallows the config write that duplicate detection reads
71
+ vi.mock('ToolCatalog', () => ({
72
+ discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)
73
+ }));
74
+
75
+ // ✅ Mock only the slow server startup; the config write stays real
76
+ vi.mock('MCPServerManager');
77
+ ```
78
+
79
+ **让替身足够具体。** 当参数、调用次数或顺序属于契约的一部分时,就要断言它们——一个接受任意参数的假对象什么也验证不了。为每个分支(成功、失败、畸形输入)提供独立的固件或 spy,这样错误的分支就无法满足期望。
80
+
81
+ **完整镜像真实数据。** 按真实存在的完整结构来 Mock——包含所有已文档化的字段——而不仅仅是你测试中读取的字段。部分 Mock 会在下游代码读取缺失字段时静默失败:测试通过,而集成已损坏。
82
+
83
+ **生产类只承载生产方法。** 仅测试需要的清理逻辑应放在测试工具中,绝不要以 `destroy()` 的形式出现在生产类上。自问:这个方法是否只被测试调用?这个类是否拥有该资源的生命周期?答案有误 → 放到测试工具里。
84
+
85
+ **复杂 Mock 不如真实组件。** 当 Mock 的搭建超过测试逻辑本身、Mock 缺少真实组件拥有的方法、或测试因 Mock 变更而崩溃时,改用基于真实组件的集成测试。**你的搭档的提问:**“这里真的需要用 Mock 吗?”
86
+
87
+ ### 门禁检查
88
+
89
+ ```
90
+ 添加 Mock 或测试辅助方法之前:
91
+ 列出真实方法的所有副作用;测试所依赖的保持真实——
92
+ 只在更底层的缓慢/外部层级进行 Mock。
93
+
94
+ Mock 的响应需完整镜像真实结构。
95
+
96
+ 仅被测试调用的方法归属于测试工具,而非生产代码。
97
+
98
+ 正要对 Mock 本身做断言?
99
+ 去掉 Mock 或删掉该断言。
100
+ ```
101
+
102
+ ## 测试与实现一同交付
103
+
104
+ TDD 循环——失败的测试、最小实现、重构——才是“完成”的定义。只交付行为所需的测试,且仅交付这些:平凡代码和面向人的 prose 不需要测试,为满足流程而写的测试会带来永久的维护成本。
105
+
106
+ ## 变异检查
107
+
108
+ 完成前,在脑中对生产代码做变异;对每一种现实的变异,至少应有一个测试会失败:
109
+
110
+ - 错误的常量或参数
111
+ - 错误的分支处理
112
+ - 缺失的状态变更或副作用
113
+ - 空返回或默认返回
114
+ - 对 zero、空、nil、未授权或畸形输入缺失校验
115
+
116
+ 没有任何测试能捕获的变异,意味着该行为未受保护——或测试本身是同义反复。
117
+
118
+ ## 速查表
119
+
120
+ | 当你…… | 应该…… |
121
+ |-------------|-----|
122
+ | 写任何测试 | 明确它要捕获的缺陷——是缺陷,而非决策 |
123
+ | 构造期望值 | 手工推导;绝不要用被测代码来计算 |
124
+ | 测试脚本或文档 | 运行它 / 对其消费者做压力测试;不要 grep 文本 |
125
+ | 想测试依赖 | 测试你的边界契约,而非对方已文档化的实现机制 |
126
+ | 想对 Mock 元素做断言 | 测试真实组件,或去掉 Mock |
127
+ | 准备 Mock 方法 | 了解其副作用;在缓慢/外部层级 Mock |
128
+ | 构造 Mock 响应 | 完整镜像真实结构 |
129
+ | 需要仅测试使用的清理逻辑 | 放到测试工具中 |
130
+ | Mock 搭建急剧膨胀 | 改用基于真实组件的集成测试 |
131
+ | 完成测试文件 | 执行变异检查 |
132
+
133
+ ## 警示信号
134
+
135
+ - 准备与断言共享同一对象,必然相等
136
+ - 测试只能通过 panic、崩溃或缺失选择器而失败
137
+ - 测试在每次有意变更时都失败,却从不在意外破坏时失败
138
+ - 期望值隐藏在循环、构造器或辅助函数之后
139
+ - 测试 grep 源码文本,或断言已移除的符号保持移除
140
+ - 即使只剩下框架,该测试依然“有意义”
141
+ - 测试仅为覆盖率而存在,未检查任何副作用或结果
142
+ - 断言检查 `*-mock` 测试 ID,或在移除 Mock 后失败
143
+ - 某个方法只被测试文件调用
144
+ - Mock 搭建超过测试的一半,或你无法解释为何需要 Mock
145
+ - “为了保险”而 Mock
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: using-git-worktrees
3
+ description: "适用于需与当前工作区隔离的功能开发或执行实现计划前,通过原生工具优先、git worktree 兜底的方式确保独立工作区就绪"
4
+ ---
5
+
6
+ # 使用 Git Worktree
7
+
8
+ ## 概述
9
+
10
+ 确保所有工作都在独立工作区中进行。优先使用平台原生的 worktree 工具,仅在无原生工具可用时再回退到手动 git worktree。
11
+
12
+ **核心原则:** 先检测是否已处于隔离环境,再使用原生工具,最后回退到 git。不要与 harness 对抗。
13
+
14
+ **开始时声明:** “我正在使用 using-git-worktrees 技能来创建独立工作区。”
15
+
16
+ ## 步骤 0:检测现有隔离状态
17
+
18
+ **在创建任何内容之前,先检查是否已处于独立工作区中。**
19
+
20
+ ```bash
21
+ GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
22
+ GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
23
+ BRANCH=$(git branch --show-current)
24
+ ```
25
+
26
+ **子模块防护:** `GIT_DIR != GIT_COMMON` 在 git 子模块中同样为真。在判定“已处于 worktree”之前,需先确认是否处于子模块中:
27
+
28
+ ```bash
29
+ # 若返回路径,则处于子模块而非 worktree——按常规仓库处理
30
+ git rev-parse --show-superproject-working-tree 2>/dev/null
31
+ ```
32
+
33
+ **若 `GIT_DIR != GIT_COMMON`(且不在子模块中):** 说明已处于关联 worktree 中。跳至步骤 2(项目初始化),不要再创建新的 worktree。
34
+
35
+ 按分支状态报告:
36
+ - 处于分支上:“已在独立工作区 `<path>`,分支为 `<name>`。”
37
+ - 游离 HEAD:“已在独立工作区 `<path>`(游离 HEAD,外部托管),结束时需创建分支。”
38
+
39
+ **若 `GIT_DIR == GIT_COMMON`(或处于子模块中):** 说明处于常规仓库检出状态。
40
+
41
+ 用户是否已在指令中表明 worktree 偏好?若无,请在创建 worktree 前征得同意:
42
+
43
+ > “是否需要我为你创建一个独立 worktree?它可以保护当前分支不受改动影响。”
44
+
45
+ 若已存在明确偏好则直接遵循,无需询问。若用户拒绝,则在原地工作并跳至步骤 2。
46
+
47
+ ## 步骤 1:创建独立工作区
48
+
49
+ **你有两种机制,请按以下顺序尝试。**
50
+
51
+ ### 1a. 原生 Worktree 工具(优先)
52
+
53
+ 用户已请求独立工作区(步骤 0 已获同意)。你是否已有创建 worktree 的方式?可能是名为 `EnterWorktree`、`WorktreeCreate`、`/worktree` 命令或 `--worktree` 参数的工具。如果有,请直接使用并跳至步骤 2。
54
+
55
+ 原生工具会自动处理目录选址、分支创建和清理。使用 `git worktree add` 而绕过原生工具会产生 harness 无法感知和管理的幽灵状态。
56
+
57
+ 仅在无原生 worktree 工具可用时,才进入步骤 1b。
58
+
59
+ ### 1b. Git Worktree 兜底方案
60
+
61
+ **仅在步骤 1a 不适用时使用**——即你没有可用的原生 worktree 工具时,才手动通过 git 创建 worktree。
62
+
63
+ #### 目录选择
64
+
65
+ 按以下优先级选择目录,用户的显式偏好始终优先于已观测到的文件系统状态。
66
+
67
+ 1. **检查指令中是否已声明 worktree 目录偏好。** 如用户已指定,直接使用,无需询问。
68
+
69
+ 2. **检查是否存在项目本地的 worktree 目录:**
70
+ ```bash
71
+ ls -d .worktrees 2>/dev/null # 优先(隐藏目录)
72
+ ls -d worktrees 2>/dev/null # 备选
73
+ ```
74
+ 如存在则直接使用;若两者都存在,以 `.worktrees` 为准。
75
+
76
+ 3. **若无其他指引**,默认为项目根目录下的 `.worktrees/`。
77
+
78
+ #### 安全性校验(仅针对项目本地目录)
79
+
80
+ **创建 worktree 前必须确认目录已被忽略:**
81
+
82
+ ```bash
83
+ git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null
84
+ ```
85
+
86
+ **若未被忽略:** 加入 .gitignore 并提交该改动后再继续。
87
+
88
+ **为何关键:** 防止将 worktree 内容误提交到仓库。
89
+
90
+ #### 创建 Worktree
91
+
92
+ ```bash
93
+ # 根据所选位置确定路径
94
+ path="$LOCATION/$BRANCH_NAME"
95
+
96
+ git worktree add "$path" -b "$BRANCH_NAME"
97
+ cd "$path"
98
+ ```
99
+
100
+ **沙盒兜底:** 若 `git worktree add` 因权限错误(沙盒拒绝)失败,请告知用户沙盒已阻止 worktree 创建,改为在当前目录继续工作,并在原地完成初始化与基线测试。
101
+
102
+ ## 步骤 2:项目初始化
103
+
104
+ 自动检测并执行对应的初始化:
105
+
106
+ ```bash
107
+ # Node.js
108
+ if [ -f package.json ]; then npm install; fi
109
+
110
+ # Rust
111
+ if [ -f Cargo.toml ]; then cargo build; fi
112
+
113
+ # Python
114
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
115
+ if [ -f pyproject.toml ]; then poetry install; fi
116
+
117
+ # Go
118
+ if [ -f go.mod ]; then go mod download; fi
119
+ ```
120
+
121
+ ## 步骤 3:验证干净基线
122
+
123
+ 运行测试以确保工作区初始状态干净:
124
+
125
+ ```bash
126
+ # 使用项目对应的命令
127
+ npm test / cargo test / pytest / go test ./...
128
+ ```
129
+
130
+ **若测试失败:** 报告失败情况,询问是否继续或先行排查。
131
+
132
+ **若测试通过:** 报告就绪。
133
+
134
+ ### 报告
135
+
136
+ ```
137
+ Worktree ready at <full-path>
138
+ Tests passing (<N> tests, 0 failures)
139
+ Ready to implement <feature-name>
140
+ ```
141
+
142
+ ## 快速参考
143
+
144
+ | 场景 | 操作 |
145
+ |-----------|--------|
146
+ | 已处于关联 worktree | 跳过创建(步骤 0) |
147
+ | 处于子模块中 | 视为常规仓库(步骤 0 防护) |
148
+ | 存在原生 worktree 工具 | 使用原生工具(步骤 1a) |
149
+ | 无原生工具 | 使用 Git worktree 兜底(步骤 1b) |
150
+ | `.worktrees/` 已存在 | 使用它(校验是否已忽略) |
151
+ | `worktrees/` 已存在 | 使用它(校验是否已忽略) |
152
+ | 两者都存在 | 使用 `.worktrees/` |
153
+ | 两者都不存在 | 先检查指令文件,再默认使用 `.worktrees/` |
154
+ | 目录未被忽略 | 加入 .gitignore 并提交 |
155
+ | 创建时权限错误 | 沙盒兜底,原地工作 |
156
+ | 基线测试失败 | 报告失败并询问 |
157
+ | 无 package.json/Cargo.toml | 跳过依赖安装 |
158
+
159
+ ## 常见托词
160
+
161
+ | 托词 | 实际情况 |
162
+ |--------|---------|
163
+ | “我显然不在 worktree 里,没必要检查” | 执行步骤 0。harness 创建的隔离和子模块都会让肉眼判断失误,用检测命令才能确定。 |
164
+ | “`git worktree add` 比到处找原生工具更快” | 原生工具(如 `EnterWorktree`)负责选址、分支和清理。绕过它是头号错误——会产生 harness 无法感知和管理的幽灵状态。 |
165
+ | “worktree 目录肯定已经被忽略了” | 请执行 `git check-ignore`。未被忽略的 worktree 目录会把整个工作树提交进仓库。 |
166
+ | “随便起个目录名都行” | 显式指令优先于已存在的项目本地目录,项目本地目录优先于 `.worktrees/` 默认值。 |
167
+ | “工作区是全新的,基线测试可以等等再跑” | 脏基线会让后续所有失败变得无法定位。现在就跑测试;是否带病继续由你的协作人决定。 |