@hunter-harness/workflow-harness 0.2.69 → 0.2.71

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 (210) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/README.md +1 -1
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +3 -1
  8. package/harness/bundles/general/claude-code/harness-plan/reference.md +31 -1
  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 +1 -1
  12. package/harness/bundles/general/claude-code/harness-run/SKILL.md +3 -1
  13. package/harness/bundles/general/claude-code/harness-run/reference.md +20 -0
  14. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/harness-test/SKILL.md +3 -1
  17. package/harness/bundles/general/claude-code/harness-test/pitfalls.md +34 -3
  18. package/harness/bundles/general/claude-code/harness-test/reference.md +9 -0
  19. package/harness/bundles/general/claude-code/scripts/harness_context.py +27 -7
  20. package/harness/bundles/general/claude-code/scripts/harness_events.py +12 -3
  21. package/harness/bundles/general/claude-code/scripts/harness_gate.py +127 -11
  22. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +244 -5
  23. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +254 -19
  24. package/harness/bundles/general/claude-code/scripts/harness_service.py +69 -15
  25. package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +48 -1
  26. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  27. package/harness/bundles/general/codebuddy/README.md +1 -1
  28. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  32. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +3 -1
  33. package/harness/bundles/general/codebuddy/harness-plan/reference.md +31 -1
  34. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
  35. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
  36. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +3 -1
  38. package/harness/bundles/general/codebuddy/harness-run/reference.md +20 -0
  39. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  40. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +3 -1
  42. package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +34 -3
  43. package/harness/bundles/general/codebuddy/harness-test/reference.md +9 -0
  44. package/harness/bundles/general/codebuddy/scripts/harness_context.py +27 -7
  45. package/harness/bundles/general/codebuddy/scripts/harness_events.py +12 -3
  46. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +127 -11
  47. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +244 -5
  48. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +254 -19
  49. package/harness/bundles/general/codebuddy/scripts/harness_service.py +69 -15
  50. package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +48 -1
  51. package/harness/bundles/general/codex/.harness-build.json +1 -1
  52. package/harness/bundles/general/codex/README.md +1 -1
  53. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  54. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  55. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  56. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  57. package/harness/bundles/general/codex/harness-plan/SKILL.md +3 -1
  58. package/harness/bundles/general/codex/harness-plan/reference.md +31 -1
  59. package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
  60. package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
  61. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  62. package/harness/bundles/general/codex/harness-run/SKILL.md +3 -1
  63. package/harness/bundles/general/codex/harness-run/reference.md +20 -0
  64. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-test/SKILL.md +3 -1
  67. package/harness/bundles/general/codex/harness-test/pitfalls.md +34 -3
  68. package/harness/bundles/general/codex/harness-test/reference.md +9 -0
  69. package/harness/bundles/general/codex/scripts/harness_context.py +27 -7
  70. package/harness/bundles/general/codex/scripts/harness_events.py +12 -3
  71. package/harness/bundles/general/codex/scripts/harness_gate.py +127 -11
  72. package/harness/bundles/general/codex/scripts/harness_ledger.py +244 -5
  73. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +254 -19
  74. package/harness/bundles/general/codex/scripts/harness_service.py +69 -15
  75. package/harness/bundles/general/codex/scripts/harness_test_guard.py +48 -1
  76. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  77. package/harness/bundles/general/cursor/README.md +1 -1
  78. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  79. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  80. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  81. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  82. package/harness/bundles/general/cursor/harness-plan/SKILL.md +3 -1
  83. package/harness/bundles/general/cursor/harness-plan/reference.md +31 -1
  84. package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
  85. package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
  86. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  87. package/harness/bundles/general/cursor/harness-run/SKILL.md +3 -1
  88. package/harness/bundles/general/cursor/harness-run/reference.md +20 -0
  89. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  90. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  91. package/harness/bundles/general/cursor/harness-test/SKILL.md +3 -1
  92. package/harness/bundles/general/cursor/harness-test/pitfalls.md +34 -3
  93. package/harness/bundles/general/cursor/harness-test/reference.md +9 -0
  94. package/harness/bundles/general/cursor/scripts/harness_context.py +27 -7
  95. package/harness/bundles/general/cursor/scripts/harness_events.py +12 -3
  96. package/harness/bundles/general/cursor/scripts/harness_gate.py +127 -11
  97. package/harness/bundles/general/cursor/scripts/harness_ledger.py +244 -5
  98. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +254 -19
  99. package/harness/bundles/general/cursor/scripts/harness_service.py +69 -15
  100. package/harness/bundles/general/cursor/scripts/harness_test_guard.py +48 -1
  101. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  102. package/harness/bundles/java/claude-code/README.md +1 -1
  103. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  104. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  105. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  106. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  107. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  108. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  109. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +3 -1
  110. package/harness/bundles/java/claude-code/harness-plan/reference.md +31 -1
  111. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
  112. package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
  113. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  114. package/harness/bundles/java/claude-code/harness-run/SKILL.md +3 -1
  115. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  116. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  117. package/harness/bundles/java/claude-code/harness-test/SKILL.md +3 -1
  118. package/harness/bundles/java/claude-code/harness-test/pitfalls.md +34 -3
  119. package/harness/bundles/java/claude-code/scripts/harness_context.py +27 -7
  120. package/harness/bundles/java/claude-code/scripts/harness_events.py +12 -3
  121. package/harness/bundles/java/claude-code/scripts/harness_gate.py +127 -11
  122. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +244 -5
  123. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +254 -19
  124. package/harness/bundles/java/claude-code/scripts/harness_service.py +69 -15
  125. package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +48 -1
  126. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  127. package/harness/bundles/java/codebuddy/README.md +1 -1
  128. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  129. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  130. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  131. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  132. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  133. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  134. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +3 -1
  135. package/harness/bundles/java/codebuddy/harness-plan/reference.md +31 -1
  136. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
  137. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
  138. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  139. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +3 -1
  140. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  141. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  142. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +3 -1
  143. package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +34 -3
  144. package/harness/bundles/java/codebuddy/scripts/harness_context.py +27 -7
  145. package/harness/bundles/java/codebuddy/scripts/harness_events.py +12 -3
  146. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +127 -11
  147. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +244 -5
  148. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +254 -19
  149. package/harness/bundles/java/codebuddy/scripts/harness_service.py +69 -15
  150. package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +48 -1
  151. package/harness/bundles/java/codex/.harness-build.json +1 -1
  152. package/harness/bundles/java/codex/README.md +1 -1
  153. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  154. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  155. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  156. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  157. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  158. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  159. package/harness/bundles/java/codex/harness-plan/SKILL.md +3 -1
  160. package/harness/bundles/java/codex/harness-plan/reference.md +31 -1
  161. package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
  162. package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
  163. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  164. package/harness/bundles/java/codex/harness-run/SKILL.md +3 -1
  165. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  166. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  167. package/harness/bundles/java/codex/harness-test/SKILL.md +3 -1
  168. package/harness/bundles/java/codex/harness-test/pitfalls.md +34 -3
  169. package/harness/bundles/java/codex/scripts/harness_context.py +27 -7
  170. package/harness/bundles/java/codex/scripts/harness_events.py +12 -3
  171. package/harness/bundles/java/codex/scripts/harness_gate.py +127 -11
  172. package/harness/bundles/java/codex/scripts/harness_ledger.py +244 -5
  173. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +254 -19
  174. package/harness/bundles/java/codex/scripts/harness_service.py +69 -15
  175. package/harness/bundles/java/codex/scripts/harness_test_guard.py +48 -1
  176. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  177. package/harness/bundles/java/cursor/README.md +1 -1
  178. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  179. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  180. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  181. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  182. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  183. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  184. package/harness/bundles/java/cursor/harness-plan/SKILL.md +3 -1
  185. package/harness/bundles/java/cursor/harness-plan/reference.md +31 -1
  186. package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
  187. package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
  188. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  189. package/harness/bundles/java/cursor/harness-run/SKILL.md +3 -1
  190. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  191. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  192. package/harness/bundles/java/cursor/harness-test/SKILL.md +3 -1
  193. package/harness/bundles/java/cursor/harness-test/pitfalls.md +34 -3
  194. package/harness/bundles/java/cursor/scripts/harness_context.py +27 -7
  195. package/harness/bundles/java/cursor/scripts/harness_events.py +12 -3
  196. package/harness/bundles/java/cursor/scripts/harness_gate.py +127 -11
  197. package/harness/bundles/java/cursor/scripts/harness_ledger.py +244 -5
  198. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +254 -19
  199. package/harness/bundles/java/cursor/scripts/harness_service.py +69 -15
  200. package/harness/bundles/java/cursor/scripts/harness_test_guard.py +48 -1
  201. package/harness/manifests/general/claude-code.json +27 -27
  202. package/harness/manifests/general/codebuddy.json +27 -27
  203. package/harness/manifests/general/codex.json +27 -27
  204. package/harness/manifests/general/cursor.json +27 -27
  205. package/harness/manifests/java/claude-code.json +27 -27
  206. package/harness/manifests/java/codebuddy.json +27 -27
  207. package/harness/manifests/java/codex.json +27 -27
  208. package/harness/manifests/java/cursor.json +27 -27
  209. package/hunter-workflow-family.json +5 -5
  210. package/package.json +1 -1
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "claude-code",
5
- "coreHash": "bfe37ba08ec6ec72"
5
+ "coreHash": "2295b79da2fe3e66"
6
6
  }
