@hunter-harness/workflow-harness 0.2.72 → 0.2.74

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 (206) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +3 -3
  3. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +14 -10
  7. package/harness/bundles/general/claude-code/harness-plan/checklist.md +49 -9
  8. package/harness/bundles/general/claude-code/harness-plan/reference.md +83 -33
  9. package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
  10. package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
  11. package/harness/bundles/general/claude-code/harness-review/SKILL.md +3 -3
  12. package/harness/bundles/general/claude-code/harness-run/SKILL.md +6 -6
  13. package/harness/bundles/general/claude-code/harness-run/checklist.md +4 -1
  14. package/harness/bundles/general/claude-code/harness-run/reference.md +3 -3
  15. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +5 -4
  16. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  17. package/harness/bundles/general/claude-code/harness-test/SKILL.md +4 -4
  18. package/harness/bundles/general/claude-code/harness-test/checklist.md +2 -0
  19. package/harness/bundles/general/claude-code/scripts/harness_archive.py +82 -1
  20. package/harness/bundles/general/claude-code/scripts/harness_context.py +270 -0
  21. package/harness/bundles/general/claude-code/scripts/harness_gate.py +71 -2
  22. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +25 -4
  23. package/harness/bundles/general/claude-code/scripts/harness_profile.py +45 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +8 -5
  25. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  26. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +3 -3
  27. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  28. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +14 -10
  31. package/harness/bundles/general/codebuddy/harness-plan/checklist.md +49 -9
  32. package/harness/bundles/general/codebuddy/harness-plan/reference.md +83 -33
  33. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
  34. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
  35. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +3 -3
  36. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +6 -6
  37. package/harness/bundles/general/codebuddy/harness-run/checklist.md +4 -1
  38. package/harness/bundles/general/codebuddy/harness-run/reference.md +3 -3
  39. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +5 -4
  40. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +4 -4
  42. package/harness/bundles/general/codebuddy/harness-test/checklist.md +2 -0
  43. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +82 -1
  44. package/harness/bundles/general/codebuddy/scripts/harness_context.py +270 -0
  45. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +71 -2
  46. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +25 -4
  47. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +45 -0
  48. package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +8 -5
  49. package/harness/bundles/general/codex/.harness-build.json +1 -1
  50. package/harness/bundles/general/codex/harness-archive/SKILL.md +3 -3
  51. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  52. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  53. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  54. package/harness/bundles/general/codex/harness-plan/SKILL.md +14 -10
  55. package/harness/bundles/general/codex/harness-plan/checklist.md +49 -9
  56. package/harness/bundles/general/codex/harness-plan/reference.md +83 -33
  57. package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
  58. package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
  59. package/harness/bundles/general/codex/harness-review/SKILL.md +3 -3
  60. package/harness/bundles/general/codex/harness-run/SKILL.md +6 -6
  61. package/harness/bundles/general/codex/harness-run/checklist.md +4 -1
  62. package/harness/bundles/general/codex/harness-run/reference.md +3 -3
  63. package/harness/bundles/general/codex/harness-submit/SKILL.md +5 -4
  64. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-test/SKILL.md +4 -4
  66. package/harness/bundles/general/codex/harness-test/checklist.md +2 -0
  67. package/harness/bundles/general/codex/scripts/harness_archive.py +82 -1
  68. package/harness/bundles/general/codex/scripts/harness_context.py +270 -0
  69. package/harness/bundles/general/codex/scripts/harness_gate.py +71 -2
  70. package/harness/bundles/general/codex/scripts/harness_ledger.py +25 -4
  71. package/harness/bundles/general/codex/scripts/harness_profile.py +45 -0
  72. package/harness/bundles/general/codex/scripts/harness_test_guard.py +8 -5
  73. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  74. package/harness/bundles/general/cursor/harness-archive/SKILL.md +3 -3
  75. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  76. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  77. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  78. package/harness/bundles/general/cursor/harness-plan/SKILL.md +14 -10
  79. package/harness/bundles/general/cursor/harness-plan/checklist.md +49 -9
  80. package/harness/bundles/general/cursor/harness-plan/reference.md +83 -33
  81. package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
  82. package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
  83. package/harness/bundles/general/cursor/harness-review/SKILL.md +3 -3
  84. package/harness/bundles/general/cursor/harness-run/SKILL.md +6 -6
  85. package/harness/bundles/general/cursor/harness-run/checklist.md +4 -1
  86. package/harness/bundles/general/cursor/harness-run/reference.md +3 -3
  87. package/harness/bundles/general/cursor/harness-submit/SKILL.md +5 -4
  88. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  89. package/harness/bundles/general/cursor/harness-test/SKILL.md +4 -4
  90. package/harness/bundles/general/cursor/harness-test/checklist.md +2 -0
  91. package/harness/bundles/general/cursor/scripts/harness_archive.py +82 -1
  92. package/harness/bundles/general/cursor/scripts/harness_context.py +270 -0
  93. package/harness/bundles/general/cursor/scripts/harness_gate.py +71 -2
  94. package/harness/bundles/general/cursor/scripts/harness_ledger.py +25 -4
  95. package/harness/bundles/general/cursor/scripts/harness_profile.py +45 -0
  96. package/harness/bundles/general/cursor/scripts/harness_test_guard.py +8 -5
  97. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  98. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +3 -3
  99. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +3 -3
  100. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  101. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  102. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  103. package/harness/bundles/java/claude-code/harness-package/SKILL.md +3 -3
  104. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +14 -10
  105. package/harness/bundles/java/claude-code/harness-plan/checklist.md +49 -9
  106. package/harness/bundles/java/claude-code/harness-plan/reference.md +83 -33
  107. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
  108. package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
  109. package/harness/bundles/java/claude-code/harness-review/SKILL.md +3 -3
  110. package/harness/bundles/java/claude-code/harness-run/SKILL.md +6 -6
  111. package/harness/bundles/java/claude-code/harness-run/checklist.md +1 -1
  112. package/harness/bundles/java/claude-code/harness-run/reference.md +348 -348
  113. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +5 -4
  114. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  115. package/harness/bundles/java/claude-code/harness-test/SKILL.md +4 -4
  116. package/harness/bundles/java/claude-code/scripts/harness_archive.py +82 -1
  117. package/harness/bundles/java/claude-code/scripts/harness_context.py +270 -0
  118. package/harness/bundles/java/claude-code/scripts/harness_gate.py +71 -2
  119. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +25 -4
  120. package/harness/bundles/java/claude-code/scripts/harness_profile.py +45 -0
  121. package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +8 -5
  122. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  123. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +3 -3
  124. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +3 -3
  125. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  126. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  127. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  128. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +3 -3
  129. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +14 -10
  130. package/harness/bundles/java/codebuddy/harness-plan/checklist.md +49 -9
  131. package/harness/bundles/java/codebuddy/harness-plan/reference.md +83 -33
  132. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
  133. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
  134. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +3 -3
  135. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +6 -6
  136. package/harness/bundles/java/codebuddy/harness-run/checklist.md +1 -1
  137. package/harness/bundles/java/codebuddy/harness-run/reference.md +348 -348
  138. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +5 -4
  139. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  140. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +4 -4
  141. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +82 -1
  142. package/harness/bundles/java/codebuddy/scripts/harness_context.py +270 -0
  143. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +71 -2
  144. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +25 -4
  145. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +45 -0
  146. package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +8 -5
  147. package/harness/bundles/java/codex/.harness-build.json +1 -1
  148. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +3 -3
  149. package/harness/bundles/java/codex/harness-archive/SKILL.md +3 -3
  150. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  151. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  152. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  153. package/harness/bundles/java/codex/harness-package/SKILL.md +3 -3
  154. package/harness/bundles/java/codex/harness-plan/SKILL.md +14 -10
  155. package/harness/bundles/java/codex/harness-plan/checklist.md +49 -9
  156. package/harness/bundles/java/codex/harness-plan/reference.md +83 -33
  157. package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
  158. package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
  159. package/harness/bundles/java/codex/harness-review/SKILL.md +3 -3
  160. package/harness/bundles/java/codex/harness-run/SKILL.md +6 -6
  161. package/harness/bundles/java/codex/harness-run/checklist.md +1 -1
  162. package/harness/bundles/java/codex/harness-run/reference.md +348 -348
  163. package/harness/bundles/java/codex/harness-submit/SKILL.md +5 -4
  164. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  165. package/harness/bundles/java/codex/harness-test/SKILL.md +4 -4
  166. package/harness/bundles/java/codex/scripts/harness_archive.py +82 -1
  167. package/harness/bundles/java/codex/scripts/harness_context.py +270 -0
  168. package/harness/bundles/java/codex/scripts/harness_gate.py +71 -2
  169. package/harness/bundles/java/codex/scripts/harness_ledger.py +25 -4
  170. package/harness/bundles/java/codex/scripts/harness_profile.py +45 -0
  171. package/harness/bundles/java/codex/scripts/harness_test_guard.py +8 -5
  172. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  173. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +3 -3
  174. package/harness/bundles/java/cursor/harness-archive/SKILL.md +3 -3
  175. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  176. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  177. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  178. package/harness/bundles/java/cursor/harness-package/SKILL.md +3 -3
  179. package/harness/bundles/java/cursor/harness-plan/SKILL.md +14 -10
  180. package/harness/bundles/java/cursor/harness-plan/checklist.md +49 -9
  181. package/harness/bundles/java/cursor/harness-plan/reference.md +83 -33
  182. package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
  183. package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
  184. package/harness/bundles/java/cursor/harness-review/SKILL.md +3 -3
  185. package/harness/bundles/java/cursor/harness-run/SKILL.md +6 -6
  186. package/harness/bundles/java/cursor/harness-run/checklist.md +1 -1
  187. package/harness/bundles/java/cursor/harness-run/reference.md +348 -348
  188. package/harness/bundles/java/cursor/harness-submit/SKILL.md +5 -4
  189. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  190. package/harness/bundles/java/cursor/harness-test/SKILL.md +4 -4
  191. package/harness/bundles/java/cursor/scripts/harness_archive.py +82 -1
  192. package/harness/bundles/java/cursor/scripts/harness_context.py +270 -0
  193. package/harness/bundles/java/cursor/scripts/harness_gate.py +71 -2
  194. package/harness/bundles/java/cursor/scripts/harness_ledger.py +25 -4
  195. package/harness/bundles/java/cursor/scripts/harness_profile.py +45 -0
  196. package/harness/bundles/java/cursor/scripts/harness_test_guard.py +8 -5
  197. package/harness/manifests/general/claude-code.json +26 -26
  198. package/harness/manifests/general/codebuddy.json +26 -26
  199. package/harness/manifests/general/codex.json +26 -26
  200. package/harness/manifests/general/cursor.json +26 -26
  201. package/harness/manifests/java/claude-code.json +27 -27
  202. package/harness/manifests/java/codebuddy.json +27 -27
  203. package/harness/manifests/java/codex.json +27 -27
  204. package/harness/manifests/java/cursor.json +27 -27
  205. package/hunter-workflow-family.json +5 -5
  206. package/package.json +1 -1
