@namewta/speculo 0.2.2 → 0.2.6

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 (134) hide show
  1. package/README.md +11 -15
  2. package/dist/src/index.js +72 -8
  3. package/dist/src/index.js.map +1 -1
  4. package/dist/src/migrate.js +8 -8
  5. package/dist/src/migrate.js.map +1 -1
  6. package/dist/src/workflows.js +2 -2
  7. package/dist/src/workflows.js.map +1 -1
  8. package/package.json +1 -1
  9. package/template/.speculo/README.md +3 -3
  10. package/template/AGENTS.md +4 -0
  11. package/template/CLAUDE.md +3 -0
  12. package/template/canonical/README.md +114 -0
  13. package/template/canonical/canonical-domain-modeling.md +289 -0
  14. package/template/canonical/canonical-skill-example.md +608 -0
  15. package/template/canonical/canonical-teach.md +296 -0
  16. package/template/commands/archive-and-consolidate.md +49 -0
  17. package/template/commands/docs-sync.md +2 -2
  18. package/template/commands/retro.md +9 -7
  19. package/template/commands/status.md +2 -2
  20. package/template/skills/archive-and-consolidate/SKILL.md +179 -0
  21. package/template/skills/archive-and-consolidate/assets/archive-plan-template.md +34 -0
  22. package/template/skills/archive-and-consolidate/assets/cleanup-candidate-template.md +69 -0
  23. package/template/skills/archive-and-consolidate/assets/consolidation-plan-template.md +67 -0
  24. package/template/skills/archive-and-consolidate/references/archive-rules.md +48 -0
  25. package/template/skills/archive-and-consolidate/references/cleanup-rules.md +73 -0
  26. package/template/skills/archive-and-consolidate/references/consolidation-rules.md +70 -0
  27. package/template/skills/archive-and-consolidate/references/knowledge-graduation.md +50 -0
  28. package/template/skills/docs-sync/SKILL.md +1 -1
  29. package/template/skills/docs-sync/references/readme-contract.md +2 -0
  30. package/template/skills/docs-sync/references/readme-writing-guide.md +294 -0
  31. package/template/skills/docs-sync/references/workflow-scope-contract.md +3 -3
  32. package/template/skills/speculo-retro/SKILL.md +1 -1
  33. package/template/skills/speculo-retro/references/issue-drafting-sop.md +1 -1
  34. package/template/skills/worktree-isolation/references/merge-and-cleanup.md +2 -2
  35. package/template/vendor/README.md +3 -3
  36. package/template/vendor/khazix-skills/neat-freak/SKILL.md +210 -0
  37. package/template/vendor/khazix-skills/neat-freak/references/agent-paths.md +72 -0
  38. package/template/vendor/khazix-skills/neat-freak/references/governance.md +88 -0
  39. package/template/vendor/khazix-skills/neat-freak/references/sync-matrix.md +77 -0
  40. package/template/vendor/khazix-skills/neat-freak/references/verification.md +92 -0
  41. package/template/vendor/khazix-skills/neat-freak/scripts/audit-inventory.sh +106 -0
  42. package/template/workflows/person/INDEX.md +12 -0
  43. package/template/workflows/person/M-mao-zedong-cognitive-os/M-mao-zedong-cognitive-os.md +73 -74
  44. package/template/workflows/specdev/D-diagnose-bugs/D-diagnose-bugs.md +85 -0
  45. package/template/workflows/specdev/D-diagnose-bugs/cleanup-postmortem.md +37 -0
  46. package/template/workflows/specdev/D-diagnose-bugs/feedback-loop-techniques.md +84 -0
  47. package/template/workflows/specdev/D-diagnose-bugs/hypothesis-format.md +46 -0
  48. package/template/workflows/specdev/D-diagnose-bugs/instrumentation-rules.md +51 -0
  49. package/template/workflows/specdev/G-grill-with-docs/G-grill-with-docs.md +54 -0
  50. package/template/workflows/specdev/G-grill-with-docs/adr-format.md +77 -0
  51. package/template/workflows/specdev/G-grill-with-docs/context-format.md +63 -0
  52. package/template/workflows/specdev/G-grill-with-docs/domain-modeling-rules.md +93 -0
  53. package/template/workflows/specdev/G-grill-with-docs/grilling-protocol.md +54 -0
  54. package/template/workflows/specdev/G-grill-with-docs/log-format.md +99 -0
  55. package/template/workflows/specdev/I-implement/I-implement.md +85 -0
  56. package/template/workflows/specdev/I-implement/code-review-process.md +83 -0
  57. package/template/workflows/specdev/I-implement/codebase-design-glossary.md +109 -0
  58. package/template/workflows/specdev/I-implement/deepening.md +37 -0
  59. package/template/workflows/specdev/I-implement/design-it-twice.md +44 -0
  60. package/template/workflows/specdev/I-implement/tdd-examples.md +139 -0
  61. package/template/workflows/specdev/I-implement/tdd-rules.md +31 -0
  62. package/template/workflows/specdev/I-init-setup/I-init-setup.md +132 -0
  63. package/template/workflows/specdev/I-init-setup/domain-layout.md +90 -0
  64. package/template/workflows/specdev/I-init-setup/status-labels.md +54 -0
  65. package/template/workflows/specdev/I-init-setup/tracking-convention.md +58 -0
  66. package/template/workflows/specdev/INDEX.md +81 -0
  67. package/template/workflows/specdev/S-spec/S-spec.md +91 -0
  68. package/template/workflows/specdev/T-tickets/T-tickets.md +241 -0
  69. package/template/workflows/specdev/W-wayfinder/W-wayfinder.md +209 -0
  70. package/template/workflows/specdev/_state/adr/.gitkeep +0 -0
  71. package/template/workflows/specdev/_state/archive/.gitkeep +0 -0
  72. package/template/workflows/specdev/_state/changes/.gitkeep +0 -0
  73. package/template/workflows/specdev/_state/context/.gitkeep +0 -0
  74. package/template/workflows/{matt-pocock → specdev}/_state/status.json +1 -1
  75. package/template/commands/finalize.md +0 -37
  76. package/template/commands/knowledge-prune.md +0 -20
  77. package/template/skills/change-lifecycle/SKILL.md +0 -25
  78. package/template/skills/change-lifecycle/assets/completion-summary-template.md +0 -25
  79. package/template/skills/change-lifecycle/assets/completion-verification-template.md +0 -29
  80. package/template/skills/change-lifecycle/references/completion-gate.md +0 -19
  81. package/template/skills/change-lifecycle/references/finalize-archive.md +0 -32
  82. package/template/skills/knowledge-prune/SKILL.md +0 -29
  83. package/template/skills/knowledge-prune/references/audit-rules.md +0 -24
  84. package/template/skills/runtime-context/SKILL.md +0 -54
  85. package/template/skills/runtime-context/references/path-resolution.md +0 -41
  86. package/template/workflows/matt-pocock/PERSISTENCE.md +0 -80
  87. package/template/workflows/matt-pocock/WORKFLOW.md +0 -103
  88. package/template/workflows/matt-pocock/_state/archive/.gitkeep +0 -1
  89. package/template/workflows/matt-pocock/_state/changes/.gitkeep +0 -1
  90. package/template/workflows/matt-pocock/atomic-skills/ask-matt.md +0 -21
  91. package/template/workflows/matt-pocock/atomic-skills/claude-handoff.md +0 -21
  92. package/template/workflows/matt-pocock/atomic-skills/code-review.md +0 -21
  93. package/template/workflows/matt-pocock/atomic-skills/codebase-design.md +0 -21
  94. package/template/workflows/matt-pocock/atomic-skills/diagnosing-bugs.md +0 -21
  95. package/template/workflows/matt-pocock/atomic-skills/domain-modeling.md +0 -21
  96. package/template/workflows/matt-pocock/atomic-skills/grill-me.md +0 -21
  97. package/template/workflows/matt-pocock/atomic-skills/grill-with-docs.md +0 -21
  98. package/template/workflows/matt-pocock/atomic-skills/grilling.md +0 -21
  99. package/template/workflows/matt-pocock/atomic-skills/handoff.md +0 -21
  100. package/template/workflows/matt-pocock/atomic-skills/implement.md +0 -21
  101. package/template/workflows/matt-pocock/atomic-skills/improve-codebase-architecture.md +0 -21
  102. package/template/workflows/matt-pocock/atomic-skills/loop-me.md +0 -21
  103. package/template/workflows/matt-pocock/atomic-skills/prototype.md +0 -21
  104. package/template/workflows/matt-pocock/atomic-skills/research.md +0 -21
  105. package/template/workflows/matt-pocock/atomic-skills/resolving-merge-conflicts.md +0 -21
  106. package/template/workflows/matt-pocock/atomic-skills/setup-matt-pocock-skills.md +0 -21
  107. package/template/workflows/matt-pocock/atomic-skills/tdd.md +0 -21
  108. package/template/workflows/matt-pocock/atomic-skills/teach.md +0 -21
  109. package/template/workflows/matt-pocock/atomic-skills/to-spec.md +0 -21
  110. package/template/workflows/matt-pocock/atomic-skills/to-tickets.md +0 -21
  111. package/template/workflows/matt-pocock/atomic-skills/triage.md +0 -21
  112. package/template/workflows/matt-pocock/atomic-skills/wayfinder.md +0 -21
  113. package/template/workflows/matt-pocock/atomic-skills/wizard.md +0 -21
  114. package/template/workflows/matt-pocock/atomic-skills/writing-beats.md +0 -21
  115. package/template/workflows/matt-pocock/atomic-skills/writing-fragments.md +0 -21
  116. package/template/workflows/matt-pocock/atomic-skills/writing-great-skills.md +0 -21
  117. package/template/workflows/matt-pocock/atomic-skills/writing-shape.md +0 -20
  118. package/template/workflows/matt-pocock/routes/architecture.md +0 -24
  119. package/template/workflows/matt-pocock/routes/diagnose.md +0 -22
  120. package/template/workflows/matt-pocock/routes/experimental.md +0 -18
  121. package/template/workflows/matt-pocock/routes/idea-to-delivery.md +0 -63
  122. package/template/workflows/matt-pocock/routes/merge-conflicts.md +0 -19
  123. package/template/workflows/matt-pocock/routes/productivity.md +0 -25
  124. package/template/workflows/matt-pocock/routes/research-prototype.md +0 -20
  125. package/template/workflows/matt-pocock/routes/review.md +0 -19
  126. package/template/workflows/matt-pocock/routes/setup.md +0 -42
  127. package/template/workflows/matt-pocock/routes/triage.md +0 -25
  128. package/template/workflows/matt-pocock/routes/wayfinder.md +0 -27
  129. package/template/workflows/person/PERSISTENCE.md +0 -56
  130. package/template/workflows/person/WORKFLOW.md +0 -50
  131. package/template/workflows/person/_state/.config/LESSONS.md +0 -3
  132. package/template/workflows/person/_state/.config/RULES.md +0 -3
  133. package/template/workflows/person/_state/.config/context/.gitkeep +0 -1
  134. package/template/workflows/person/_templates/mao-consultation-output-template.md +0 -55