@@ -376,7 +376,7 @@ $out = "<build-output-dir>"
376
376
  # 通用项目
377
377
  python "$skillsRoot/scripts/harness_deploy.py" build --skills-root $skillsRoot --out $out --json
378
378
 
379
- # Java 后端(core + java overlay → 12 skill 自包含树)
379
+ # Java 后端(core + java overlay → 14 skill 自包含树:12 base + 2 java-only)
380
380
  python "$skillsRoot/scripts/harness_deploy.py" build --skills-root $skillsRoot --overlay java --out $out --json
381
381
 
382
382
  python "$skillsRoot/scripts/harness_deploy.py" install --from $out --project "<目标项目>" --json
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-archive — 变更归档
26
26
 
27
27
  ## Purpose
@@ -20,7 +20,7 @@ disallowed-tools:
20
20
  - Bash(awk *)
21
21
  - Bash(curl *)
22
22
  ---
23
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
23
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
24
24
  # harness-codebase-map — 代码库地图
25
25
 
26
26
  ## Purpose
@@ -8,7 +8,7 @@ disallowed-tools:
8
8
  - Bash(git *)
9
9
  - Bash(curl *)
10
10
  ---
11
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
11
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
12
12
  # harness-knowledge-ingest
13
13
 
14
14
  知识 ingest 完全由 Hunter Platform 负责。归档完成时,客户端生成一个确定性 ZIP;
@@ -8,7 +8,7 @@ disallowed-tools:
8
8
  - Bash(git *)
9
9
  - Bash(curl *)
10
10
  ---
11
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
11
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
12
12
  # harness-knowledge-query
13
13
 
14
14
  项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-plan — 需求规划
26
26
 
27
27
  ## Purpose