@@ -104,11 +104,16 @@ description: harness-plan 的需求提取模板、任务拆分规则、测试场
104
104
 
105
105
  > **本阶段是强制检查点。** 先展示设计审批包,收到确认并追加 decision 事件后,才能落盘 `status: approved` 的设计文档并进入阶段 6(任务拆分)。设计方向正确后再细化任务,避免基于错误理解拆分无效任务。
106
106
 
107
- **用户确认后必须立即写入** `.harness/changes/<change-name>/spec/<change-name>-design.md`。如果此文件不存在,harness-plan 不得进入阶段 6。
107
+ **用户确认后必须立即追加 decision 事件**,然后按路径分流落盘:
108
108
 
109
- **设计文档路径规则**:设计文档必须保存到 `.harness/changes/<change-name>/spec/<change-name>-design.md`。禁止保存到 `docs/superpowers/specs/` 作为正式产物;`/harness-plan` 不运行时调用 Superpowers。
109
+ | 路径 | 审批内容去哪 | 设计文档 |
110
+ |------|------------|---------|
111
+ | **v2**(默认) | `meta/plan-evidence-input.json` 的 `approval.content` + `approver_id` | `plans/<change-name>-design.md`,由 finalize 从审批内容派生——**不要手写**,手写的会被派生渲染覆盖 |
112
+ | **legacy** | 直接写文档 | `.harness/changes/<change-name>/spec/<change-name>-design.md`(不存在则不得进入阶段 6) |
110
113
 
111
- ### 设计文档模板
114
+ **设计文档路径规则**:禁止保存到 `docs/superpowers/specs/` 作为正式产物;`/harness-plan` 不运行时调用 Superpowers。同一 change 不得同时存在 `plans/` 与 `spec/` 两份设计——v2 发布的那份才受完整性门禁保护。
115
+
116
+ ### 设计文档模板(legacy 路径手写时使用;v2 由 finalize 派生,此模板仅作内容清单参考)
112
117
 
113
118
  ```markdown
114
119
  ---
@@ -181,15 +186,19 @@ source: harness-plan
181
186
 
182
187
  ### 产物结构
183
188
 
184
- 推荐结构:
185
-
186
189
  ```
187
190
  .harness/changes/<change-name>/plans/
191
+ ├── <change-name>-design.md # 设计(v2 由 finalize 派生)
188
192
  ├── <change-name>-plan.md # harness 简洁任务表,run 默认读取
189
193
  ├── <change-name>-implementation-detail.md # 原生自适应详细执行参考,run 补充读取
190
194
  └── <change-name>-test-scenarios.md # 测试场景表
191
195
  ```
192
196
 
197
+ > **v2 路径下这四份都是派生产物**:唯一手写的是 `meta/plan-evidence-input.json`。
198
+ > 阶段 6 的任务拆分结果直接填进它的 `structured_input.tasks`,阶段 7 的场景填 `structured_input.scenarios`——
199
+ > 同一份内容不要先写成 Markdown 再誊进 JSON,finalize 会用派生渲染覆盖手写的 Markdown。
200
+ > 下面的 Markdown 格式说明用于**理解字段语义**与 legacy 路径手写。
201
+
193
202
  ### 计划文件 frontmatter(必须)
194
203
 
195
204
  ```yaml
@@ -284,10 +293,12 @@ status: approved
284
293
  .harness/changes/<change-name>/backups/