@@ -0,0 +1,99 @@
1
+ # LOG.md 格式
2
+
3
+ 设计决策日志存放在变更目录的 `<Path>{roots.state}/specdev/changes/{change}/LOG.md</Path>` 中。它记录设计访谈中已经确认、延后或被替代的具体结论——"当时讨论了什么、最终固定了什么行为以及对应哪个 ADR"。与 CONTEXT.md 的精炼不同,LOG.md 可以记录讨论中的具体场景、边界条件和交互细节。
4
+
5
+ ## 规则
6
+
7
+ - **记录每一次设计结论。** 无论大小,只要在访谈中确认、延后或被替代,都写入 LOG.md。宁可多记,不要遗漏。
8
+ - **状态驱动。** 每个条目明确标记 `accepted`、`deferred` 或 `superseded`,让读者一眼知道当前有效性。
9
+ - **关联 ADR。** 如果该结论同时满足 ADR 的三个条件,在 LOG 中标注 `Related: ADR-XXXX`,并在对应 ADR 条目中也关联回 LOG。
10
+ - **保持可修订。** 后续决定改变既有结论时,直接更新原条目状态和正文,不要新建一条矛盾的条目。标注 `Superseded by: LOG-XXXX`。
11
+ - **不堆积废弃条目。** 被替代的条目保留但标记清楚;延后(deferred)的条目保留以便后续恢复讨论。
12
+ - **记录具体交互和边界。** 与 CONTEXT.md 的精炼不同,LOG.md 可以记录讨论中的具体场景、边界条件和交互细节。
13
+
14
+ ## 模板
15
+
16
+ ```md
17
+ # 设计决策日志
18
+
19
+ 本文件记录设计访谈中已经确认、延后或被替代的具体结论。它保存"当时讨论了什么、最终固定了什么行为以及对应哪个 ADR";CONTEXT.md 是规范词汇表,ADR.md 是难以逆转的架构决策,本文件则是可持续增删改的完整设计轨迹。
20
+
21
+ ## 维护规则
22
+
23
+ - 每次完成一个设计问答,同步更新 LOG.md、CONTEXT.md 与 ADR.md。
24
+ - 已确认结论使用 `accepted`;暂不决定使用 `deferred`;被后续决定替代使用 `superseded`。
25
+ - 后续确认改变既有结论时,直接修订原日志条目,并记录替代关系,不保留互相矛盾的"现行规则"。
26
+ - 日志可以记录具体交互和边界;词汇表保持精炼;ADR 只记录难以逆转、令人意外且存在真实权衡的决定。
27
+
28
+ ## LOG-0001: {决策的简短标题}
29
+
30
+ Status: accepted
31
+ Related: ADR-0001
32
+
33
+ {背景:讨论了什么问题,做出了什么决定,以及为什么。可以记录具体的交互过程、边界场景和固定行为。}
34
+
35
+ ## LOG-0002: {另一决策标题}
36
+
37
+ Status: deferred
38
+
39
+ {为什么暂不决定,以及后续恢复讨论时应从什么问题开始。}
40
+
41
+ ## LOG-0003: {被替代的决策标题}
42
+
43
+ Status: superseded
44
+ Superseded by: LOG-0004
45
+
46
+ {原决策内容,以及为什么被替代。保留作为设计演进的历史上下文。}
47
+ ```
48
+
49
+ ## 状态说明
50
+
51
+ - **Status: accepted**——已确认的现行结论。这是讨论后用户明确同意的决定,当前仍然有效。
52
+ - **Status: deferred**——暂不决定,留待后续讨论。记录为什么暂不决定以及从什么角度恢复讨论,以便后续接续上下文。
53
+ - **Status: superseded**——被后续决定替代。标注 `Superseded by: LOG-XXXX` 指向替代条目。保留原条目作为设计演进的历史上下文。
54
+
55
+ ## 追加新日志
56
+
57
+ 1. 读取 `<Path>{roots.state}/specdev/changes/{change}/LOG.md</Path>`,找到最高现有编号
58
+ 2. 编号加 1(从 `0001` 开始,不足四位补零)
59
+ 3. 在文件末尾追加新条目
60
+
61
+ ## 修改已有日志
62
+
63
+ - **改变结论**——将原条目状态改为 `superseded`,标注 `Superseded by: LOG-XXXX`,在新条目中说明替代原因。不要直接修改原条目的结论内容——保留它让读者能看到设计是如何演进的。
64
+ - **延后决定被重新讨论**——将状态从 `deferred` 改为 `accepted`,或新建条目替代原条目(原条目改为 `superseded`)。如果内容没有变化只是状态升级,可以直接改状态;如果结论发生了变化,使用替代模式。
65
+ - **补充细节**——直接编辑条目正文,不改变状态。可以追加更多场景、边界条件或交互细节。
66
+ - **关联 ADR**——如果后来为该日志创建了 ADR,补充 `Related: ADR-XXXX` 标注。
67
+ - **不要删除**——即使结论被替代,保留条目作为设计演进的历史上下文。
68
+
69
+ ## 示例:被替代的日志
70
+
71
+ 以下示例展示一条日志从 accepted 变为 superseded 的全过程:
72
+
73
+ 原始条目:
74
+
75
+ ```md
76
+ ## LOG-0005: 订单状态机使用三态模型
77
+
78
+ Status: accepted
79
+
80
+ 订单状态为 pending → confirmed → completed 的三态模型。
81
+ ```
82
+
83
+ 讨论后发现需要更细粒度,追加替代条目:
84
+
85
+ ```md
86
+ ## LOG-0005: 订单状态机使用三态模型
87
+
88
+ Status: superseded
89
+ Superseded by: LOG-0007
90
+
91
+ 订单状态为 pending → confirmed → completed 的三态模型。后续讨论发现 confirmed 状态无法区分"已付款待发货"和"已发货待签收",因此改为五态模型。
92
+
93
+ ## LOG-0007: 订单状态机使用五态模型
94
+
95
+ Status: accepted
96
+
97
+ 订单状态为 pending → paid → shipped → delivered → completed 的五态模型。
98
+ 详见 LOG-0005 的讨论背景。
99
+ ```
@@ -0,0 +1,85 @@
1
+ ---
2
+ id: specdev/implement
3
+ type: workflow-entry
4
+ workflow: specdev
5
+ name: 实现
6
+ description: 基于 spec 或 tickets 实现工作——以深层模块设计原则指导架构、以 TDD 红绿循环驱动编码、以双轴审查把关质量。
7
+ keywords: [实现, TDD, 代码审查, 模块设计, 重构]
8
+ ---
9
+
10
+ # 实现
11
+
12
+ 基于 spec 或 tickets 实现工作——融合设计检查、TDD、审查、提交的完整实现流程。每一步引用内部子文件,不依赖外部 skill。
13
+
14
+ 在开始实现之前,读取当前变更的上下文与架构决策:
15
+
16
+ - **CONTEXT.md** —— 项目领域术语与概念:`<Path>{roots.state}/specdev/changes/{change}/CONTEXT.md</Path>`
17
+ - **ADR.md** —— 架构决策记录:`<Path>{roots.state}/specdev/changes/{change}/ADR.md</Path>`
18
+
19
+ 如果这些文件不存在,先运行 `<Path>{roots.workflows}/specdev/W-wayfinder/W-wayfinder.md</Path>` 或询问用户以建立上下文。
20
+
21
+ ## 流程
22
+
23
+ ### 1. 设计检查
24
+
25
+ 在编写任何代码之前,检查当前变更涉及的模块接口设计。使用 `<Path>{roots.workflows}/specdev/I-implement/codebase-design-glossary.md</Path>` 中的术语表评估:
26
+
27
+ - 要实现的代码属于哪些**模块**?
28
+ - 每个模块的**接口**是什么?(类型签名、不变量、顺序约束、错误模式)
29
+ - 接口的**深度**如何?调用者是否获得了足够的**杠杆效应**?
30
+ - **接缝**放在哪里?是否有至少两个适配器来证明接缝是真实的?
31
+ - 每个接缝处的依赖属于哪个类别?(进程内、本地可替换、远程但自有、真正的外部依赖——参见 `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>`)
32
+
33
+ 如果需要探索替代接口设计,启动 `<Path>{roots.workflows}/specdev/I-implement/design-it-twice.md</Path>` 流程。
34
+
35
+ **完成标准**:模块接口设计已检查——深度、接缝位置、适配器策略合理。每个接缝的依赖类别已分类。
36
+
37
+ ### 2. TDD 循环
38
+
39
+ 按照 `<Path>{roots.workflows}/specdev/I-implement/tdd-rules.md</Path>` 执行红→绿循环:
40
+
41
+ 1. 确认每个接缝的测试策略(参见 `<Path>{roots.workflows}/specdev/I-implement/tdd-examples.md</Path>` 中的示例)
42
+ 2. 在接缝处编写失败的测试——通过公共接口验证行为
43
+ 3. 只写足以通过测试的代码
44
+ 4. 每个循环一张垂直切片,响应上一个循环的反馈
45
+
46
+ 循环重复直到所有 tickets 实现完成。如果审查后发现阻塞性问题(步骤 3),返回此步骤继续循环。
47
+
48
+ **完成标准**:红→绿循环完成——每个接缝一张垂直切片,测试通过公共接口验证行为。所有 tickets 实现完成。
49
+
50
+ ### 3. 审查
51
+
52
+ 按照 `<Path>{roots.workflows}/specdev/I-implement/code-review-process.md</Path>` 执行双轴审查:
53
+
54
+ - **标准轴** — 代码是否符合编码规范?是否出现 Fowler 代码异味?
55
+ - **规范轴** — 代码是否忠实地实现了 `<Path>{roots.state}/specdev/changes/{change}/spec.md</Path>` 中的要求?
56
+
57
+ 如果审查发现阻塞性问题,返回步骤 2「TDD 循环」修复;否则进入提交。
58
+
59
+ **完成标准**:双轴审查完成——标准轴和规范轴均已通过,无阻塞性问题。
60
+
61
+ ### 4. 提交
62
+
63
+ 1. 运行类型检查:`npx tsc --noEmit`
64
+ 2. 运行完整测试套件:`npx vitest run`
65
+ 3. 将更改提交到当前分支:`git add -A && git commit -m "<描述性提交信息>"`
66
+ 4. 更新 `<Path>{roots.state}/specdev/changes/{change}/</Path>` 下的状态文件
67
+
68
+ **完成标准**:代码已提交到当前分支,类型检查和测试通过,状态文件已更新。
69
+
70
+ ---
71
+
72
+ ## 子文件引用
73
+
74
+ 以下子文件包含各步骤的详细规则、示例和参考材料,仅在对应步骤进入时加载:
75
+
76
+ | 文件 | 内容 | 触发条件 |
77
+ |------|------|---------|
78
+ | `<Path>{roots.workflows}/specdev/I-implement/codebase-design-glossary.md</Path>` | 深层模块设计的 8 个术语定义、原则、为可测试性设计 | 步骤 1「设计检查」进入时 |
79
+ | `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>` | 依赖类别、接缝纪律、测试策略 | 设计检查中需要分析依赖时 |
80
+ | `<Path>{roots.workflows}/specdev/I-implement/design-it-twice.md</Path>` | 并行子 Agent 探索替代接口的三步流程 | 设计检查中需要探索替代方案时 |
81
+ | `<Path>{roots.workflows}/specdev/I-implement/tdd-rules.md</Path>` | 红→绿循环的完整规则、反模式、接缝测试策略 | 步骤 2「TDD 循环」进入时 |
82
+ | `<Path>{roots.workflows}/specdev/I-implement/tdd-examples.md</Path>` | 好的测试 vs 坏的测试、同义反复、Mock 指南、为可 Mock 性设计 | 编写具体测试时参考 |
83
+ | `<Path>{roots.workflows}/specdev/I-implement/code-review-process.md</Path>` | 双轴审查流程、12 个 Fowler 异味基线、子 Agent 提示词模板 | 步骤 3「审查」进入时 |
84
+
85
+ 实现的产物(代码变更)直接写入仓库的源代码目录。变更追踪、spec 和 ADR 仍存放在 `<Path>{roots.state}/specdev/changes/{change}/</Path>` 下。
@@ -0,0 +1,83 @@
1
+ # 代码审查
2
+
3
+ 对 `HEAD` 与实现开始前的某个固定点之间的 diff 进行双轴审查:
4
+
5
+ - **标准** — 代码是否符合此仓库已记录的编码规范?
6
+ - **规范** — 代码是否忠实地实现了原始 spec?
7
+
8
+ 两个轴以**并行子 Agent** 方式运行,以免互相污染上下文,然后本流程汇总它们的发现。
9
+
10
+ ## 流程
11
+
12
+ ### 1. 确定固定点
13
+
14
+ 实现开始前的 commit SHA、分支名、tag、`main` 或合并基准。通常为开始实现前的 HEAD。如果用户未指定,请询问。
15
+
16
+ 一次性捕获 diff 命令:`git diff <固定点>...HEAD`(三个点,以便与合并基准比较)。同时通过 `git log <固定点>..HEAD --oneline` 记录 commit 列表。
17
+
18
+ 继续之前,确认固定点可解析(`git rev-parse <固定点>`)且 diff 非空。无效引用或空 diff 应在此处失败 — 而非在两个并行子 Agent 内部。
19
+
20
+ ### 2. 识别规范来源
21
+
22
+ 规范来源为当前变更的 spec 文件:
23
+
24
+ `<Path>{roots.state}/specdev/changes/{change}/spec.md</Path>`
25
+
26
+ 如果该文件不存在,**规范**子 Agent 将跳过并报告"无可用的规范"。
27
+
28
+ ### 3. 识别标准来源
29
+
30
+ 仓库中所有记录代码应如何编写的文件,如 `CODING_STANDARDS.md` 或 `CONTRIBUTING.md`。
31
+
32
+ 在仓库记录的任何标准之上,标准轴始终携带以下**异味基线** — 一组固定的 Fowler 代码异味(《重构》第 3 章),即使仓库没有任何记录也适用。两条规则约束它:
33
+
34
+ - **仓库优先。** 已记录的仓库标准始终优先;当它认可基线可能标记的内容时,抑制该异味。
35
+ - **始终是判断。** 每个异味是一个带标签的启发式("可能的 Feature Envy"),从来不是硬性违规 — 并且,与这里的任何标准一样,跳过工具链已在强制执行的内容。
36
+
37
+ 每个异味读作*它是什么* → *如何修复*;将其与 diff 进行匹配:
38
+
39
+ - **Mysterious Name** — 函数、变量或类型,其名称不能揭示它做什么或持有什么。→ 重命名;如果没有诚实的名称可用,说明设计不清晰。
40
+ - **Duplicated Code** — 相同的逻辑形态出现在变更中的一个以上代码块或文件中。→ 提取共享形态,从两处调用。
41
+ - **Feature Envy** — 一个方法过多地访问另一个对象的数据,而非自身数据。→ 将该方法移动到它所羡慕的数据上。
42
+ - **Data Clumps** — 相同的几个字段或参数总是一起出现(一个等待诞生的类型)。→ 将它们捆绑成一个类型,传递该类型。
43
+ - **Primitive Obsession** — 一个基本类型或字符串替代了应拥有自己类型的领域概念。→ 为该概念创建自己的小型类型。
44
+ - **Repeated Switches** — 对同一类型的相同 `switch`/`if` 级联在变更中反复出现。→ 用多态替代,或使用两个位置共享的一个映射。
45
+ - **Shotgun Surgery** — 一个逻辑变更迫使在 diff 中跨多个文件进行分散编辑。→ 将一起变更的内容汇聚到一个模块中。
46
+ - **Divergent Change** — 一个文件或模块因多个不相关的原因被编辑。→ 拆分,使每个模块因一个原因变更。
47
+ - **Speculative Generality** — 为 spec 中没有的需求添加的抽象、参数或钩子。→ 删除它;回退内联,直到真实需求出现。
48
+ - **Message Chains** — 调用者不应依赖的长链式 `a.b().c().d()` 导航。→ 在第一个对象上用一个方法隐藏整个链路。
49
+ - **Middle Man** — 一个类或函数大部分只是委托转发。→ 删除它,直接调用真正的目标。
50
+ - **Refused Bequest** — 一个子类或实现者忽略或覆盖了其继承的大部分内容。→ 放弃继承,使用组合。
51
+
52
+ ### 4. 并行启动两个子 Agent
53
+
54
+ 发送一条消息,包含两个 `Agent` 工具调用。两者均使用 `general-purpose` 子 Agent。
55
+
56
+ **标准子 Agent 提示词** — 包含:
57
+
58
+ - 完整的 diff 命令和 commit 列表。
59
+ - 在第 3 步中找到的标准来源文件列表,**加上第 3 步中的异味基线全文粘贴** — 子 Agent 没有其他途径获取它。
60
+ - 任务简述:"报告 — 在相关时按文件/代码块 — (a) diff 违反已记录标准的每个地方:引用标准(文件 + 规则);以及 (b) 你发现的任何基线异味:命名并引用代码块。区分硬性违规和判断性调用 — 已记录标准的违规可以是硬性的,但基线异味始终是判断性调用,且已记录的仓库标准覆盖基线。跳过工具链已强制执行的内容。400 词以内。"
61
+
62
+ **规范子 Agent 提示词** — 包含:
63
+
64
+ - diff 命令和 commit 列表。
65
+ - spec 的路径或已获取的内容:`<Path>{roots.state}/specdev/changes/{change}/spec.md</Path>`。
66
+ - 任务简述:"报告:(a) spec 要求但缺失或不完整的需求;(b) diff 中存在但未被要求的、超出范围的行为;(c) 看起来已实现但实现方式错误的需求。每个发现引用 spec 原文。400 词以内。"
67
+
68
+ 如果 spec 缺失,跳过规范子 Agent 并在最终报告中注明。
69
+
70
+ ### 5. 汇总
71
+
72
+ 在 `## 标准` 和 `## 规范` 标题下呈现两份报告,原文或略微整理。**不要**合并或重新排名发现 — 两个轴故意分离(见*为什么是两个轴*)。
73
+
74
+ 以一行摘要结束:每个轴的发现总数,以及每个轴内_最严重的问题_(如有)。不要在轴之间选一个"赢家" — 那正是分离设计要防止的重新排名。
75
+
76
+ ## 为什么是两个轴
77
+
78
+ 一个变更可能通过一个轴而未通过另一个:
79
+
80
+ - 代码遵循所有标准但实现了错误的东西 → **标准通过,规范未通过。**
81
+ - 代码完全按 spec 要求做了,但违反了项目的约定 → **规范通过,标准未通过。**
82
+
83
+ 分开报告可以防止一个轴掩盖另一个轴。
@@ -0,0 +1,109 @@
1
+ # 代码仓设计
2
+
3
+ 设计**深层模块**:通过一个小接口承载大量行为,放置在干净的缝合点处,可通过该接口进行测试。在任何设计或重构代码的地方使用这些语言和原则。目标是为调用者提供杠杆效应,为维护者提供局部性,为所有人提供可测试性。
4
+
5
+ ## 术语表
6
+
7
+ 严格使用以下术语 — 不要用 "component"、"service"、"API" 或 "boundary" 替代。一致的语言才是重点。
8
+
9
+ **Module(模块)** — 任何具有接口和实现的东西。有意识地与规模无关:函数、类、包或跨层切片。_避免使用_:unit、component、service。
10
+
11
+ **Interface(接口)** — 调用者正确使用模块所需了解的一切:类型签名,还包括不变量、顺序约束、错误模式、必需配置和性能特征。_避免使用_:API、signature(太窄 — 它们仅指类型层面的表面)。
12
+
13
+ **Implementation(实现)** — 模块内部的内容,它的代码体。区别于 **Adapter(适配器)**:一个东西可以是一个小适配器加一个大实现(Postgres 仓库),也可以是一个大适配器加一个小实现(内存假实现)。当讨论缝合点时用 "adapter";否则用 "implementation"。
14
+
15
+ **Depth(深度)** — 接口处的杠杆效应:调用者(或测试)每学习一个单位的接口可以驱动的行为量。当大量行为隐藏在小接口后面时,模块是**深层的**;当接口几乎和实现一样复杂时,模块是**浅层的**。
16
+
17
+ **Seam(缝合点)** _(Michael Feathers)_ — 一个可以在不编辑该位置的情况下改变行为的地方;模块接口所在的*位置*。缝合点放在哪里本身就是一个设计决策,与缝合点后面放什么不同。_避免使用_:boundary(与 DDD 的有界上下文重载)。
18
+
19
+ **Adapter(适配器)** — 在缝合点处满足接口的具体事物。描述的是*角色*(它填充哪个槽位),而非实质(内部是什么)。
20
+
21
+ **Leverage(杠杆效应)** — 调用者从深度中获得的好处:每学习一个单位的接口获得更多的能力。一个实现为 N 个调用点和 M 个测试带来回报。
22
+
23
+ **Locality(局部性)** — 维护者从深度中获得的好处:变更、bug、知识和验证集中在一个地方,而非分散在调用者之间。一次修复,处处生效。
24
+
25
+ ## 深层 vs 浅层
26
+
27
+ **深层模块** = 小接口 + 大量实现:
28
+
29
+ ```
30
+ ┌─────────────────────┐
31
+ │ 小接口 │ ← 少量方法,简单参数
32
+ ├─────────────────────┤
33
+ │ │
34
+ │ 深层实现 │ ← 隐藏的复杂逻辑
35
+ │ │
36
+ └─────────────────────┘
37
+ ```
38
+
39
+ **浅层模块** = 大接口 + 少量实现(应避免):
40
+
41
+ ```
42
+ ┌─────────────────────────────────┐
43
+ │ 大接口 │ ← 大量方法,复杂参数
44
+ ├─────────────────────────────────┤
45
+ │ 薄实现 │ ← 仅仅是透传
46
+ └─────────────────────────────────┘
47
+ ```
48
+
49
+ 设计接口时,问自己:
50
+
51
+ - 我能减少方法数量吗?
52
+ - 我能简化参数吗?
53
+ - 我能隐藏更多内部的复杂性吗?
54
+
55
+ ## 原则
56
+
57
+ - **深度是接口的属性,而非实现的属性。** 一个深层模块内部可以由小型、可模拟、可替换的部分组成 — 只是它们不属于接口的一部分。一个模块可以拥有**内部缝合点**(对其实现私有,用于其自身测试)以及位于其接口处的**外部缝合点**。
58
+ - **删除测试。** 想象删除这个模块。如果复杂性消失,它就是个透传层。如果复杂性在 N 个调用者中重新出现,它就在发挥价值。
59
+ - **接口就是测试表面。** 调用者和测试穿过同一个缝合点。如果你想测试接口_之外_的内容,模块可能形状不对。
60
+ - **一个适配器意味着假设的缝合点。两个适配器意味着真实的缝合点。** 除非有东西确实在缝合点两侧变化,否则不要引入缝合点。
61
+
62
+ ## 为可测试性而设计
63
+
64
+ 良好的接口使测试变得自然:
65
+
66
+ 1. **接收依赖,不要创建依赖。**
67
+
68
+ ```typescript
69
+ // 可测试
70
+ function processOrder(order, paymentGateway) {}
71
+
72
+ // 难以测试
73
+ function processOrder(order) {
74
+ const gateway = new StripeGateway();
75
+ }
76
+ ```
77
+
78
+ 2. **返回结果,不要产生副作用。**
79
+
80
+ ```typescript
81
+ // 可测试
82
+ function calculateDiscount(cart): Discount {}
83
+
84
+ // 难以测试
85
+ function applyDiscount(cart): void {
86
+ cart.total -= discount;
87
+ }
88
+ ```
89
+
90
+ 3. **小表面积。** 更少的方法 = 更少的测试需求。更少的参数 = 更简单的测试设置。
91
+
92
+ ## 关系
93
+
94
+ - 一个 **Module** 恰好有一个 **Interface**(它向调用者和测试呈现的表面)。
95
+ - **Depth** 是一个 **Module** 的属性,对照其 **Interface** 来度量。
96
+ - 一个 **Seam** 是一个 **Module** 的 **Interface** 所在的位置。
97
+ - 一个 **Adapter** 位于 **Seam** 处,满足 **Interface**。
98
+ - **Depth** 为调用者产生 **Leverage**,为维护者产生 **Locality**。
99
+
100
+ ## 已拒绝的框架
101
+
102
+ - **深度作为实现行数与接口行数之比** (Ousterhout):奖励填充实现。我们使用深度即杠杆效应来替代。
103
+ - **"Interface" 作为 TypeScript 的 `interface` 关键字或类的公开方法**:太窄 — 此处的接口包括调用者必须了解的每个事实。
104
+ - **"Boundary"**:与 DDD 的有界上下文重载。说 **seam** 或 **interface**。
105
+
106
+ ## 深入阅读
107
+
108
+ - **给定依赖的情况下深化一个集群** — 见 `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>`:依赖类别、缝合点规程、以及替换而非分层的测试。
109
+ - **探索替代接口** — 见 `<Path>{roots.workflows}/specdev/I-implement/design-it-twice.md</Path>`:启动并行子 agent,以几种截然不同的方式设计接口,然后在深度、局部性和缝合点位置上进行比较。
@@ -0,0 +1,37 @@
1
+ # 深化
2
+
3
+ 如何在给定依赖关系的情况下,安全地深化一组浅模块。假定你已掌握 `<Path>{roots.workflows}/specdev/I-implement/codebase-design-glossary.md</Path>` 中的词汇 — **module**(模块)、**interface**(接口)、**seam**(接缝)、**adapter**(适配器)。
4
+
5
+ ## 依赖类别
6
+
7
+ 在评估一个深化候选时,对其依赖进行分类。类别决定了深化后的模块如何通过其接缝进行测试。
8
+
9
+ ### 1. 进程内
10
+
11
+ 纯计算、内存状态、无 I/O。始终可深化 — 合并模块并通过新接口直接测试。不需要适配器。
12
+
13
+ ### 2. 本地可替换
14
+
15
+ 具有本地测试替代品的依赖(PGLite 替代 Postgres、内存文件系统)。如果存在替代品则可深化。深化后的模块在测试套件中使用运行的替代品进行测试。接缝是内部的;在模块的外部接口处不需要端口。
16
+
17
+ ### 3. 远程但自有(端口与适配器)
18
+
19
+ 跨网络边界的自有服务(微服务、内部 API)。在接缝处定义一个 **port**(端口,即接口)。深模块拥有逻辑;传输层作为 **adapter**(适配器)注入。测试使用内存适配器。生产环境使用 HTTP/gRPC/队列适配器。
20
+
21
+ 建议形式:*"在接缝处定义一个端口,为生产环境实现 HTTP 适配器,为测试实现内存适配器,这样逻辑就驻留在一个深模块中,即使它跨网络部署。"*
22
+
23
+ ### 4. 真正的外部依赖(Mock)
24
+
25
+ 你无法控制的第三方服务(Stripe、Twilio 等)。深化后的模块将外部依赖作为注入端口;测试提供一个 mock 适配器。
26
+
27
+ ## 接缝纪律
28
+
29
+ - **一个适配器意味着假设性接缝。两个适配器意味着真正的接缝。** 除非至少有两个适配器是合理的(通常是生产 + 测试),否则不要引入端口。单一适配器的接缝只是间接层。
30
+ - **内部接缝 vs 外部接缝。** 一个深模块可以既有内部接缝(对其实现私有,供其自身的测试使用),也有其接口处的外部接缝。不要仅仅因为测试使用了内部接缝就通过接口暴露它们。
31
+
32
+ ## 测试策略:替换,而非叠加
33
+
34
+ - 一旦深化后模块接口的测试存在,旧有浅模块上的单元测试就变成了废料 — 删除它们。
35
+ - 在深化后模块的接口处编写新测试。**接口就是测试表面**。
36
+ - 测试通过接口断言可观察的结果,而非内部状态。
37
+ - 测试应经受住内部重构 — 它们描述的是行为,而非实现。如果测试在实现改变时必须更改,那它就是在测试接口之后的东西。
@@ -0,0 +1,44 @@
1
+ # 设计两次
2
+
3
+ 当用户想要为选定的深化候选探索替代接口时,使用此并行子 Agent 模式。基于"Design It Twice"(Ousterhout)— 你的第一个想法不太可能是最好的。
4
+
5
+ 使用 `<Path>{roots.workflows}/specdev/I-implement/codebase-design-glossary.md</Path>` 中的词汇 — **module**(模块)、**interface**(接口)、**seam**(接缝)、**adapter**(适配器)、**leverage**(杠杆)。
6
+
7
+ ## 流程
8
+
9
+ ### 1. 界定问题空间
10
+
11
+ 在启动子 Agent 之前,为选定候选编写一份面向用户的问题空间说明:
12
+
13
+ - 任何新接口需要满足的约束条件
14
+ - 它将依赖的依赖项,以及它们属于哪个类别(参见 `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>`)
15
+ - 一个粗略的示例代码草图来使约束具体化 — 不是提案,只是让约束变得具体的一种方式
16
+
17
+ 将此展示给用户,然后立即进入第 2 步。用户在子 Agent 并行工作时阅读和思考。
18
+
19
+ ### 2. 启动子 Agent
20
+
21
+ 使用 Agent 工具并行启动 3+ 个子 Agent。每个子 Agent 必须为深化后的模块生成一个**截然不同的**接口。
22
+
23
+ 为每个子 Agent 提供一份独立的技术简报(文件路径、耦合细节、来自 `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>` 的依赖类别、接缝背后的内容)。简报独立于第 1 步中面向用户的问题空间说明。给每个 Agent 一个不同的设计约束:
24
+
25
+ - Agent 1:"最小化接口 — 目标 1–3 个入口点。最大化每个入口点的杠杆。"
26
+ - Agent 2:"最大化灵活性 — 支持多种用例和扩展。"
27
+ - Agent 3:"为最常见的调用方优化 — 让默认情况变得简单。"
28
+ - Agent 4(如适用):"围绕接缝设计端口与适配器,以处理跨接缝依赖。"
29
+
30
+ 在简报中同时包含 `<Path>{roots.workflows}/specdev/I-implement/codebase-design-glossary.md</Path>` 词汇和 CONTEXT.md 词汇,以便每个子 Agent 能使用架构语言和项目的领域语言一致地命名事物。
31
+
32
+ 每个子 Agent 输出:
33
+
34
+ 1. 接口(类型、方法、参数 — 以及不变量、排序、错误模式)
35
+ 2. 使用示例,展示调用方如何使用它
36
+ 3. 实现在接缝背后隐藏了什么
37
+ 4. 依赖策略和适配器(参见 `<Path>{roots.workflows}/specdev/I-implement/deepening.md</Path>`)
38
+ 5. 权衡 — 哪里杠杆高,哪里杠杆薄
39
+
40
+ ### 3. 展示和比较
41
+
42
+ 按顺序展示各个设计,让用户能够消化每一个,然后用文字进行比较。通过 **depth**(深度,接口处的杠杆)、**locality**(局部性,变更集中的位置)和 **seam placement**(接缝位置)来对比。
43
+
44
+ 比较之后,给出你自己的建议:你认为哪个设计最强以及原因。如果不同设计中的元素可以很好地组合,提出一个混合方案。要有主见 — 用户想要的是一个有力的判断,而不是一个菜单。
@@ -0,0 +1,139 @@
1
+ # 好的测试与坏的测试
2
+
3
+ ## 好的测试
4
+
5
+ **集成风格**:通过真实接口测试,而非 mock 内部部件。
6
+
7
+ ```typescript
8
+ // 好:测试可观察的行为
9
+ test("user can checkout with valid cart", async () => {
10
+ const cart = createCart();
11
+ cart.add(product);
12
+ const result = await checkout(cart, paymentMethod);
13
+ expect(result.status).toBe("confirmed");
14
+ });
15
+ ```
16
+
17
+ 特征:
18
+
19
+ - 测试用户/调用方关心的行为
20
+ - 仅使用公共 API
21
+ - 经受住内部重构
22
+ - 描述 WHAT(做什么),而非 HOW(怎么做)
23
+ - 每个测试一个逻辑断言
24
+
25
+ ## 坏的测试
26
+
27
+ **实现细节测试**:与内部结构耦合。
28
+
29
+ ```typescript
30
+ // 坏:测试实现细节
31
+ test("checkout calls paymentService.process", async () => {
32
+ const mockPayment = jest.mock(paymentService);
33
+ await checkout(cart, payment);
34
+ expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
35
+ });
36
+ ```
37
+
38
+ 危险信号:
39
+
40
+ - Mock 内部协作者
41
+ - 测试私有方法
42
+ - 断言调用次数/顺序
43
+ - 重构时测试失败但没有行为变化
44
+ - 测试名称描述 HOW 而非 WHAT
45
+ - 通过外部手段而非接口进行验证
46
+
47
+ ```typescript
48
+ // 坏:绕过接口进行验证
49
+ test("createUser saves to database", async () => {
50
+ await createUser({ name: "Alice" });
51
+ const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
52
+ expect(row).toBeDefined();
53
+ });
54
+
55
+ // 好:通过接口进行验证
56
+ test("createUser makes user retrievable", async () => {
57
+ const user = await createUser({ name: "Alice" });
58
+ const retrieved = await getUser(user.id);
59
+ expect(retrieved.name).toBe("Alice");
60
+ });
61
+ ```
62
+
63
+ **同义反复测试**:预期值重述了实现,因此测试在构造上就通过了。
64
+
65
+ ```typescript
66
+ // 坏:预期值以与代码计算方式相同的方式重新计算
67
+ test("calculateTotal sums line items", () => {
68
+ const items = [{ price: 10 }, { price: 5 }];
69
+ const expected = items.reduce((sum, i) => sum + i.price, 0);
70
+ expect(calculateTotal(items)).toBe(expected);
71
+ });
72
+
73
+ // 好:预期值是独立的、已知的字面量
74
+ test("calculateTotal sums line items", () => {
75
+ expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
76
+ });
77
+ ```
78
+
79
+ ---
80
+
81
+ # 何时使用 Mock
82
+
83
+ 仅在**系统边界**处使用 Mock:
84
+
85
+ - 外部 API(支付、邮件等)
86
+ - 数据库(有时 — 优先使用测试数据库)
87
+ - 时间/随机性
88
+ - 文件系统(有时)
89
+
90
+ 不要 Mock:
91
+
92
+ - 你自己的类/模块
93
+ - 内部协作者
94
+ - 任何你控制的东西
95
+
96
+ ## 为可 Mock 性设计
97
+
98
+ 在系统边界处,设计易于 mock 的接口:
99
+
100
+ **1. 使用依赖注入**
101
+
102
+ 将外部依赖从外部传入,而不是在内部创建:
103
+
104
+ ```typescript
105
+ // 易于 mock
106
+ function processPayment(order, paymentClient) {
107
+ return paymentClient.charge(order.total);
108
+ }
109
+
110
+ // 难以 mock
111
+ function processPayment(order) {
112
+ const client = new StripeClient(process.env.STRIPE_KEY);
113
+ return client.charge(order.total);
114
+ }
115
+ ```
116
+
117
+ **2. 偏好 SDK 风格接口而非通用获取器**
118
+
119
+ 为每个外部操作创建特定的函数,而不是带有条件逻辑的通用函数:
120
+
121
+ ```typescript
122
+ // 好:每个函数可以独立 mock
123
+ const api = {
124
+ getUser: (id) => fetch(`/users/${id}`),
125
+ getOrders: (userId) => fetch(`/users/${userId}/orders`),
126
+ createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
127
+ };
128
+
129
+ // 坏:mock 需要在 mock 内部编写条件逻辑
130
+ const api = {
131
+ fetch: (endpoint, options) => fetch(endpoint, options),
132
+ };
133
+ ```
134
+
135
+ SDK 方式的优点:
136
+ - 每个 mock 返回一个特定的形态
137
+ - 测试设置中无需条件逻辑
138
+ - 更容易看出测试涉及哪些端点
139
+ - 每个端点的类型安全
@@ -0,0 +1,31 @@
1
+ # 测试驱动开发
2
+
3
+ TDD 是红 → 绿循环。此文件是该循环的参考指南,确保该循环产出的测试值得保留:什么是一个好的测试、测试放在哪里、反模式、以及循环的规则。每个章节在每次循环中都适用 —— 在循环之前和循环期间查阅,而不是之后。
4
+
5
+ 在探索代码库时,读取 `{roots.state}/specdev/changes/{change}/CONTEXT.md`(如果存在),使测试名称和接口词汇与项目的领域语言保持一致,并尊重所涉及区域的 ADR。
6
+
7
+ ## 什么是好的测试
8
+
9
+ 测试通过公共接口验证行为,而不是实现细节。代码可以完全改变;测试不应该。一个好的测试读起来像规范 —— "用户可以使用有效购物车结账" 准确地告诉你存在什么能力 —— 并且在重构后能够存活,因为它不关心内部结构。
10
+
11
+ 参见 `<Path>{roots.workflows}/specdev/I-implement/tdd-examples.md</Path>` 了解示例和 Mock 指南。
12
+
13
+ ## 接缝 —— 测试放置的位置
14
+
15
+ **接缝(seam)** 是你进行测试的公共边界:你在该接口处观察行为而不触及内部。测试位于接缝处,绝不针对内部细节。
16
+
17
+ **仅在预先约定的接缝处进行测试。** 在编写任何测试之前,写下要测试的接缝并与用户确认。没有在未确认的接缝处编写测试。你无法测试一切 —— 提前约定接缝可以确保测试工作集中在关键路径和复杂逻辑上,而不是每个边缘情况。
18
+
19
+ 问:"公共接口是什么,我们应该在哪些接缝处进行测试?"
20
+
21
+ ## 反模式
22
+
23
+ - **与实现耦合** —— mock 内部协作者、测试私有方法、或通过旁路通道验证(查询数据库而不是使用接口)。特征:当重构时代码行为未变但测试却失败了。
24
+ - **同义反复** —— 断言以与代码相同的方式重新计算预期值(`expect(add(a, b)).toBe(a + b)`、以相同方式手动推导的快照、将常量断言为等于自身),因此它在构造上就必然通过,永远不可能与代码产生分歧。预期值必须来自独立的真相来源 —— 已知正确的字面量、手工计算示例、规范。
25
+ - **水平切片** —— 先写所有测试,再写所有实现。批量测试验证的是*想象中*的行为:你测试的是事物的*形态*而非面向用户的行为,测试变得对真实变更不敏感,并且你在理解实现之前就锁定了测试结构。应采用**垂直切片** —— 一个测试 → 一个实现 → 重复,每个测试都是一颗**曳光弹**,响应上一个循环的反馈。
26
+
27
+ ## 循环的规则
28
+
29
+ - **先红后绿。** 先写失败的测试,然后只写足以通过测试的代码。不要预测未来的测试或添加推测性功能。
30
+ - **一次一个切片。** 每个循环一个接缝、一个测试、一个最小实现。
31
+ - **重构不属于循环。** 它属于审查阶段(参见 `<Path>{roots.workflows}/specdev/I-implement/code-review-process.md</Path>`),而不是红 → 绿实现循环的一部分。