@@ -120,6 +120,8 @@ change-name 范围变更 → 提示重命名或记 🟡WARN(→ `reference.md`
120
120
  | 空目录 | 不得为“预留目录”生成 `.gitkeep`;只有产品明确需要跟踪空目录时才能创建,并在计划中说明业务原因 |
121
121
  | 设计审批包 | 一次 blocking user confirmation 含 worktree(读 `harness.json` `defaultWorktree`) |
122
122
  | 阶段 8 | 二选一且不得混用:**v2** = `hunter-harness plan finalize`(证据包 → 八 target + journal committed + plan-events.ndjson);**legacy** = 六项标准产物先进入 staging,仅 finalizer 校验成功后发布并写唯一 `phase.end`/log,随后 `verify` 确认 start/end、收据完整覆盖六项标准产物、哈希、全部任务表和非空场景清单一致。失败均不得手工补终态 |
123
+ | 发布后改产物 | 用 `harness_plan_finalize.py republish --run-id <全新> --reason "<why>"` 一次完成(新 attempt + 换收据 + 重新派生 manifest)。重跑 `finalize` 会报 `PLAN_FINALIZATION_HASH_CONFLICT`;**绝不手改 `meta/scenario-manifest.json`**(派生物,手改必致 `ARTIFACT_HASH_DRIFT`)→ `reference.md`「发布后修订计划」 |
124
+ | v2 输入骨架 | 不要猜 `plan-evidence-input.json` 结构,也不要去翻 TS 接口/npx 缓存:`npx hunter-harness plan evidence-pack --print-template` 直接给带占位符的完整骨架 |
123
125
  | Plan 结束 | **禁止**询问执行模式;只提示 `/harness-run` |
124
126
  | 知识查询 | 阶段 1 失败不得假装已读历史,也不得改用本地索引或其他执行入口 |
125
127
  | 歧义优先检查 | 否定、对比、动作对象或范围存在多种合理解释时,最小取证后先给推荐理解并一次一问;确认前不深挖错误方向 |
@@ -357,7 +357,14 @@ npx hunter-harness plan evidence-pack --input .harness/changes/<cn>/meta/plan-ev
357
357
  npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidence.json
358
358
  ```
359
359
 
360
- **自然输入文件**(`meta/plan-evidence-input.json`,权威定义 `packages/cli/src/commands/plan-evidence-pack.ts` 的 `EvidencePackInputFile`)由规划阶段逐步沉淀,各字段定稿时点不得倒置:
360
+ **自然输入文件**(`meta/plan-evidence-input.json`,权威定义 `packages/cli/src/commands/plan-evidence-pack.ts` 的 `EvidencePackInputFile`)由规划阶段逐步沉淀,各字段定稿时点不得倒置。
361
+
362
+ > 📋 **先取骨架,别猜结构**:CLI 自带模板输出,不需要去找 TS 接口或翻 npx 缓存——
363
+ > ```bash
364
+ > npx hunter-harness plan evidence-pack --print-template > .harness/changes/<cn>/meta/plan-evidence-input.json
365
+ > ```
366
+ > 输出是带 `<...>` 占位符的完整骨架,逐项替换即可;替换完 grep 一次 `<` 自检有无遗漏。
367
+ > `--print-template` 不读写任何文件,只打到 stdout。
361
368
 
362
369
  | 字段 | 内容 | 定稿阶段 |
363
370
  |------|------|:---:|
@@ -379,6 +386,29 @@ npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidenc
379
386
  - **legacy 收据**:过渡期(阶段 14 前)v2 路径不写 `plan-finalization.json`;消费方若仍读 legacy receipt,由兼容投影单独提供,不得反向要求 v2 双写。
380
387
  - **回退**:v2 自然输入不完整(如缺真实审批记录)时才允许走 Python finalizer(legacy 路径);同一次发布不得两条路径混用。
381
388
 
389
+ ### 发布后修订计划(republish)
390
+
391
+ 计划发布后又要改产物,是**正常且高频**的情况——用户看完计划补一个回归场景、修正一条任务、调整验收标准。这时不要与哈希守卫搏斗:
392
+
393
+ ```bash
394
+ python <skills-root>/scripts/harness_plan_finalize.py republish \
395
+ --change-dir ".harness/changes/<cn>" --staging-dir ".harness/changes/<cn>/runtime/plan-staging" \
396
+ --change <cn> --run-id "plan_$(uuidgen)" --reason "<为什么要改>" --json
397
+ ```
398
+
399
+ 它一次性完成整套动作:校验 staging → 分配新 attempt(自动取已用最大值 +1)→ 追加 `phase.start` → 替换收据 → 重新派生 `scenario-manifest.json` 与 `implementation-checkpoints.json` → 写 `phase.end`。收据里保留 `supersedes`(被取代的 hash/runId/attempt)与 `amendReason`,修订全程可审计。
400
+
401
+ | 约束 | 说明 |
402
+ |------|------|
403
+ | `--reason` 必填 | 修订已发布计划必须留下理由,否则 `PLAN_AMEND_REASON_REQUIRED` |
404
+ | `--run-id` 必须全新 | 复用旧 run-id 报 `PLAN_AMEND_RUN_ID_IN_USE`(attempt 与 run-id 一一绑定) |
405
+ | 首次发布不能用它 | 无收据时报 `PLAN_NOT_FINALIZED`,首次发布走 `finalize` |
406
+ | 内容没变则空操作 | 返回 `idempotent:true`,不写事件、不消耗 attempt |
407
+
408
+ ⚠️ **绝对不要手改 `meta/scenario-manifest.json`**。它是 finalizer 从 `test-scenarios.md` 派生的产物,手改会造成真实漂移:`verify` 报 `ARTIFACT_HASH_DRIFT`,run 阶段 `validate_plan_handoff` 也会记 WARN。`republish` 会重新派生它,这才是唯一正确入口。
409
+
410
+ > 直接重跑 `finalize` 会报 `PLAN_FINALIZATION_HASH_CONFLICT`——这是守卫在防止发布后产物被悄悄改动,不是 bug。报错信息里已经给出 `republish` 命令行。
411
+
382
412
  ### Plan 结束行为规则
383
413
 
384
414
  - **禁止询问执行模式**:Subagent-Driven / Inline Execution 属于 /harness-run 阶段
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-pull — 从 Hunter Platform 下拉与恢复
26
26
 
27
27
  ## Purpose
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-push — 上传到 Hunter Platform
26
26
 
27
27
  ## Purpose
@@ -22,7 +22,7 @@ disallowed-tools:
22
22
  - Bash(curl *)
23
23
  - Bash(codegraph *)
24
24
  ---
25
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
25
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
26
26
  # harness-review — 代码审查
27
27
 
28
28
  ## Purpose
@@ -22,7 +22,7 @@ disallowed-tools:
22
22
  - Bash(curl *)
23
23
  - Bash(codegraph *)
24
24
  ---
25
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
25
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
26
26
  # harness-run — 需求编码
27
27
 
28
28
  ## Purpose
@@ -87,6 +87,8 @@ disallowed-tools:
87
87
 
88
88
  **阶段归属规则**:只用 `ownerPhase=run` 的任务和场景判定编码阶段结果。`ownerPhase=test` 的任务或场景按计划留给测试阶段属于正常移交,必须记录为“待测试阶段执行”,不得将编码阶段降级为 WARN;只有 run 自身负责的工作未完成、验证降级或证据异常时才使用 WARN。
89
89
 
90
+ > 关门脚本与本规则一致:`harness_gate.py close --phase run` 的 C9 场景覆盖只要求 `ownerPhase` 为 `plan`/`run` 的必需场景有通过 receipt,`ownerPhase=test` 的场景出现在返回值的 `deferred` 里,不阻断 run。若 run 关门报 `REQUIRED_SCENARIO_NOT_EXECUTED` 且缺的是接口/端到端场景,那是 `meta/scenario-manifest.json` 里 `ownerPhase` 标错了(或老清单没声明),应当修清单——**不要**为了过门在 run 阶段起服务补跑本属 test 的验证。
91
+
90
92
  **Fixback**:入口只用 `launch-review`,后续问题处理通过 `resolve-issue/close` 驱动,不得把修复说明当成新的普通 Run。只读取返回的受影响问题和文件;验证仅失效与 `changedFiles` 相交的目标,其他 Test/Review 证据继续复用。RED 优先;`manual`、`workflow` 或未选用的建议不进入代码批次,使用中文记录处理结论。
91
93
 
92
94
  **执行器边界**:优先使用项目 build profile 和已有测试入口。禁止为了绕过 ESM、路径或参数问题临时生成 `.js`、`require` 脚本;需要文件式 runner 时使用项目已有入口,确需新增时遵循项目模块类型(例如 ESM 使用 `.mjs`)。runner 包装说明写入 `runnerCommand` 元数据,不得拼进账本的规范 `command`。
@@ -799,6 +799,8 @@ powershell.exe -Command "git -C '<project-path>' diff --check"
799
799
  > 可直接复制;替换 `<skills-root>` / `<cn>` / `<dir>` / `<project>`。`--task` **仅在该 change 启用 checkpoint 时必需**(checkpoints 文件缺失或不含 pending foundation-gate 时不要传)。
800
800
  > `ledger record` 必需:`--duration-ms`、`--evidence`,以及 `--files`(逗号分隔源文件)或 `--profile-input <verificationKey> --project <project>`(从 build-profile 展开)。`status` 枚举: `ok|fail|not_run`(没有 PASS)。
801
801
  > `--skills-root` 仅用于 `begin`(及 `lint-skills`):必须是 adapter 根(如 `.cursor/skills`),不是 `scripts/` 子目录。**`close` 不需要 `--skills-root`**(该子命令不接受此参数)。
802
+ >
803
+ > ⚠️ **`--project` 一律传路径,不传项目名。** 在项目根执行时就写 `--project .`。传项目名(`--project udp`)会被解析成 `<cwd>/udp`,然后以各不相同的下游错误暴露出来(`PROJECT_ROOT_INVALID` / `EXECUTION_ROOT_INVALID`)。
802
804
 
803
805
  ```powershell
804
806
  # gate begin/close(--task 仅在该 change 启用 checkpoint 时必需;close 不需要 --skills-root)
@@ -810,8 +812,21 @@ python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verif
810
812
 
811
813
  # 复用检查(--profile-input 取 verification key,不是文件路径;配合 --project)
812
814
  python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --verification unitTestFull --profile-input unitTestFull --project <project>
815
+
816
+ # scenario-manifest schemaVersion 2:record 绑定场景必须带 receipt,先生成骨架再记录
817
+ python <skills-root>/scripts/harness_ledger.py scenario-receipt-template --change-dir <dir> --scenario-ids "UT-001,UT-002" --runner <runner 名> --out runtime/scenario-receipt-unit.json --json
818
+ python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification unitTest --status ok ... --scenario-ids "UT-001,UT-002" --scenario-receipt-file runtime/scenario-receipt-unit.json
813
819
  ```
814
820
 
821
+ ### run 阶段关门顺序
822
+
823
+ ```powershell
824
+ python <skills-root>/scripts/harness_test_guard.py close --project . --change-dir ".harness/changes/<cn>" --json
825
+ python <skills-root>/scripts/harness_gate.py close --change <cn> --phase run --status OK --to-phase <后继> --executor <tool>
826
+ ```
827
+
828
+ C9 场景覆盖按 `ownerPhase` **分阶段判定**:run 关门只要求 `ownerPhase` 为 `plan`/`run` 的必需场景有通过 receipt;`ownerPhase=test` 的场景计入返回值的 `deferred`,属正常移交,不阻断 run。反过来,test 关门时这些场景仍然必须有 receipt。未声明 `ownerPhase` 的老清单沿用旧语义(全部即时要求)。
829
+
815
830
  ### 常见报错对照
816
831
 
817
832
  | 报错 | 原因 | 处理 |
@@ -823,6 +838,11 @@ python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --ve
823
838
  | `record requires --files or a non-empty --profile-input file set` | 缺少输入文件集 | 补 `--files` 或 `--profile-input <key> --project <project>` |
824
839
  | `--profile-input requires --project` | can-reuse/record 展开 profile 需要项目根 | 补 `--project <project>` |
825
840
  | `record` 缺 `--duration-ms` / `--evidence` | 参数为必填 | 按模板补齐 |
841
+ | `PROJECT_ROOT_INVALID` | `--project` 传了项目名而不是路径 | 在项目根执行并传 `--project .`;报错的 `resolvedProject` 字段显示了实际解析到哪 |
842
+ | `EXECUTION_ROOT_INVALID` | gate 的 `--project` 是**执行根路径**(worktree),同样不接受项目名 | 传 `.` 或 worktree 绝对路径 |
843
+ | `SCENARIO_RECEIPT_REQUIRED` | manifest 是 schemaVersion 2,`--scenario-ids` 必须配 receipt | 用上面的 `scenario-receipt-template` 生成骨架,再传 `--scenario-receipt-file` |
844
+ | `SCENARIO_RECEIPT_NOT_FOUND` | receipt 路径找不到 | 报错的 `triedPaths` 列出了尝试过的绝对路径;相对路径同时按 CWD 和 `--change-dir` 解析,二选一放对即可 |
845
+ | `REQUIRED_SCENARIO_NOT_EXECUTED`(run 关门,缺的是接口场景) | 这些场景 `ownerPhase` 应为 `test` 却写成了 run,或清单未声明 `ownerPhase` | 修 `meta/scenario-manifest.json` 的 `ownerPhase`;**不要**为了过门去跑本该属于 test 阶段的验证 |
826
846
 
827
847
  ## 关键原则
828
848
 
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-submit — 最终提交(含 worktree 合并)
26
26
 
27
27
  ## Purpose
@@ -19,7 +19,7 @@ disallowed-tools:
19
19
  - Bash(awk *)
20
20
  - Bash(curl *)
21
21
  ---
22
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
22
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
23
23
  # harness-sync
24
24
 
25
25
  ## Purpose
@@ -23,7 +23,7 @@ disallowed-tools:
23
23
  - Bash(node *)
24
24
  - Bash(codegraph *)
25
25
  ---
26
- <!-- generated by harness_deploy.py; core=bfe37ba08ec6ec72; overlay=none; agent=claude-code; do not edit -->
26
+ <!-- generated by harness_deploy.py; core=2295b79da2fe3e66; overlay=none; agent=claude-code; do not edit -->
27
27
  # harness-test — 测试执行
28
28
 
29
29
  ## Purpose
@@ -201,6 +201,8 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
201
201
 
202
202
  先读 `.harness/changes/<change-name>/runtime/credential-cache.json`(认证凭证缓存,按项目认证机制;token/SSO 为常见实现),本地轻量接口验证通过则复用,失败才走远程认证。接口测试执行器用 request context / 原生 HTTP 客户端直连本地 baseURL,**不得依赖浏览器当前页面 origin**。同一次流程内凭证刷新计数 > 1 → 🟡 WARN。**不得在报告/日志/对话总结中输出明文凭证**。详见 `reference.md`「认证凭证缓存与复用」。
203
203
 
204
+ > ⛔ **验证码 = 硬停,不是待解的技术问题。** 登录响应出现 `验证码` / `captcha` / `blockPuzzle` / `slider` / `geetest` 等特征时,**禁止**编写或运行任何求解代码(图像匹配、OCR、打码平台、反编译服务端找容差均在禁止之列)。立即记 `apiTest=BLOCKED`,请用户手工把凭证写入 `runtime/credential-cache.json` 或临时关闭测试环境验证码,并提示 `.harness/config/harness-test-config.md` 的认证方式已过期。详见 `pitfalls.md` 规则 31。
205
+
204
206
  ### 九、测试报告状态规则
205
207
 
206
208
  整体 ✅OK / 🟡WARN / ❌FAIL 三态;API 维度使用 `OK` / `PARTIAL` / `BLOCKED` / `NOT_RUN` / `FAIL` 五态。**不得把「5 PASS + 9 BLOCKED + 1 FAIL」写成 `apiTest=NOT_RUN`**,正确为 `apiTest=PARTIAL`。P0 场景 BLOCKED 不得仍 OK。详见 `reference.md`「结果分级规则」。
@@ -2,16 +2,16 @@
2
2
  description: harness-test 的踩坑规则(来自真实对话日志)。执行测试前必须通读,遇到测试失败时重新参考。
3
3
  ---
4
4
 
5
- # 避坑规则(30 条)
5
+ # 避坑规则(32 条)
6
6
 
7
- > 以下规则来自真实对话日志(2026-06-12 ~ 06-24),每个都有明确的现象和根因。
7
+ > 以下规则来自真实对话日志(2026-06-12 ~ 08-17),每个都有明确的现象和根因。
8
8
  > 执行 `harness-test` 时必须通读,避免重蹈覆辙。
9
9
 
10
10
  | # | 规则 | 现象 | 根因 | 解法 |
11
11
  |:--:|------|------|------|------|
12
12
  | 1 | 凭证可展示但注意安全 | 对话日志记录了凭证 | Bearer 凭证被粘贴到聊天中 | 凭证可以在对话中展示用于调试,但不要写入持久化文件(测试报告、执行日志等) |
13
13
  | 2 | 不用 curl 发中文 | `Invalid UTF-8 start byte 0xb2` | Windows curl 默认 GBK 编码 | 用 PowerShell Invoke-WebRequest 或 Playwright fetch |
14
- | 3 | 必须带租户标识 | 所有接口 500 | 租户上下文为空 | 请求头加租户标识(见 build-profile.httpHeaders |
14
+ | 3 | 必须带租户标识 | 所有接口 500,或 200 但 `data:null` | 租户上下文为空,或请求头租户值与测试数据行的租户不一致 | 请求头加租户标识(见 build-profile.httpHeaders);**取值必须等于测试数据实际所属租户**,先查数据行的租户列再填,不要默认写 0 |
15
15
  | 4 | 认证降级 | 接口 401 "账号未登录" | 本地无认证服务 | 应用配置放行(如 permit-all-urls)或权限降级 |
16
16
  | 5 | 错误码双格式兼容 | 测试断言失败 | 预期 `1_003_002_009`,实际 `1003002009` | 自动兼容下划线和数字两种格式 |
17
17
  | 6 | 编译产物确认 | 修改代码后测试结果不变 | IDE 热重载未触发 | 测试前编译(如 `mvn compile -o`) |
@@ -39,6 +39,8 @@ description: harness-test 的踩坑规则(来自真实对话日志)。执行
39
39
  | 28 | **已有服务未先决策就跑业务接口** | 旧服务不含新代码,接口 500 后才发现版本不匹配 | 检测到已有应用服务(端口被占)后,未先展示服务决策门就跑业务接口 | 先展示服务决策门,询问复用/重启/跳过/停止;询问前只允许 health/meta 检查 |
40
40
  | 29 | **启动命令反复试相对配置叠加 / 中文路径** | 相对路径或中文路径导致应用读不到运行时配置叠加,启动失败 | 默认只用 `C:/temp/harness-test-overlay/<change>/application-harness-test.yml` ASCII 绝对路径,并固化已知良好测试配置 |
41
41
  | 30 | **伪 diffHash 导致错误复用** | `3files-84plus-5minus` 不能证明代码未变 | 用 `git diff --binary` 生成 patch 并计算 SHA-256,ledger 只认 `sha256:<hash>` |
42
+ | 31 | **尝试破解验证码 / 人机验证** | 认证服务返回"验证码不能为空",AI 写图像识别脚本反复试坐标(实测 28 次 / 4 种算法全失败) | 测试配置里的账号密码登录流程被加了滑块/图形验证码,配置未同步 | **硬停**。禁止编写或运行任何验证码求解代码;立即标 `apiTest=BLOCKED`,请用户手工提供凭证写入 `runtime/credential-cache.json`,或临时关闭测试环境验证码 |
43
+ | 32 | **预存环境缺陷被当成本次变更的阻塞** | `unitTestFull` 144 个测试挂 1 个,根因是别人提交引入的缺省配置缺失 | 全量验证把预存缺陷和本次 diff 混在一起判定 | 先用 `git log -S` + diff 触点证明与本次变更无关,再按「预存缺陷」上报并请用户决策(修根因 / 记 🟡WARN / 停止),**不得**擅自改 tracked 配置或排除测试 |
42
44
 
43
45
 
44
46
  ## 详细说明
@@ -257,6 +259,35 @@ $cred = $resp.data.accessToken
257
259
  **后果**:代码已变却复用旧测试结果,掩盖回归问题
258
260
  **正确做法**:用 `git diff --binary` 生成 patch 并计算 SHA-256,ledger 只认 `sha256:<hash>` 格式。diffHash 不一致时必须重跑相关测试。
259
261
 
262
+ ### 规则 31:尝试破解验证码 / 人机验证
263
+ **严重度**:❌FAIL(硬性禁止)
264
+ **场景**:本地服务已起来,但业务接口需要真实登录上下文;认证服务登录接口返回 `验证码不能为空` / 返回滑块拼图(如 AJ-Captcha `blockPuzzle`)/ 图形验证码 / 短信验证码
265
+ **后果**:真实日志里 AI 连续写了 4 版求解算法(边缘求和 → NCC 模板匹配 → 二维边缘匹配 → 一维归一化互相关)、反编译服务端 jar 找容差、共 28 次尝试全部失败,烧掉整段会话,最终仍然 BLOCKED
266
+ **正确做法**:**验证码是安全控制,不是待调试的技术问题。** 一旦识别到验证码/人机验证:
267
+
268
+ 1. **立即停止**,不得编写、下载或运行任何验证码求解代码(图像匹配、OCR、打码平台、反编译校验逻辑找容差都在禁止之列)
269
+ 2. 该维度记 `apiTest=BLOCKED`,受影响场景标 🟡 BLOCKED,**不得**伪造 receipt 或把未执行写成通过
270
+ 3. 向用户输出原文,并给出三条可选出路:
271
+ - 用户手工登录后把凭证写入 `.harness/changes/<change-name>/runtime/credential-cache.json`(推荐,执行器本来就先读 cache)
272
+ - 用户在测试环境临时关闭验证码开关,AI 重跑
273
+ - 本轮跳过接口验证,按 BLOCKED 归档,留待环境就绪
274
+ 4. 顺手提示更新 `.harness/config/harness-test-config.md`——认证方式已变更,配置已过期
275
+
276
+ > 判据:只要登录响应里出现 `验证码` / `captcha` / `blockPuzzle` / `slider` / `geetest` / `secretKey + originalImageBase64` 任一特征,即触发本规则。
277
+
278
+ ### 规则 32:预存环境缺陷被当成本次变更的阻塞
279
+ **严重度**:🟡WARN
280
+ **场景**:模块全量验证(如 `unitTestFull`)失败,但失败点在本次 diff 之外——例如别人提交的类新增了无默认值的配置占位符,导致某个全上下文测试无法加载
281
+ **后果**:要么误判成自己的回归浪费时间排查,要么擅自改 tracked 配置 / 排除测试把问题掩盖掉,污染 diff
282
+ **正确做法**:
283
+
284
+ 1. **先证明无关**:`git log -S '<符号>' --oneline` 定位引入提交 + `git diff` 证明本次触点不涉及该文件/目录,两条证据都要有
285
+ 2. **再上报**:按「预存缺陷」向用户说明——受影响模块、失败文件、引入提交、不阻断依据
286
+ 3. **由用户决策**:修根因(会扩大 diff,需明确点头)/ 记 🟡WARN 继续 / 停止本轮
287
+ 4. **禁止自作主张**:不得改 tracked 配置文件、不得 `-Dtest=` 排除失败测试、不得偏离 build-profile 的规范命令——这些都会让证据与 ledger 命令不一致
288
+
289
+ > 用户批准修复后,该修复文件即进入本次 diff:必须在提交前复核它仍在工作区(真实日志里用户中途手工还原了该修复,AI 直到 staging 才发现)。
290
+
260
291
  > diffHash/ledger 复用规则遵循 `../protocols/ledger-protocol.md`;结果证据要求遵循 `../protocols/evidence-based-reporting-protocol.md`。
261
292
 
262
293
  ## BOM-safe JSON / identifier sanitizer / 凭据扫描 (runtime-helpers.mjs)
@@ -736,6 +736,8 @@ TEST_<change-name>_<timestamp>_<short-random>
736
736
 
737
737
  > test 阶段常用子集。`--task` **仅在该 change 启用 checkpoint 时必需**。ledger status 枚举: ok|fail|not_run(没有 PASS)。`record` 还需 `--duration-ms`、`--evidence`,以及 `--files` 或 `--profile-input`+`--project`。
738
738
  > `--skills-root` 仅用于 `begin`(及 `lint-skills`):必须是 adapter 根(如 `.cursor/skills`),不是 `scripts/` 子目录。**`close` 不需要 `--skills-root`**(该子命令不接受此参数)。
739
+ >
740
+ > ⚠️ **`--project` 一律传路径,不传项目名**(在项目根就写 `--project .`)。传项目名会解析成 `<cwd>/<名字>` 并报 `PROJECT_ROOT_INVALID` / `EXECUTION_ROOT_INVALID`。
739
741
 
740
742
  ```powershell
741
743
  # gate begin/close(phase=test;--task 仅 checkpoint 启用时必需;close 不需要 --skills-root)
@@ -746,6 +748,9 @@ python <skills-root>/scripts/harness_gate.py close --change <cn> --phase test --
746
748
  python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification unitTestFull --status ok --command "<完整命令>" --exit-code 0 --duration-ms 120000 --evidence "Tests run: N, Failures: 0" --coverage full --files "packages/core/src/index.ts"
747
749
  python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification browserTest --status ok --command "<真实栈 Playwright 命令>" --exit-code 0 --duration-ms 120000 --evidence "Browser E2E: N passed, 0 failed" --coverage module --files "<Playwright 配置与受测 spec>"
748
750
  python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --verification unitTestFull --profile-input unitTestFull --project <project>
751
+
752
+ # scenario-manifest schemaVersion 2:绑定场景必须带 receipt,先生成骨架再 record
753
+ python <skills-root>/scripts/harness_ledger.py scenario-receipt-template --change-dir <dir> --scenario-ids "API-001,API-002" --runner <runner 名> --out runtime/scenario-receipt-api.json --json
749
754
  ```
750
755
 
751
756
  > **Ledger v3(v2 契约 / split-v1 布局起)**:`record` 强制顶层身份(缺失非零退出、不写账本);`--metrics-json` 必须过 typed schema(unit/apiTest/browserTest/apiContract/dbCompatibility 各有不同必填键);`browserTest` 在报告中投影为 `browserE2E`;dbCompatibility 等不适用验证用 `--applicability NOT_APPLICABLE --applicability-reason "<scope 原因>"`(不计通过也不计失败)。legacy 契约行为不变。详见 `../protocols/ledger-protocol.md` 第十节。
@@ -761,6 +766,10 @@ python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --ve
761
766
  | `record requires --files or a non-empty --profile-input file set` | 缺少输入文件集 | 补 `--files` 或 `--profile-input <key> --project <project>` |
762
767
  | `--profile-input requires --project` | can-reuse/record 展开 profile 需要项目根 | 补 `--project <project>` |
763
768
  | `record` 缺 `--duration-ms` / `--evidence` | 参数为必填 | 按模板补齐 |
769
+ | `PROJECT_ROOT_INVALID` | `--project` 传了项目名而不是路径 | 传 `.` 或绝对路径;报错的 `resolvedProject` 显示实际解析结果 |
770
+ | `SCENARIO_RECEIPT_REQUIRED` | manifest 是 schemaVersion 2,`--scenario-ids` 必须配 receipt | 用 `scenario-receipt-template` 生成骨架 |
771
+ | `SCENARIO_RECEIPT_NOT_FOUND` | receipt 路径找不到 | 看报错的 `triedPaths`;相对路径同时按 CWD 与 `--change-dir` 解析 |
772
+ | `REQUIRED_SCENARIO_NOT_EXECUTED`(test 关门) | `ownerPhase=test` 的场景到 test 阶段仍无通过 receipt——这是真阻塞 | 补跑该场景;接口被验证码挡住时按 `pitfalls.md` 规则 31 记 BLOCKED,不得伪造 receipt |
764
773
 
765
774
  `can-reuse` 的主文案按 `executionNeed` 呈现,错误码仅放技术详情:`first-run` 表示首次执行,`rerun` 表示已有证据因输入或环境变化失效,`evidence-incomplete` 表示旧记录缺少当前契约字段,`reuse` 表示可直接复用。不得把 `VALIDATION_MISSING` 翻译成“强制重跑”。
766
775
 
@@ -219,10 +219,30 @@ def _same_repository(project: Path, candidate: Path) -> bool:
219
219
  return candidate.is_relative_to(project)
220
220
 
221
221
 
222
+ def _contract_error_code(exc: BaseException) -> str:
223
+ """Keep PROJECT_ROOT_INVALID distinct from a genuinely missing change."""
224
+ if str(exc).startswith("PROJECT_ROOT_INVALID"):
225
+ return "PROJECT_ROOT_INVALID"
226
+ return "CHANGE_NOT_FOUND"
227
+
228
+
222
229
  def _contract(project: Path, change: str) -> tuple[Path, dict[str, Any], Path]:
223
230
  root = project.resolve()
224
- contract_root = (root / ".harness" / "changes" / change).resolve()
231
+ # A bare project *name* (--project udp) resolves to <cwd>/udp and then
232
+ # reports CHANGE_NOT_FOUND, which sends the caller hunting for the change
233
+ # instead of fixing the argument. Separate the two failures.
234
+ if not root.is_dir():
235
+ raise ValueError(
236
+ f"PROJECT_ROOT_INVALID: {root} is not a directory — --project takes "
237
+ "a path to the project root (use '.'), not the project name"
238
+ )
225
239
  changes_root = (root / ".harness" / "changes").resolve()
240
+ if not changes_root.is_dir():
241
+ raise ValueError(
242
+ f"PROJECT_ROOT_INVALID: {changes_root} does not exist — "
243
+ f"{root} is not a harness project root"
244
+ )
245
+ contract_root = (root / ".harness" / "changes" / change).resolve()
226
246
  if not contract_root.is_relative_to(changes_root) or not contract_root.is_dir():
227
247
  raise ValueError(f"CHANGE_NOT_FOUND: {change}")
228
248
  context_path = contract_root / "meta" / "change-context.json"
@@ -296,7 +316,7 @@ def configure_phase_plan(
296
316
  try:
297
317
  contract_root, _contract_data, _state_root = _contract(project, change)
298
318
  except (OSError, ValueError, json.JSONDecodeError) as exc:
299
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
319
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
300
320
  normalized = [str(item).strip() for item in phases if str(item).strip()]
301
321
  if (
302
322
  not normalized
@@ -802,7 +822,7 @@ def close_transition(
802
822
  try:
803
823
  contract_root, _contract_data, state_root = _contract(project, change)
804
824
  except (OSError, ValueError, json.JSONDecodeError) as exc:
805
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
825
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
806
826
  allowed_next = _allowed_next_phases(contract_root, from_phase)
807
827
  if to_phase not in allowed_next:
808
828
  planned_phases, source = _phase_plan(contract_root)
@@ -962,7 +982,7 @@ def _begin_transition_unlocked(
962
982
  try:
963
983
  contract_root, _contract_data, state_root = _contract(project, change)
964
984
  except (OSError, ValueError, json.JSONDecodeError) as exc:
965
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
985
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
966
986
  paths = _paths(state_root)
967
987
  try:
968
988
  execution_root = _execution_root(project, contract_root, state_root)
@@ -1074,7 +1094,7 @@ def begin_transition(
1074
1094
  try:
1075
1095
  _contract_root, _contract_data, state_root = _contract(project, change)
1076
1096
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1077
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1097
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1078
1098
  paths = _paths(state_root)
1079
1099
  with _exclusive_state_lock(paths["runtime"] / "branch-selection.lock"):
1080
1100
  return _begin_transition_unlocked(
@@ -1100,7 +1120,7 @@ def cancel_prepared_context(
1100
1120
  try:
1101
1121
  _contract_root, _contract_data, state_root = _contract(project, change)
1102
1122
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1103
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1123
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1104
1124
  paths = _paths(state_root)
1105
1125
  removed: list[str] = []
1106
1126
  receipt_hash: str | None = None
@@ -1186,7 +1206,7 @@ def context_view(project: Path, change: str) -> dict[str, Any]:
1186
1206
  try:
1187
1207
  contract_root, contract, state_root = _contract(project, change)
1188
1208
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1189
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1209
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1190
1210
  paths = _paths(state_root)
1191
1211
  transitions = _read_ndjson(paths["transitions"])
1192
1212
  begins = _read_ndjson(paths["begins"])
@@ -704,7 +704,11 @@ def append_event(
704
704
  return {
705
705
  "ok": False,
706
706
  "code": "PHASE_ALREADY_CLOSED",
707
- "message": "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same attempt",
707
+ "message": (
708
+ "PHASE_ALREADY_CLOSED: refusing a second phase.end for the "
709
+ "same attempt. A retry needs a fresh run id AND the next "
710
+ "attempt number (attempts are global per phase, not per run id)."
711
+ ),
708
712
  "event": event,
709
713
  "autoSealed": result.get("autoSealed") or [],
710
714
  }
@@ -1931,7 +1935,9 @@ def cmd_append(args: argparse.Namespace) -> int:
1931
1935
  ):
1932
1936
  attempt_error = (
1933
1937
  "EVENT_ATTEMPT_CONFLICT: run_id is already bound to "
1934
- f"attempt {expected_attempt}, received {explicit_attempt}"
1938
+ f"attempt {expected_attempt}, received "
1939
+ f"{explicit_attempt}. One run id maps to exactly one "
1940
+ "attempt — allocate a fresh run id for the next attempt."
1935
1941
  )
1936
1942
  else:
1937
1943
  event["attempt"] = expected_attempt
@@ -1972,7 +1978,10 @@ def cmd_append(args: argparse.Namespace) -> int:
1972
1978
  return emit_error(f"append failed: {exc}", as_json=as_json)
1973
1979
  if phase_closed:
1974
1980
  return emit_error(
1975
- "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same attempt",
1981
+ "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same "
1982
+ "attempt. A retry needs a fresh run id AND the next attempt number "
1983
+ "(attempts are global per phase, not per run id). For a published "
1984
+ "plan use harness_plan_finalize.py republish, which allocates both.",
1976
1985
  as_json=as_json,
1977
1986
  error_code="PHASE_ALREADY_CLOSED",
1978
1987
  )