285
294
  ```
286
295
 
287
- 3. **保存设计文档**:将阶段 4 已确认的设计文档保存到:
288
- - `.harness/changes/<change-name>/spec/<change-name>-design.md`
296
+ 3. **保存设计文档**:
297
+ - **v2**:不手写文档;把审批内容填进 `meta/plan-evidence-input.json` 的 `approval.content`,
298
+ `plans/<change-name>-design.md` 由 finalize 派生(frontmatter 也由渲染器写)
299
+ - **legacy**:保存到 `.harness/changes/<change-name>/spec/<change-name>-design.md`
289
300
 
290
- 设计文档 frontmatter 格式:
301
+ legacy 设计文档 frontmatter 格式:
291
302
  ```yaml
292
303
  ---
293
304
  change-name: <change-name>
@@ -299,14 +310,17 @@ status: approved
299
310
 
300
311
  > 如果 frontmatter 缺失,后续 run/test/review/submit/archive 不得依赖模型猜测 change-name。
301
312
 
302
- 4. **初始化结构化事件**:确定 change-name 后,立即生成稳定的 `<plan-run-id>`(必须小写字母开头:v2 identity 规则,裸 UUID 有 10/16 概率数字开头被拒——统一用 `plan_<uuid>` 形状,contracts `createPlanRunId()`;同一次 plan 尝试内不得改变;首次 `<attempt>` `1`),运行 `harness_events.py append --change-dir ... --phase plan --type phase.start --run-id <plan-run-id> --attempt <attempt>`。finalizer 必须复用完全相同的 `--run-id` / `--attempt`,否则 verify 会按生命周期身份 fail-closed。脚本负责建立父目录和 `events.ndjson`;执行日志在 `phase.end` 时由完整事件流渲染,任何阶段都不得直接用 Write/Edit 维护该投影。
313
+ 4. **初始化结构化事件**:由阶段 0.5 的 `harness_context.py bootstrap-plan` 一次完成——它生成合规的 `<plan-run-id>`(`plan_<uuid>` 形状,必须小写字母开头:v2 identity 规则,裸 UUID 有 10/16 概率数字开头被拒)、`<attempt>`(首次为 `1`)并追加 `phase.start`,重跑复用同一身份不重复写事件。finalizer 必须复用引导返回的 `runId`/`attempt`,否则 verify 会按生命周期身份 fail-closed。同一次 plan 尝试内不得改变身份。执行日志在 `phase.end` 时由完整事件流渲染,任何阶段都不得直接用 Write/Edit 维护该投影。
303
314
 
304
- 5. **保存计划文件**:计划文件包含 YAML frontmatter(含 change-name),保存到:
305
- - `.harness/changes/<change-name>/plans/<change-name>-plan.md`(简洁任务表)
306
- - `.harness/changes/<change-name>/plans/<change-name>-implementation-detail.md`(自适应详细执行参考)
307
- - `.harness/changes/<change-name>/plans/<change-name>-test-scenarios.md`(测试场景表)
315
+ 5. **保存计划文件**:
316
+ - **v2**:不手写;任务填 `structured_input.tasks`、场景填 `structured_input.scenarios`,
317
+ `plans/` 下四份 Markdown 全部由 finalize 派生
318
+ - **legacy**:手写并保存到(含 YAML frontmatter,含 change-name
319
+ - `.harness/changes/<change-name>/plans/<change-name>-plan.md`(简洁任务表)
320
+ - `.harness/changes/<change-name>/plans/<change-name>-implementation-detail.md`(自适应详细执行参考)
321
+ - `.harness/changes/<change-name>/plans/<change-name>-test-scenarios.md`(测试场景表)
308
322
 
309
- 计划文件 frontmatter 格式:
323
+ legacy 计划文件 frontmatter 格式:
310
324
  ```yaml
311
325
  ---
312
326
  change-name: <change-name>
@@ -327,23 +341,27 @@ status: approved
327
341
 
328
342
  ## 阶段 8:结束前产物完整性检查 ⚠️ 强制
329
343
 
330
- > **缺任一文件 → ❌FAIL,不得宣称 plan 完成。**
331
-
332
- | 文件 | 必须存在 |
333
- |------|:---:|
334
- | `.harness/changes/<change>/spec/<change>-design.md` | ✅ |
335
- | `.harness/changes/<change>/plans/<change>-plan.md` | |
336
- | `.harness/changes/<change>/plans/<change>-implementation-detail.md` | ✅ |
337
- | `.harness/changes/<change>/plans/<change>-test-scenarios.md` | ✅ |
338
- | `.harness/changes/<change>/meta/gate-policy.json` | ✅ |
339
- | `.harness/changes/<change>/meta/worktree.json` | ✅ |
340
- | `.harness/changes/<change>/meta/implementation-checkpoints.json` | ✅ |
341
- | `.harness/changes/<change>/meta/scenario-manifest.json` | ✅ |
342
- | `.harness/changes/<change>/meta/plan-finalization.json` | ✅ |
343
- | `.harness/changes/<change>/logs/execution-log.md` | ✅ |
344
- | `.harness/changes/<change>/events.ndjson` | ✅ |
345
-
346
- `plan-finalization.json.files` 必须完整列出 design、plan、implementation-detail、test-scenarios、gate-policy、worktree 六项标准输入。`verify` 对缺项、重复项、越界路径以及 symlink/junction/reparse point 一律 fail-closed;不得通过删减收据文件集后重算哈希来绕过完整性检查。
344
+ > **缺任一文件 → ❌FAIL,不得宣称 plan 完成。先认清走的是 v2 还是 legacy——两条路径的必需文件集不同,拿 legacy 的表去查 v2 会得出假失败。**
345
+
346
+ | 文件 | v2 | legacy |
347
+ |------|:---:|:---:|
348
+ | `.harness/changes/<change>/meta/plan-evidence-input.json` | ✅ | — |
349
+ | `.harness/changes/<change>/plans/<change>-design.md` | ✅(派生) | — |
350
+ | `.harness/changes/<change>/spec/<change>-design.md` | — | ✅ |
351
+ | `.harness/changes/<change>/plans/<change>-plan.md` | ✅(派生) | ✅ |
352
+ | `.harness/changes/<change>/plans/<change>-implementation-detail.md` | ✅(派生) | ✅ |
353
+ | `.harness/changes/<change>/plans/<change>-test-scenarios.md` | ✅(派生) | ✅ |
354
+ | `.harness/changes/<change>/meta/gate-policy.json` | ✅ | ✅ |
355
+ | `.harness/changes/<change>/meta/worktree.json` | ✅ | ✅ |
356
+ | `.harness/changes/<change>/meta/implementation-checkpoints.json` | ✅ | ✅ |
357
+ | `.harness/changes/<change>/meta/scenario-manifest.json` | ✅ | ✅ |
358
+ | `.harness/changes/<change>/meta/publication-journals/<op>.json`(committed) | ✅ | — |
359
+ | `.harness/changes/<change>/meta/plan-events.ndjson` | ✅ | — |
360
+ | `.harness/changes/<change>/meta/plan-finalization.json` | | |
361
+ | `.harness/changes/<change>/logs/execution-log.md` | — | ✅ |
362
+ | `.harness/changes/<change>/events.ndjson` | ✅ | ✅ |
363
+
364
+ legacy 的 `plan-finalization.json.files` 必须完整列出 design、plan、implementation-detail、test-scenarios、gate-policy、worktree 六项标准输入。`verify` 对缺项、重复项、越界路径以及 symlink/junction/reparse point 一律 fail-closed;不得通过删减收据文件集后重算哈希来绕过完整性检查。
347
365
 
348
366
  ### 阶段 8 v2 路径(结构化证据包流程,新 change 优先)
349
367
 
@@ -363,8 +381,32 @@ npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidenc
363
381
  > ```bash
364
382
  > npx hunter-harness plan evidence-pack --print-template > .harness/changes/<cn>/meta/plan-evidence-input.json
365
383
  > ```
366
- > 输出是带 `<...>` 占位符的完整骨架,逐项替换即可;替换完 grep 一次 `<` 自检有无遗漏。
367
- > `--print-template` 不读写任何文件,只打到 stdout。
384
+ > **骨架一个字不改就能通过 `evidence-pack`**(回归测试冻结这条不变量),所以可以先跑一次确认链路通,再逐项替换。
385
+ > 注意这只保证结构合法:`finalize` 还要求 `change_key` 与 `run_id` 是本次真实身份,占位值过不了发布。
386
+ > 自由文本字段用 `<...>` 占位,替换完 grep 一次 `<` 自检有无遗漏。`--print-template` 不读写任何文件,只打到 stdout。
387
+
388
+ **带不了 `<>` 的占位字段**(受枚举/哈希/命名约束,grep `<` 查不出来,必须逐个确认):
389
+
390
+ | 字段 | 模板占位值 | 换成什么 |
391
+ |------|-----------|---------|
392
+ | `change_key` | `replace-with-change-name` | 真实 change-name(kebab-case:`^[a-z0-9]+(-[a-z0-9]+)*$`) |
393
+ | `context.run_id` | `plan_replace-with-your-plan-run-id` | 阶段 0.5 生成、`phase.start` 已用的**同一个** plan-run-id |
394
+ | `evidence_sources[].content_hash` | `sha256:deadbeef…` | 证据源内容的真实 sha256(校验器显式拒绝全 0) |
395
+ | `risk_signals` | `["production_code"]` | classify 实际返回的信号 |
396
+
397
+ **容易踩的硬约束**(违反时命令会给 `field_path`,不必再猜):
398
+
399
+ - `structured_input.scenarios` **至少 3 条**——八维度缺项由命令补 `not_applicable`,但场景总数不能少于 3
400
+ - `intent.acceptance_examples` 2~5 条,`approval.content.acceptance_examples` 3~7 条 → 取 3 条同时满足
401
+ - `key_alternatives` / `invariants` / `failure_behaviors` / `compatibility_boundaries` 各至少 1 条
402
+ - `intent.in_scope`/`out_of_scope` 与 `approval.content` 同名字段必须**集合相等**
403
+ - tasks 只写 `task_id/objective/affected_paths/owner_phase`,六个 refs 数组由命令接线;多写 `cluster`/`title` 这类键会因精确键集被拒
404
+ - scenarios 只写 `scenario_id/title/acceptance/coverage_dimension/execution_level/evidence_requirements/risk_level`(+可选 `verification_command`);`priority`/`test_file` 这类计划表列不属于本输入
405
+ - `machine.worktree_policy` ∈ `project_default | required | forbidden`(没有 `none`)
406
+
407
+ > **结构错了怎么读报错**:命令在边界返回 `code:"PLAN_EVIDENCE_INPUT_INVALID"`(`stage:"boundary"`),
408
+ > `field_path` 指向第一处问题,`problems[]` 逐条给 `missing_keys`/`unexpected_keys`/`message`。
409
+ > 按 `problems` 改完重跑即可——**不需要**去反编译 `dist/bin.js` 或翻 npx 缓存找校验器。
368
410
 
369
411
  | 字段 | 内容 | 定稿阶段 |
370
412
  |------|------|:---:|
@@ -380,6 +422,14 @@ npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidenc
380
422
  | `context` | project_id/run_id/branch_name/attempt(复用 plan-run-id 与 attempt) | 0.5 |
381
423
  | `expected_baseline` | 首次发布 `{state:"absent", manifest_hash:null, generation:0}` | 8 |
382
424
 
425
+ > ⚠️ **已知缺口:v2 派生的 `meta/scenario-manifest.json` 目前喂不了 run/test 门禁。**
426
+ > 它是 artifact 包装体,每条场景只有 `scenario_id/coverage_dimension/execution_level/
427
+ > evidence_requirements/risk_level/task_refs/requirement_refs`;而门禁按
428
+ > `id/priority/requiredEvidenceKind/ownerPhase/executableTestId/testFile/testTitle`
429
+ > 判定哪些场景需要 ledger 证据。缺 `priority` 与 `requiredEvidenceKind` 时"必需场景"会算成空集,
430
+ > 所以门禁**明确报 `SCENARIO_MANIFEST_V2_UNSUPPORTED` 并列出 `missingFields`,绝不静默放行**。
431
+ > 需要 ledger 证据闭环的变更,在 v2 场景契约补齐这些字段前请走 legacy 路径。
432
+
383
433
  - **证据包**(`plan-evidence.json`)是命令推导的产物(trusted/publication/context/baseline),不得手改;任何字段变化必须改自然输入后重跑 evidence-pack。
384
434
  - **成功语义**:finalize exit 0 且 `code:"PLAN_FINALIZED"`。落盘事实 = 八 target(plans/*.md ×4 + meta/*.json ×4)+ `meta/publication-journals/<op>.json`(状态 committed)+ `meta/plan-events.ndjson`(artifact_published/phase_ended)。确定性门失败 exit 1 且 `code:"PLAN_FINALIZE_DETERMINISTIC_FAILED"` 附 findings——此时必须回到对应阶段修正规划内容,**不得**手改证据包或 staged 内容绕过。
385
435
  - **验证**:journal `state==="committed"` + 八 target 存在 + plan-events.ndjson 含两类终态事件;不得手工补写任何一项。
@@ -3,7 +3,7 @@ name: harness-pull
3
3
  description: 从 Hunter Platform 下拉配置/规则/架构/指令(及显式来源分支的分支文件恢复)。仅当用户显式调用
4
4
  /harness-pull 或明确说'从平台拉取/恢复'时使用;不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
7
7
  # harness-pull — 从 Hunter Platform 下拉与恢复
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-push
3
3
  description: 上传本地配置/规则/架构/指令(及显式归档)到 Hunter Platform。仅当用户显式调用 /harness-push
4
4
  或明确说'上传到平台'时使用;不得因存在本地修改就自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
7
7
  # harness-push — 上传到 Hunter Platform
8
8
 
9
9
  ## Purpose
@@ -4,7 +4,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。仅当用户显式调用 /harness-review 时使用;不得在
5
5
  test 结束后自动接续执行。
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
8
8
  # harness-review — 代码审查
9
9
 
10
10
  ## Purpose
@@ -31,9 +31,9 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
31
31
  ## 统一读取协议
32
32
 
33
33
  1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
34
- 2. **change-name 优先从 frontmatter 读取** — `spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
34
+ 2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
35
35
  3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
36
- 4. **spec** — 设计真相源:`spec/<change>-design.md`
36
+ 4. **design** — 设计真相源按序取第一个存在的:`plans/<change>-design.md`(v2 发布产物,哈希绑定)→ `spec/<change>-design.md`(legacy 手写)。两份**同时存在**时以 `plans/` 为准,并记 `🟡 WARN 设计文档双份`——v2 发布的那份才受完整性门禁保护,读手写的那份等于绕过校验
37
37
  5. **plan** — 任务真相源:`plans/<change>-plan.md`
38
38
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
39
39
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
@@ -3,7 +3,7 @@ name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。仅当用户显式调用
4
4
  /harness-run 时使用;不得因用户提到编码/实现就自动触发,也不得被其他阶段 skill 自动接续。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -18,7 +18,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
18
18
 
19
19
  ## 前置条件
20
20
 
21
- - `spec/*-design.md`、`plans/*-plan.md`(含 frontmatter)存在且已审批
21
+ - 设计文档(`plans/*-design.md` 优先,回退 `spec/*-design.md`)与 `plans/*-plan.md`(含 frontmatter)存在且已审批
22
22
  - 读 `meta/worktree.json`:`requested=true` 时 worktree 须存在或 run 负责创建
23
23
 
24
24
  ## Worktree 门禁
@@ -41,9 +41,9 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
41
41
  ## 统一读取协议
42
42
 
43
43
  1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
44
- 2. **change-name 优先从 frontmatter 读取** — `spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
44
+ 2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
45
45
  3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
46
- 4. **spec** — 设计真相源:`spec/<change>-design.md`
46
+ 4. **design** — 设计真相源按序取第一个存在的:`plans/<change>-design.md`(v2 发布产物,哈希绑定)→ `spec/<change>-design.md`(legacy 手写)。两份**同时存在**时以 `plans/` 为准,并记 `🟡 WARN 设计文档双份`——v2 发布的那份才受完整性门禁保护,读手写的那份等于绕过校验
47
47
  5. **plan** — 任务真相源:`plans/<change>-plan.md`
48
48
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
49
49
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
@@ -59,10 +59,10 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
59
59
 
60
60
  ## Workflow 概要
61
61
 
62
- 0. 加载上下文:普通 Run 先 `harness_context.py prepare --phase run --executor <tool> [--change <id>] --json`,再 **`harness_context.py begin --phase run --change <id> --executor <tool> --json`** 校验交接,最后运行 **`harness_gate.py begin --phase run --change <id>`**。`harness_gate.py` 会从当前已安装适配器自动识别 skills root;只有执行复制到别处的脚本时才显式传 `--skills-root`。`--fixback` 不得拼装这些底层步骤,必须只调用一次 `harness_fixback.py launch-review --project . --change <id> --change-dir <change-dir> --executor <tool> --skills-root <skills-root> --product-identity <当前产品身份> --json`:该命令会先筛选结构化评审项,再原子选择 Fixback 分支、确认上下文、取得 Run 门禁并创建已填充批次。返回 `FIXBACK_NOTHING_TO_APPLY` 时直接报告“本轮评审没有需要执行的代码修复”并停止;返回阻塞码时按 `recoveryAction` 停止,不搜索实现、不试探其他参数、不创建空批次。禁止手写 `events.ndjson` / `phase.end`。已连接平台时 begin 会 best-effort 补传事件,失败只告警。
62
+ 0. 加载上下文:普通 Run 先 `harness_context.py prepare --project . --change <id> --phase run --executor <tool> --json`(`--project` 必填,漏了直接 argparse 报错),再 **`harness_context.py begin --project . --change <id> --phase run --executor <tool> --json`** 校验交接,最后运行 **`harness_gate.py begin --phase run --change <id>`**。<br>**交接凭证不必手工补**:v2 计划的 `plan finalize` 不写 context 事务,`prepare` 会在检测到 committed 的 `meta/publication-journals/*.json` 时自动补录 `plan → run` 凭证(凭证带 `bootstrapSource=plan_publication_journal` 留痕)。仍报 `HANDOFF_REQUIRED`/`LEGACY_BOOTSTRAP_REQUIRED` 说明**没有**这份发布证据——回到 plan 阶段确认发布是否真的完成,**不得**自己拼 `classify + configure-plan + close` 造凭证。`harness_gate.py` 会从当前已安装适配器自动识别 skills root;只有执行复制到别处的脚本时才显式传 `--skills-root`。`--fixback` 不得拼装这些底层步骤,必须只调用一次 `harness_fixback.py launch-review --project . --change <id> --change-dir <change-dir> --executor <tool> --skills-root <skills-root> --product-identity <当前产品身份> --json`:该命令会先筛选结构化评审项,再原子选择 Fixback 分支、确认上下文、取得 Run 门禁并创建已填充批次。返回 `FIXBACK_NOTHING_TO_APPLY` 时直接报告“本轮评审没有需要执行的代码修复”并停止;返回阻塞码时按 `recoveryAction` 停止,不搜索实现、不试探其他参数、不创建空批次。禁止手写 `events.ndjson` / `phase.end`。已连接平台时 begin 会 best-effort 补传事件,失败只告警。
63
63
  0.5. **测试基础设施探测**(先写 `CHECKING`,四项证据齐备后再结论)→ `reference.md` Step 0.5;测试基线已由上一步 gate begin 内部建立,不得再次执行 guard begin
64
64
  1. **变更簇 TDD** — `protocols.md` `run-tdd-protocol`;批量 RED/GREEN;按需 `change-cluster-review-protocol`(高风险 + reviewer 预检可用)
65
- 2. 构建验证 + **仅**通过 `harness_ledger.py record` 写 ledger(禁止 Write/Edit `verification-ledger.json`);若本阶段创建/删除了清单、锁文件、源码根或改变技术栈,最后一次产品编辑后、写账本与关门前必须执行 `harness_preflight.py detect --project . --json` 刷新 build profile。`record --project . --profile-input <key>` 会对缺失/陈旧 profile 自动检测并从同一 target 推导 scope、coverage、规范命令和输入闭包;不得再手填另一套身份。`diff-hash --change-dir` 纳入 ignored tests → `reference.md` Step 2c
65
+ 2. 构建验证 + **仅**通过 `harness_ledger.py record` 写 ledger(禁止 Write/Edit `verification-ledger.json`);若本阶段创建/删除了清单、锁文件、源码根或改变技术栈,最后一次产品编辑后、写账本与关门前必须执行 `harness_preflight.py detect --project . --json` 刷新 build profile。`record --project . --profile-input <key>` 会对缺失/陈旧 profile 自动检测并从同一 target 推导 scope、coverage、规范命令和输入闭包;不得再手填另一套身份。detect 在嵌套/多组件仓库返回 `DETECTION_AMBIGUOUS` 时,响应里的 `profileTemplate` 就是可填骨架(已含 defaultsFingerprint、excludedRoots 默认集与 commands 条目形状),按 `hint` 填好写入 `.harness/config/build-profile.json` 即可,**不要去反读 `harness_profile.py` 源码凑结构**。`diff-hash --change-dir` 纳入 ignored tests → `reference.md` Step 2c
66
66
  3. **场景覆盖检查**(场景表映射,禁止用用例数冒充场景数)
67
67
  4. **关门检查**(10 项)→ 只执行一次 `harness_gate.py close`;`--to-phase` 必须取返回的 `nextPhases` 或 `plannedPhases` 中 run 的真实后继,禁止写死 test。该命令内部关闭 test guard、写 `phase.end`、释放租约、写 handoff 并补传事件;不得再单独调用 test-guard/context close。失败时按结构化 `recoveryAction` 原样重试,已完成步骤幂等复用。
68
68
 
@@ -29,7 +29,7 @@ description: harness-run 的执行检查清单。仅在编码执行时读取。
29
29
  - [ ] 读取并执行 `meta/worktree.json`:如果 `requested=true` 必须创建/切换 worktree,创建失败则停止或询问用户改为主目录;禁止静默降级
30
30
  - [ ] **读取计划文件(主任务源)**:`.harness/changes/<change>/plans/<change>-plan.md` — 获取任务列表和依赖关系
31
31
  - [ ] **读取详细计划(补充参考)**:`.harness/changes/<change>/plans/<change>-implementation-detail.md`(新版必需;legacy 缺失时 🟡WARN)
32
- - [ ] **读取设计文档**:`.harness/changes/<change>/spec/<change>-design.md` 获取核心设计决策和不变项
32
+ - [ ] **读取设计文档**:`.harness/changes/<change>/plans/<change>-design.md`(v2);不存在时回退 `spec/<change>-design.md`(legacy)— 获取核心设计决策和不变项
33
33
  - [ ] **读取测试场景表**:`.harness/changes/<change>/plans/<change>-test-scenarios.md` — 获取与当前任务相关的测试场景
34
34
  - [ ] **读取验证账本**:通过 context 返回的 `executionRoot` 读取 `evidence/verification-ledger.json`(如存在)— 复用已有 compile/unitTest 结果
35
35
  - [ ] **读取任务状态**:`.harness/changes/<change>/evidence/run-task-status.md`(如存在)— 恢复上次运行状态
@@ -39,7 +39,10 @@ description: harness-run 的执行检查清单。仅在编码执行时读取。
39
39
  - [ ] 检查构建配置完整性(worktree 中确认构建配置文件存在,如 Java 的 `.mvn/maven.config`、`settings.xml`,前端的 `package.json`/lockfile 等)
40
40
  - [ ] 依赖模块预安装(worktree 中检查上游依赖是否已安装,如 Java 的 `mvn install`、前端的 `npm install`/lockfile 等)
41
41
  - [ ] 代码探索优先用 `codegraph_explore`,仅在返回不完整时补充 Read
42
+ - [ ] `harness_context.py prepare/begin` 均带 `--project .` 与 `--change <id>`(缺 `--project` 会被 argparse 直接拒)
43
+ - [ ] 交接凭证缺失时**不自行拼造**:v2 计划由 `prepare` 依据 committed 发布 journal 自动补录;仍报 `HANDOFF_REQUIRED`/`LEGACY_BOOTSTRAP_REQUIRED` 即代表发布证据不存在,回 plan 阶段查,不得用 `classify + configure-plan + close` 现编凭证
42
44
  - [ ] `harness_gate.py begin --phase run` 已返回 Plan handoff 校验通过并自动 append `phase.start`;不得手工写事件绕过
45
+ - [ ] 门禁报 `SCENARIO_MANIFEST_V2_UNSUPPORTED` 时按 `missingFields` 回规划阶段补齐场景字段后重新发布;**不得**手改 `meta/scenario-manifest.json`(派生产物,手改必致哈希漂移)
43
46
 
44
47
  ### 步骤 0.1:执行模式(无询问)
45
48
 
@@ -19,7 +19,7 @@ description: harness-run 的编译失败策略表、TDD循环详细步骤和编
19
19
 
20
20
  ## 前置条件
21
21
 
22
- - `.harness/changes/<change-name>/spec/<change-name>-design.md` 存在(含完整 frontmatter)
22
+ - 设计文档存在:`plans/<change-name>-design.md`(v2)或 `spec/<change-name>-design.md`(legacy,含完整 frontmatter),按 `shared/read-protocol.md` 的顺序取第一个
23
23
  - `.harness/changes/<change-name>/plans/<change-name>-plan.md` 存在(含完整 frontmatter)
24
24
  - `.harness/changes/<change-name>/plans/<change-name>-test-scenarios.md` 存在
25
25
  - `.harness/changes/<change-name>/plans/<change-name>-implementation-detail.md`(新版必需,legacy 缺失时 🟡WARN)
@@ -90,7 +90,7 @@ requested=true + path missing
90
90
  2. **读取并执行 worktree 决策**:读取 `.harness/changes/<change-name>/meta/worktree.json`。如果 `requested=false`,在主目录执行;如果 `requested=true` 且 worktree 存在,必须 cd 到该 worktree;如果 `requested=true` 且 worktree 不存在,必须创建 worktree,创建失败则停止或询问用户是否改为主目录执行。禁止静默降级。
91
91
  3. **读取计划文件(主任务源)**:`.harness/changes/<change-name>/plans/<change-name>-plan.md` → 获取任务列表和依赖关系
92
92
  4. **读取详细计划(补充参考)**:`.harness/changes/<change-name>/plans/<change-name>-implementation-detail.md`(新版必需,legacy 缺失时 🟡WARN)→ 获取自适应执行参考
93
- 5. **读取设计文档**:`.harness/changes/<change-name>/spec/<change-name>-design.md` 获取核心设计决策和不变项
93
+ 5. **读取设计文档**:`.harness/changes/<change-name>/plans/<change-name>-design.md`(v2 发布产物)→ 不存在时回退 `spec/<change-name>-design.md`(legacy)→ 获取核心设计决策和不变项
94
94
  6. **读取测试场景表**:`.harness/changes/<change-name>/plans/<change-name>-test-scenarios.md` → 获取测试真相源
95
95
  7. **读取验证账本**:通过 state layout resolver 定位 `evidence/verification-ledger.json`(如存在)→ 复用已有 compile/unitTest 结果
96
96
  8. **读取任务状态**:`.harness/changes/<change-name>/evidence/run-task-status.md`(如存在)→ 恢复上次运行状态
@@ -113,7 +113,7 @@ requested=true + path missing
113
113
 
114
114
  ```
115
115
  检查逻辑:
116
- 1. 读取 .harness/changes/<change-name>/spec/<change-name>-design.md
116
+ 1. 读取 .harness/changes/<change-name>/plans/<change-name>-design.md(不存在则回退 spec/<change-name>-design.md
117
117
  2. 读取 .harness/changes/<change-name>/plans/<change-name>-plan.md
118
118
  3. 读取 .harness/changes/<change-name>/plans/<change-name>-implementation-detail.md(legacy 缺失时 🟡WARN)
119
119
  4. 读取 .harness/changes/<change-name>/plans/<change-name>-test-scenarios.md
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff 合并回主分支。仅当用户显式调用
4
4
  /harness-submit(或 /harness-merge 重入合并段)时使用;用户口头说'提交/commit/push'时必须先确认,不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose
@@ -30,9 +30,9 @@ description: 最终提交封装:验证→中文 commit→提交/推送;workt
30
30
  ## 统一读取协议
31
31
 
32
32
  1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
33
- 2. **change-name 优先从 frontmatter 读取** — `spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
33
+ 2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
34
34
  3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
35
- 4. **spec** — 设计真相源:`spec/<change>-design.md`
35
+ 4. **design** — 设计真相源按序取第一个存在的:`plans/<change>-design.md`(v2 发布产物,哈希绑定)→ `spec/<change>-design.md`(legacy 手写)。两份**同时存在**时以 `plans/` 为准,并记 `🟡 WARN 设计文档双份`——v2 发布的那份才受完整性门禁保护,读手写的那份等于绕过校验
36
36
  5. **plan** — 任务真相源:`plans/<change>-plan.md`
37
37
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
38
38
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
@@ -54,10 +54,11 @@ worktree 合并前必须运行 `harness_change.py integration-lock acquire --run
54
54
 
55
55
  ### 提交流程(步骤 0–7)
56
56
 
57
- 0. **启动准备** — `harness_context.py prepare --phase submit --executor <tool> [--change <id>] --json` 确定唯一变更与 executionRoot;`harness_context.py begin --phase submit --change <id> --executor <tool> --json` 校验 review→submit receipt;**`harness_gate.py begin --phase submit --change <id>`**;读 ledger,以 `harness_ledger.py diff-hash --repo <executionRoot> --base <baseCommit> --change-dir ".harness/changes/<change-name>" --json` 计算 diffHash + post-test 7 类分类(`executionRoot` 必须直接取准备回执;禁止手写 ledger / 手工 phase.end)
57
+ 0. **启动准备** — `harness_context.py prepare --project . --change <id> --phase submit --executor <tool> --json`(`--project` 必填)确定唯一变更与 executionRoot;`harness_context.py begin --project . --change <id> --phase submit --executor <tool> --json` 校验 review→submit receipt;**`harness_gate.py begin --phase submit --change <id>`**;读 ledger,以 `harness_ledger.py diff-hash --repo <executionRoot> --base <baseCommit> --change-dir ".harness/changes/<change-name>" --json` 计算 diffHash + post-test 7 类分类(`executionRoot` 必须直接取准备回执;禁止手写 ledger / 手工 phase.end)
58
58
  1. **合并最新代码** — 主目录与 worktree 均**不在业务工作区 stash/pull**;远端同步由合并段 integration transaction 在隔离 integration worktree 内完成(见「worktree 合并流程」);**正常路径禁止 `git stash` / `stash pop`**
59
59
  2. **最终验证** — ledger 复用优先;提交前只调用 `can-reuse --project . --profile-input unitTestFull --command <profile 规范命令>`。不得传入另一套 `--files`,不得把 runner 包装说明写进 `command`。`reuse=true` 时禁止重跑;只有真实输入、依赖、工具链或环境身份变化时才执行一次同一 profile 验证,并只登记一条结果。不要读取 ledger/archive 实现源码或临时编写散列脚本排查参数。
60
60
  - 无远端 CI 且 gate-policy 未强制 remote provider 时,验证通过/复用后运行 `harness_archive.py certify-local --change-dir ... --project . --json`,从同一 ledger 生成 `local-reproducible` 产品候选收据;该命令不执行测试。
61
+ - **被与本变更无关的预存失败卡住时的正规出路**:用 `harness_preflight.py record-quirk --project . --action skip-not-block --pattern "<具体错误签名,≥8 字符>" --reason "<为什么与本变更无关>"` 把它声明进 build-profile 的 `knownPreexistingErrors`,certify-local 会在**该失败的 ledger 证据里确实出现该签名**时放行,并把 `{validation, pattern, reason}` 写进收据的 `verification.preexistingExemptions` 留痕。**不得**改 gate-policy 的 `candidateVerification.requiredValidations` 来绕过——那是把门禁本身拆掉。声明了但证据对不上、或签名短到能匹配一切,仍然阻断。
61
62
  - gate-policy 要求 `remote-attested` 时不得降级成本地收据,等待远端 attestation。
62
63
  3. **.gitignore + 精确暂存** ⚠️ — 检查 `.harness/` 在 `.gitignore`;**禁止 `git add -A`**。若存在 `evidence/test-tracking.json`,先执行 `python <skills-root>/scripts/harness_test_guard.py stage --project . --change-dir ".harness/changes/<change-name>" --json`;失败即硬停止。无 manifest 时不使用 `-f`。manifest 之外的文件按精确业务路径正常暂存,**禁止全局 force-add**。
63
64
  4. **提交方式** — 主目录:blocking user confirmation 三选项(commit+push / 仅本地 / 取消);**worktree:固定仅本地 commit**
@@ -4,7 +4,7 @@ description: Use when the user asks to synchronize, refresh, or validate Harness
4
4
  metadata, adapters, remote knowledge ownership, instruction entrypoints,
5
5
  config origins, or CodeGraph status.
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
8
8
  # harness-sync
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-test
3
3
  description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。仅当用户显式调用 /harness-test
4
4
  时使用;不得在 run 结束后自动接续执行。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=4239bfb29d5056eb; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=ecbe1a870731d9da; overlay=none; agent=codex; do not edit -->
7
7
  # harness-test — 测试执行
8
8
 
9
9
  ## Purpose
@@ -32,9 +32,9 @@ description: 测试执行:读取场景表,执行单元测试+API接口测试
32
32
  ## 统一读取协议
33
33
 
34
34
  1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
35
- 2. **change-name 优先从 frontmatter 读取** — `spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
35
+ 2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
36
36
  3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
37
- 4. **spec** — 设计真相源:`spec/<change>-design.md`
37
+ 4. **design** — 设计真相源按序取第一个存在的:`plans/<change>-design.md`(v2 发布产物,哈希绑定)→ `spec/<change>-design.md`(legacy 手写)。两份**同时存在**时以 `plans/` 为准,并记 `🟡 WARN 设计文档双份`——v2 发布的那份才受完整性门禁保护,读手写的那份等于绕过校验
38
38
  5. **plan** — 任务真相源:`plans/<change>-plan.md`
39
39
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
40
40
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
@@ -85,7 +85,7 @@ Runner 强制同项目单实例、低调度优先级、逐命令超时、正常
85
85
 
86
86
  ### Phase 0:环境准备(主会话执行,需要交互确认)
87
87
 
88
- 先 `harness_context.py prepare --phase test --executor <tool> [--change <id>] --json`,再 `harness_context.py begin --phase test --change <id> --executor <tool> --json` 校验最新 run→test receipt 的 artifact/hash/HEAD;然后 **`harness_gate.py begin --phase test --change <id>`**(禁止手工 phase.start / 手写 ledger)。执行各项强制环境检查 + **命令执行模式 preflight (0.1)**;只有首选执行器不可用时,才执行 fallback 执行器探测。
88
+ 先 `harness_context.py prepare --project . --change <id> --phase test --executor <tool> --json`(`--project` 必填,漏了直接 argparse 报错),再 `harness_context.py begin --project . --change <id> --phase test --executor <tool> --json` 校验最新 run→test receipt 的 artifact/hash/HEAD;然后 **`harness_gate.py begin --phase test --change <id>`**(禁止手工 phase.start / 手写 ledger)。执行各项强制环境检查 + **命令执行模式 preflight (0.1)**;只有首选执行器不可用时,才执行 fallback 执行器探测。
89
89
 
90
90
  验证写入**仅**允许 `harness_ledger.py record` / `can-reuse`;禁止 Write/Edit `verification-ledger.json`。测试跟踪:gate begin → 执行(可选 `harness_test_guard.py mark stale-test-repair`)→ 单次 `harness_gate.py close`。`--to-phase` 取实际阶段计划的后继;Fixback 返回 run,普通流程可直接进入 Review、Submit 或 Archive。Fixback 只失效与改动文件相交的验证目标。
91
91
 
@@ -273,6 +273,8 @@ powershell.exe -NoProfile -Command "try { (Invoke-WebRequest -Uri 'http://127.0.
273
273
  - [ ] 复用 → 跳过重跑,标记"✅ 复用 harness-run 单元测试结果"
274
274
  - [ ] 不复用 → 按 profile key resolve 重跑测试命令(`harness_profile.py resolve --key unitTest`,不复制示例 `-pl` 命令),结果写回 ledger 的 `unitTest` 项
275
275
  - [ ] HTTP/API 契约结果写入 `apiTest`;真实浏览器/真实栈 Playwright 结果写入 `browserTest`,两者不得互相覆盖
276
+ - [ ] **本变更没有该维度场景时**(如全部为单元场景 → 无 API 场景):用 `harness_ledger.py record --verification apiTest --status NOT_RUN --applicability NOT_APPLICABLE --applicability-reason "<为什么不适用>"` 登记,**不传 `--files`**。门禁 close 要求 requiredValidations 每项都有 entry,但**绝不能拿无关文件(比如单元测试文件)凑 `--files`**——那会让 ledger 声称该维度的输入是那些文件,是假证据
277
+ - [ ] `can-reuse` 返回 `reuse:false` 时直接读默认输出里的 `reason`/`executionNeed`/`detail` 定位原因,不必再补跑 `--verbose`
276
278
  - [ ] 复用判断前以 `harness_ledger.py diff-hash --repo . --base <baseCommit> --change-dir ".harness/changes/<change-name>" --json` 重算指纹;test-tracking manifest 无效或 hash 漂移即停止
277
279
  - [ ] 测试失败若明确为陈旧测试,仅在当前代码/批准计划/可验证历史唯一确定新契约且只改测试时自动修复;否则记录 `BLOCKED_PREEXISTING`
278
280
  - [ ] 自动修复后立即重跑该测试与目标测试,并以 `harness_test_guard.py record ... --reason stale-test-repair` 记录精确路径
@@ -991,6 +991,62 @@ def migrate_legacy_candidate_evidence(
991
991
  return receipt
992
992
 
993
993
 
994
+ # 声明必须是具体的错误签名,不能是能匹配一切的短串。这个下限不是"安全边界"
995
+ # ——审计线索才是——但它挡住最省事的滥用写法。
996
+ _PREEXISTING_MIN_PATTERN = 8
997
+
998
+
999
+ def _known_preexisting_patterns(
1000
+ project_root: Path,
1001
+ ) -> tuple[list[dict[str, str]], list[dict[str, str]]]:
1002
+ """build-profile 里声明的预存失败签名,按"是否足够具体"分成两组。
1003
+
1004
+ `knownPreexistingErrors` 由 `harness_preflight.py record-quirk --action
1005
+ skip-not-block` 写入,此前没有任何消费方——于是预存失败在 certify-local 处
1006
+ 成为死结:要么去 gate-policy 降门禁,要么顺手改范围外的产品 bug。
1007
+ """
1008
+ path = project_root / ".harness" / "config" / "build-profile.json"
1009
+ if not path.is_file():
1010
+ return [], []
1011
+ try:
1012
+ profile = json.loads(path.read_text(encoding="utf-8-sig"))
1013
+ except (OSError, ValueError):
1014
+ return [], []
1015
+ declared = profile.get("knownPreexistingErrors") if isinstance(profile, dict) else None
1016
+ if not isinstance(declared, list):
1017
+ return [], []
1018
+ usable: list[dict[str, str]] = []
1019
+ vague: list[dict[str, str]] = []
1020
+ for item in declared:
1021
+ if not isinstance(item, dict) or item.get("action") != "skip-not-block":
1022
+ continue
1023
+ pattern = str(item.get("pattern") or "").strip()
1024
+ reason = str(item.get("reason") or "").strip()
1025
+ if not pattern or not reason:
1026
+ continue
1027
+ record = {"pattern": pattern, "reason": reason}
1028
+ (usable if len(pattern) >= _PREEXISTING_MIN_PATTERN else vague).append(record)
1029
+ return usable, vague
1030
+
1031
+
1032
+ def _match_preexisting(
1033
+ entry: dict[str, Any], patterns: list[dict[str, str]]
1034
+ ) -> dict[str, str] | None:
1035
+ """失败证据里是否确实出现了某个已声明签名。
1036
+
1037
+ 只看"声明过"是不够的——那等于声明一次豁免一切。必须是这一条失败的证据里
1038
+ 真的带着该签名,声明与现场才对得上。
1039
+ """
1040
+ evidence = entry.get("evidence")
1041
+ haystack = evidence if isinstance(evidence, str) else json.dumps(
1042
+ evidence, ensure_ascii=False, sort_keys=True
1043
+ )
1044
+ for record in patterns:
1045
+ if record["pattern"] in haystack:
1046
+ return record
1047
+ return None
1048
+
1049
+
994
1050
  def certify_local_candidate(
995
1051
  change_dir: Path,
996
1052
  *,
@@ -1029,14 +1085,28 @@ def certify_local_candidate(
1029
1085
  required = ["unitTestFull"]
1030
1086
  required = [str(item).strip() for item in required if str(item).strip()]
1031
1087
 
1088
+ usable_patterns, vague_patterns = _known_preexisting_patterns(project_root)
1089
+
1032
1090
  selected: list[dict[str, Any]] = []
1091
+ exemptions: list[dict[str, Any]] = []
1033
1092
  for name in required:
1034
1093
  entry = validations.get(name)
1035
1094
  if not isinstance(entry, dict):
1036
1095
  raise ValueError(f"required validation missing: {name}")
1037
1096
  missing: list[str] = []
1097
+ exempted = None
1038
1098
  if str(entry.get("status") or "").upper() != "OK":
1039
- missing.append("status=OK")
1099
+ exempted = _match_preexisting(entry, usable_patterns)
1100
+ if exempted is None:
1101
+ vague = _match_preexisting(entry, vague_patterns)
1102
+ if vague is not None:
1103
+ raise ValueError(
1104
+ f"required validation {name} failed and the declared "
1105
+ f"knownPreexistingErrors pattern {vague['pattern']!r} is too "
1106
+ "generic to identify it; declare the specific error signature "
1107
+ f"(at least {_PREEXISTING_MIN_PATTERN} characters)"
1108
+ )
1109
+ missing.append("status=OK")
1040
1110
  for field in (
1041
1111
  "command",
1042
1112
  "evidence",
@@ -1048,6 +1118,15 @@ def certify_local_candidate(
1048
1118
  raise ValueError(
1049
1119
  f"required validation {name} is incomplete: {', '.join(missing)}"
1050
1120
  )
1121
+ if exempted is not None:
1122
+ exemptions.append(
1123
+ {
1124
+ "validation": name,
1125
+ "status": str(entry.get("status") or "").upper(),
1126
+ "pattern": exempted["pattern"],
1127
+ "reason": exempted["reason"],
1128
+ }
1129
+ )
1051
1130
  selected.append(
1052
1131
  {
1053
1132
  "name": name,
@@ -1160,6 +1239,8 @@ def certify_local_candidate(
1160
1239
  {str(item["inputsHash"]) for item in selected}
1161
1240
  ),
1162
1241
  "logHashes": sorted({str(item["logHash"]) for item in selected}),
1242
+ # 豁免必须随收据一起留痕,否则事后无法把它与干净通过区分开
1243
+ "preexistingExemptions": exemptions,
1163
1244
  },
1164
1245
  }
1165
1246
  if rebound_from_commit is not None: