@hunter-harness/workflow-harness 0.2.10 → 0.2.12

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 (250) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/contracts/ci-metrics.schema.json +36 -0
  3. package/harness/bundles/general/claude-code/contracts/workflow-policy.json +30 -0
  4. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  8. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +4 -4
  10. package/harness/bundles/general/claude-code/harness-plan/checklist.md +6 -0
  11. package/harness/bundles/general/claude-code/harness-plan/reference.md +7 -2
  12. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  14. package/harness/bundles/general/claude-code/harness-run/checklist.md +3 -2
  15. package/harness/bundles/general/claude-code/harness-run/reference.md +6 -4
  16. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  17. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  18. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  19. package/harness/bundles/general/claude-code/scripts/harness_archive.py +282 -120
  20. package/harness/bundles/general/claude-code/scripts/harness_events.py +400 -24
  21. package/harness/bundles/general/claude-code/scripts/harness_gate.py +585 -21
  22. package/harness/bundles/general/claude-code/scripts/harness_integration.py +403 -18
  23. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +225 -29
  24. package/harness/bundles/general/claude-code/scripts/harness_phase.py +926 -0
  25. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +361 -0
  26. package/harness/bundles/general/claude-code/scripts/harness_profile.py +50 -0
  27. package/harness/bundles/general/claude-code/scripts/harness_runtime.py +248 -0
  28. package/harness/bundles/general/claude-code/scripts/harness_state.py +15 -2
  29. package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +188 -0
  30. package/harness/bundles/general/claude-code/scripts/harness_workflow_policy.py +19 -0
  31. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  32. package/harness/bundles/general/codebuddy/contracts/ci-metrics.schema.json +36 -0
  33. package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +30 -0
  34. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  35. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  36. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  38. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  39. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +4 -4
  40. package/harness/bundles/general/codebuddy/harness-plan/checklist.md +6 -0
  41. package/harness/bundles/general/codebuddy/harness-plan/reference.md +7 -2
  42. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  44. package/harness/bundles/general/codebuddy/harness-run/checklist.md +3 -2
  45. package/harness/bundles/general/codebuddy/harness-run/reference.md +6 -4
  46. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  47. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  48. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  49. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +282 -120
  50. package/harness/bundles/general/codebuddy/scripts/harness_events.py +400 -24
  51. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +585 -21
  52. package/harness/bundles/general/codebuddy/scripts/harness_integration.py +403 -18
  53. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +225 -29
  54. package/harness/bundles/general/codebuddy/scripts/harness_phase.py +926 -0
  55. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +361 -0
  56. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +50 -0
  57. package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +248 -0
  58. package/harness/bundles/general/codebuddy/scripts/harness_state.py +15 -2
  59. package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +188 -0
  60. package/harness/bundles/general/codebuddy/scripts/harness_workflow_policy.py +19 -0
  61. package/harness/bundles/general/codex/.harness-build.json +1 -1
  62. package/harness/bundles/general/codex/contracts/ci-metrics.schema.json +36 -0
  63. package/harness/bundles/general/codex/contracts/workflow-policy.json +30 -0
  64. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  67. package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  68. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  69. package/harness/bundles/general/codex/harness-plan/SKILL.md +4 -4
  70. package/harness/bundles/general/codex/harness-plan/checklist.md +6 -0
  71. package/harness/bundles/general/codex/harness-plan/reference.md +7 -2
  72. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  73. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  74. package/harness/bundles/general/codex/harness-run/checklist.md +3 -2
  75. package/harness/bundles/general/codex/harness-run/reference.md +6 -4
  76. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  77. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  78. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  79. package/harness/bundles/general/codex/scripts/harness_archive.py +282 -120
  80. package/harness/bundles/general/codex/scripts/harness_events.py +400 -24
  81. package/harness/bundles/general/codex/scripts/harness_gate.py +585 -21
  82. package/harness/bundles/general/codex/scripts/harness_integration.py +403 -18
  83. package/harness/bundles/general/codex/scripts/harness_ledger.py +225 -29
  84. package/harness/bundles/general/codex/scripts/harness_phase.py +926 -0
  85. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +361 -0
  86. package/harness/bundles/general/codex/scripts/harness_profile.py +50 -0
  87. package/harness/bundles/general/codex/scripts/harness_runtime.py +248 -0
  88. package/harness/bundles/general/codex/scripts/harness_state.py +15 -2
  89. package/harness/bundles/general/codex/scripts/harness_test_guard.py +188 -0
  90. package/harness/bundles/general/codex/scripts/harness_workflow_policy.py +19 -0
  91. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  92. package/harness/bundles/general/cursor/contracts/ci-metrics.schema.json +36 -0
  93. package/harness/bundles/general/cursor/contracts/workflow-policy.json +30 -0
  94. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  95. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  96. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  97. package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  98. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  99. package/harness/bundles/general/cursor/harness-plan/SKILL.md +4 -4
  100. package/harness/bundles/general/cursor/harness-plan/checklist.md +6 -0
  101. package/harness/bundles/general/cursor/harness-plan/reference.md +7 -2
  102. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  103. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  104. package/harness/bundles/general/cursor/harness-run/checklist.md +3 -2
  105. package/harness/bundles/general/cursor/harness-run/reference.md +6 -4
  106. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  107. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  108. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  109. package/harness/bundles/general/cursor/scripts/harness_archive.py +282 -120
  110. package/harness/bundles/general/cursor/scripts/harness_events.py +400 -24
  111. package/harness/bundles/general/cursor/scripts/harness_gate.py +585 -21
  112. package/harness/bundles/general/cursor/scripts/harness_integration.py +403 -18
  113. package/harness/bundles/general/cursor/scripts/harness_ledger.py +225 -29
  114. package/harness/bundles/general/cursor/scripts/harness_phase.py +926 -0
  115. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +361 -0
  116. package/harness/bundles/general/cursor/scripts/harness_profile.py +50 -0
  117. package/harness/bundles/general/cursor/scripts/harness_runtime.py +248 -0
  118. package/harness/bundles/general/cursor/scripts/harness_state.py +15 -2
  119. package/harness/bundles/general/cursor/scripts/harness_test_guard.py +188 -0
  120. package/harness/bundles/general/cursor/scripts/harness_workflow_policy.py +19 -0
  121. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  122. package/harness/bundles/java/claude-code/contracts/ci-metrics.schema.json +36 -0
  123. package/harness/bundles/java/claude-code/contracts/workflow-policy.json +30 -0
  124. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  125. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  126. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  127. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  128. package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  129. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  130. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  131. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +4 -4
  132. package/harness/bundles/java/claude-code/harness-plan/checklist.md +6 -0
  133. package/harness/bundles/java/claude-code/harness-plan/reference.md +7 -2
  134. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  135. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  136. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  137. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  138. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  139. package/harness/bundles/java/claude-code/scripts/harness_archive.py +282 -120
  140. package/harness/bundles/java/claude-code/scripts/harness_events.py +400 -24
  141. package/harness/bundles/java/claude-code/scripts/harness_gate.py +585 -21
  142. package/harness/bundles/java/claude-code/scripts/harness_integration.py +403 -18
  143. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +225 -29
  144. package/harness/bundles/java/claude-code/scripts/harness_phase.py +926 -0
  145. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +361 -0
  146. package/harness/bundles/java/claude-code/scripts/harness_profile.py +50 -0
  147. package/harness/bundles/java/claude-code/scripts/harness_runtime.py +248 -0
  148. package/harness/bundles/java/claude-code/scripts/harness_state.py +15 -2
  149. package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +188 -0
  150. package/harness/bundles/java/claude-code/scripts/harness_workflow_policy.py +19 -0
  151. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  152. package/harness/bundles/java/codebuddy/contracts/ci-metrics.schema.json +36 -0
  153. package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +30 -0
  154. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  155. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  156. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  157. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  158. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  159. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  160. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  161. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +4 -4
  162. package/harness/bundles/java/codebuddy/harness-plan/checklist.md +6 -0
  163. package/harness/bundles/java/codebuddy/harness-plan/reference.md +7 -2
  164. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  165. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  166. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  167. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  168. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  169. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +282 -120
  170. package/harness/bundles/java/codebuddy/scripts/harness_events.py +400 -24
  171. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +585 -21
  172. package/harness/bundles/java/codebuddy/scripts/harness_integration.py +403 -18
  173. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +225 -29
  174. package/harness/bundles/java/codebuddy/scripts/harness_phase.py +926 -0
  175. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +361 -0
  176. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +50 -0
  177. package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +248 -0
  178. package/harness/bundles/java/codebuddy/scripts/harness_state.py +15 -2
  179. package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +188 -0
  180. package/harness/bundles/java/codebuddy/scripts/harness_workflow_policy.py +19 -0
  181. package/harness/bundles/java/codex/.harness-build.json +1 -1
  182. package/harness/bundles/java/codex/contracts/ci-metrics.schema.json +36 -0
  183. package/harness/bundles/java/codex/contracts/workflow-policy.json +30 -0
  184. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  185. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  186. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  187. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  188. package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  189. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  190. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  191. package/harness/bundles/java/codex/harness-plan/SKILL.md +4 -4
  192. package/harness/bundles/java/codex/harness-plan/checklist.md +6 -0
  193. package/harness/bundles/java/codex/harness-plan/reference.md +7 -2
  194. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  195. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  196. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  197. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  198. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  199. package/harness/bundles/java/codex/scripts/harness_archive.py +282 -120
  200. package/harness/bundles/java/codex/scripts/harness_events.py +400 -24
  201. package/harness/bundles/java/codex/scripts/harness_gate.py +585 -21
  202. package/harness/bundles/java/codex/scripts/harness_integration.py +403 -18
  203. package/harness/bundles/java/codex/scripts/harness_ledger.py +225 -29
  204. package/harness/bundles/java/codex/scripts/harness_phase.py +926 -0
  205. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +361 -0
  206. package/harness/bundles/java/codex/scripts/harness_profile.py +50 -0
  207. package/harness/bundles/java/codex/scripts/harness_runtime.py +248 -0
  208. package/harness/bundles/java/codex/scripts/harness_state.py +15 -2
  209. package/harness/bundles/java/codex/scripts/harness_test_guard.py +188 -0
  210. package/harness/bundles/java/codex/scripts/harness_workflow_policy.py +19 -0
  211. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  212. package/harness/bundles/java/cursor/contracts/ci-metrics.schema.json +36 -0
  213. package/harness/bundles/java/cursor/contracts/workflow-policy.json +30 -0
  214. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  215. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  216. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  217. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  218. package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +24 -2
  219. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  220. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  221. package/harness/bundles/java/cursor/harness-plan/SKILL.md +4 -4
  222. package/harness/bundles/java/cursor/harness-plan/checklist.md +6 -0
  223. package/harness/bundles/java/cursor/harness-plan/reference.md +7 -2
  224. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  225. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  226. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  227. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  228. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  229. package/harness/bundles/java/cursor/scripts/harness_archive.py +282 -120
  230. package/harness/bundles/java/cursor/scripts/harness_events.py +400 -24
  231. package/harness/bundles/java/cursor/scripts/harness_gate.py +585 -21
  232. package/harness/bundles/java/cursor/scripts/harness_integration.py +403 -18
  233. package/harness/bundles/java/cursor/scripts/harness_ledger.py +225 -29
  234. package/harness/bundles/java/cursor/scripts/harness_phase.py +926 -0
  235. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +361 -0
  236. package/harness/bundles/java/cursor/scripts/harness_profile.py +50 -0
  237. package/harness/bundles/java/cursor/scripts/harness_runtime.py +248 -0
  238. package/harness/bundles/java/cursor/scripts/harness_state.py +15 -2
  239. package/harness/bundles/java/cursor/scripts/harness_test_guard.py +188 -0
  240. package/harness/bundles/java/cursor/scripts/harness_workflow_policy.py +19 -0
  241. package/harness/manifests/general/claude-code.json +43 -27
  242. package/harness/manifests/general/codebuddy.json +43 -27
  243. package/harness/manifests/general/codex.json +43 -27
  244. package/harness/manifests/general/cursor.json +43 -27
  245. package/harness/manifests/java/claude-code.json +43 -27
  246. package/harness/manifests/java/codebuddy.json +43 -27
  247. package/harness/manifests/java/codex.json +43 -27
  248. package/harness/manifests/java/cursor.json +43 -27
  249. package/hunter-workflow-family.json +2 -2
  250. package/package.json +1 -1
@@ -266,6 +266,8 @@ def capture_current_state(
266
266
  segment_files=files,
267
267
  )
268
268
  changed: list[str] = []
269
+ baseline_created = previous is None
270
+ unresolved: list[str] = []
269
271
  if previous:
270
272
  old_segments = previous.get("segments") or {}
271
273
  for name, segment in fresh["segments"].items():
@@ -275,7 +277,12 @@ def capture_current_state(
275
277
  else:
276
278
  changed.append(name)
277
279
  else:
278
- changed = list(fresh["segments"])
280
+ # A first observation establishes the baseline; it is not evidence
281
+ # that every segment changed during the current phase.
282
+ unresolved = sorted(fresh["segments"])
283
+ fresh["baselineCreated"] = baseline_created
284
+ fresh["changedSegments"] = sorted(changed)
285
+ fresh["unresolvedSegments"] = unresolved
279
286
  write_snapshot(change_dir, fresh)
280
287
  return fresh, changed
281
288
 
@@ -312,7 +319,13 @@ def main(argv: list[str] | None = None) -> int:
312
319
  "ok": True,
313
320
  "path": str(snapshot_path(change_dir)),
314
321
  "changedSegments": changed,
315
- "reusedSegments": sorted(set(snapshot["segments"]) - set(changed)),
322
+ "reusedSegments": sorted(
323
+ set(snapshot["segments"])
324
+ - set(changed)
325
+ - set(snapshot.get("unresolvedSegments") or [])
326
+ ),
327
+ "baselineCreated": bool(snapshot.get("baselineCreated")),
328
+ "unresolvedSegments": list(snapshot.get("unresolvedSegments") or []),
316
329
  "git": snapshot["git"],
317
330
  }
318
331
  if args.json:
@@ -564,6 +564,159 @@ def record(
564
564
  return _result(True, action, "RECORDED", [rel for _, rel in validated], manifestPath=str(manifest_path))
565
565
 
566
566
 
567
+ def rehome(
568
+ from_project: Path | str,
569
+ to_project: Path | str,
570
+ change_dir: Path | str,
571
+ expected_head: str,
572
+ ) -> dict[str, Any]:
573
+ """Atomically hand test ownership from a merged feature worktree to target."""
574
+ action = "rehome"
575
+ from_root = Path(from_project).resolve()
576
+ to_root = Path(to_project).resolve()
577
+ if not from_root.is_dir() or not to_root.is_dir():
578
+ return _result(False, action, "PROJECT_ROOT_MISSING", [])
579
+ if harness_paths.repository_identity(from_root) != harness_paths.repository_identity(to_root):
580
+ return _result(False, action, "REPOSITORY_MISMATCH", [])
581
+ to_head_result = _git(to_root, "rev-parse", "--verify", "HEAD")
582
+ to_head = to_head_result.stdout.strip() if to_head_result.returncode == 0 else ""
583
+ if not expected_head or to_head != expected_head:
584
+ return _result(
585
+ False, action, "EXPECTED_HEAD_MISMATCH", [],
586
+ expectedHead=expected_head, actualHead=to_head,
587
+ )
588
+ from_head_result = _git(from_root, "rev-parse", "--verify", "HEAD")
589
+ from_head = from_head_result.stdout.strip() if from_head_result.returncode == 0 else ""
590
+ from_tree_result = _git(from_root, "rev-parse", "HEAD^{tree}")
591
+ to_tree_result = _git(to_root, "rev-parse", f"{expected_head}^{{tree}}")
592
+ from_tree = from_tree_result.stdout.strip() if from_tree_result.returncode == 0 else ""
593
+ to_tree = to_tree_result.stdout.strip() if to_tree_result.returncode == 0 else ""
594
+ if not from_tree or from_tree != to_tree:
595
+ return _result(
596
+ False, action, "TREE_MISMATCH", [],
597
+ fromHead=from_head, toHead=to_head, fromTree=from_tree, toTree=to_tree,
598
+ )
599
+
600
+ change_root = _change_dir(from_root, change_dir)
601
+ if change_root is None:
602
+ return _result(False, action, "CHANGE_DIR_OUTSIDE_PROJECT", [])
603
+ manifest_path = _manifest_path(change_root)
604
+ if manifest_path is None:
605
+ return _result(False, action, "MANIFEST_PATH_OUTSIDE_PROJECT", [])
606
+ lock_path = manifest_path.with_name(manifest_path.name + ".lock")
607
+ try:
608
+ with _exclusive_lock(lock_path, wait_seconds=5.0):
609
+ try:
610
+ manifest = _read_json(manifest_path)
611
+ except FileNotFoundError:
612
+ return _result(False, action, "MANIFEST_MISSING", [])
613
+ except (OSError, json.JSONDecodeError) as exc:
614
+ return _result(False, action, "MANIFEST_INVALID", [], error=str(exc))
615
+ is_v2 = isinstance(manifest, dict) and manifest.get("schemaVersion") == 2
616
+ entries = manifest.get("files") if isinstance(manifest, dict) else None
617
+ if not isinstance(entries, list):
618
+ return _result(False, action, "MANIFEST_INVALID", [])
619
+ if not is_v2 and (
620
+ manifest.get("schemaVersion") != SCHEMA_VERSION
621
+ or manifest.get("mode") != MODE
622
+ or manifest.get("projectRoot") != str(from_root)
623
+ ):
624
+ return _result(False, action, "MANIFEST_PROJECT_MISMATCH", [])
625
+ if is_v2:
626
+ error, validated = _validate_existing_manifest_v2(
627
+ from_root, manifest, require_files=False
628
+ )
629
+ if error:
630
+ return _result(False, action, error, [])
631
+ else:
632
+ validated: dict[str, dict[str, Any]] = {}
633
+ for item in entries:
634
+ if not _entry_shape_valid(item):
635
+ return _result(False, action, "MANIFEST_INVALID", [])
636
+ rel = item["path"]
637
+ source, normalized, error = _validate_file(from_root, rel)
638
+ if error or normalized != rel or source is None:
639
+ return _result(False, action, error or "MANIFEST_INVALID", [rel])
640
+ if item["sha256"] != _sha256(source):
641
+ return _result(False, action, "HASH_DRIFT", [rel])
642
+ validated[rel] = dict(item)
643
+
644
+ updated = json.loads(json.dumps(manifest))
645
+ updated_entries: list[dict[str, Any]] = []
646
+ for rel, item in sorted(validated.items()):
647
+ target, normalized, error = _validate_file(to_root, rel)
648
+ if error or normalized != rel or target is None:
649
+ return _result(False, action, "TARGET_CONTENT_MISMATCH", [rel])
650
+ replacement = dict(item)
651
+ if is_v2:
652
+ digest = logical_file_hash(to_root, rel)
653
+ source_digest = item.get("logicalHash") or item.get("binaryHash")
654
+ if digest != source_digest:
655
+ return _result(False, action, "TARGET_CONTENT_MISMATCH", [rel])
656
+ replacement["logicalHash"] = digest
657
+ replacement["binaryHash"] = (
658
+ None if digest.startswith("gitblob:") else digest
659
+ )
660
+ replacement["ignored"] = _is_ignored(to_root, rel)
661
+ else:
662
+ digest = _sha256(target)
663
+ if digest != item["sha256"]:
664
+ return _result(False, action, "TARGET_CONTENT_MISMATCH", [rel])
665
+ replacement["sha256"] = digest
666
+ replacement["ignored"] = _is_ignored(to_root, rel)
667
+ replacement["trackedBefore"] = (
668
+ _git(to_root, "ls-files", "--error-unmatch", "--", rel).returncode == 0
669
+ )
670
+ updated_entries.append(replacement)
671
+
672
+ before_hash = _sha256(manifest_path)
673
+ handoff_at = time.strftime("%Y-%m-%dT%H:%M:%S")
674
+ handoff_id = "handoff-" + hashlib.sha256(
675
+ f"{from_root}\0{to_root}\0{from_head}\0{to_head}".encode("utf-8")
676
+ ).hexdigest()[:20]
677
+ handoffs = updated.get("handoffs")
678
+ if handoffs is None:
679
+ handoffs = []
680
+ if not isinstance(handoffs, list):
681
+ return _result(False, action, "MANIFEST_INVALID", [])
682
+ handoff = {
683
+ "id": handoff_id,
684
+ "fromRoot": str(from_root),
685
+ "toRoot": str(to_root),
686
+ "fromHead": from_head,
687
+ "toHead": to_head,
688
+ "expectedHead": expected_head,
689
+ "treeHash": to_tree,
690
+ "at": handoff_at,
691
+ "manifestHashBefore": before_hash,
692
+ }
693
+ if not any(isinstance(item, dict) and item.get("id") == handoff_id for item in handoffs):
694
+ handoffs.append(handoff)
695
+ updated["files"] = updated_entries
696
+ updated["projectRoot"] = str(to_root)
697
+ updated["head"] = to_head
698
+ updated["handoffs"] = handoffs
699
+ _write_json(manifest_path, updated)
700
+ after_hash = _sha256(manifest_path)
701
+ except LockUnavailable:
702
+ return _result(False, action, "MANIFEST_LOCKED", [])
703
+ return _result(
704
+ True,
705
+ action,
706
+ "REHOMED",
707
+ sorted(validated),
708
+ fromRoot=str(from_root),
709
+ toRoot=str(to_root),
710
+ fromHead=from_head,
711
+ toHead=to_head,
712
+ treeHash=to_tree,
713
+ handoffId=handoff_id,
714
+ manifestHashBefore=before_hash,
715
+ manifestHashAfter=after_hash,
716
+ manifestPath=str(manifest_path),
717
+ )
718
+
719
+
567
720
  def _stage_locked(
568
721
  project_root: Path, change_root: Path, manifest_path: Path, index_path: Path
569
722
  ) -> dict[str, Any]:
@@ -709,6 +862,31 @@ def begin(project: Path | str, change_dir: Path | str) -> dict[str, Any]:
709
862
  if not _manifest_target_inside(change_root, snapshot_path):
710
863
  return _result(False, action, "SNAPSHOT_PATH_OUTSIDE_PROJECT", [])
711
864
 
865
+ if snapshot_path.is_file():
866
+ try:
867
+ snapshot = _read_json(snapshot_path)
868
+ except (OSError, json.JSONDecodeError) as exc:
869
+ return _result(False, action, "SNAPSHOT_INVALID", [], error=str(exc))
870
+ entries = snapshot.get("files") if isinstance(snapshot, dict) else None
871
+ if (
872
+ not isinstance(snapshot, dict)
873
+ or snapshot.get("schemaVersion") != SCHEMA_VERSION
874
+ or snapshot.get("mode") != MODE
875
+ or snapshot.get("projectRoot") != str(project_root)
876
+ or not isinstance(entries, list)
877
+ or any(not isinstance(item, dict) or not isinstance(item.get("path"), str)
878
+ for item in entries)
879
+ ):
880
+ return _result(False, action, "SNAPSHOT_INVALID", [])
881
+ return _result(
882
+ True,
883
+ action,
884
+ "SNAPSHOT_REUSED",
885
+ [item["path"] for item in entries],
886
+ snapshotPath=str(snapshot_path),
887
+ fileCount=len(entries),
888
+ )
889
+
712
890
  files = _enumerate_allowed_test_files(project_root)
713
891
  snapshot = {
714
892
  "schemaVersion": SCHEMA_VERSION,
@@ -867,6 +1045,12 @@ def main(argv: list[str] | None = None) -> int:
867
1045
  close_parser.add_argument("--project", required=True)
868
1046
  close_parser.add_argument("--change-dir", required=True)
869
1047
  close_parser.add_argument("--json", action="store_true")
1048
+ rehome_parser = sub.add_parser("rehome")
1049
+ rehome_parser.add_argument("--from", dest="from_project", required=True)
1050
+ rehome_parser.add_argument("--to", dest="to_project", required=True)
1051
+ rehome_parser.add_argument("--change-dir", required=True)
1052
+ rehome_parser.add_argument("--expected-head", required=True)
1053
+ rehome_parser.add_argument("--json", action="store_true")
870
1054
  mark_parser = sub.add_parser("mark")
871
1055
  mark_parser.add_argument("--project", required=True)
872
1056
  mark_parser.add_argument("--change-dir", required=True)
@@ -883,6 +1067,10 @@ def main(argv: list[str] | None = None) -> int:
883
1067
  elif args.action == "mark":
884
1068
  files = [item.strip() for item in args.files.split(",") if item.strip()]
885
1069
  result = mark(args.project, args.change_dir, files)
1070
+ elif args.action == "rehome":
1071
+ result = rehome(
1072
+ args.from_project, args.to_project, args.change_dir, args.expected_head
1073
+ )
886
1074
  else:
887
1075
  result = stage(args.project, args.change_dir)
888
1076
  print(json.dumps(result, ensure_ascii=False, indent=2 if args.json else None))
@@ -19,6 +19,8 @@ TOP_LEVEL_KEYS = frozenset(
19
19
  "skills",
20
20
  "requiredArtifacts",
21
21
  "requiredValidations",
22
+ "validationPhases",
23
+ "capabilityGates",
22
24
  "conditionalStages",
23
25
  "interactionWhitelist",
24
26
  "checkpointRules",
@@ -48,6 +50,9 @@ SKILL_KEYS = frozenset(
48
50
  )
49
51
 
50
52
  CONDITIONAL_STAGE_KEYS = frozenset({"tiers", "signals"})
53
+ CAPABILITY_GATE_KEYS = frozenset(
54
+ {"signals", "requiredStages", "requiredValidations"}
55
+ )
51
56
  CHECKPOINT_RULE_KEYS = frozenset(
52
57
  {
53
58
  "afterTasks",
@@ -121,6 +126,20 @@ def validate_policy(data: Any) -> dict[str, Any]:
121
126
  for phase, items in section_data.items():
122
127
  _require_type(items, list, f"{section}.{phase}")
123
128
 
129
+ validation_phases = data["validationPhases"]
130
+ _require_type(validation_phases, dict, "validationPhases")
131
+ for verification, phase in validation_phases.items():
132
+ _require_type(phase, str, f"validationPhases.{verification}")
133
+
134
+ capability_gates = data["capabilityGates"]
135
+ _require_type(capability_gates, dict, "capabilityGates")
136
+ for capability, gate in capability_gates.items():
137
+ path = f"capabilityGates.{capability}"
138
+ _require_type(gate, dict, path)
139
+ _reject_unknown(gate, CAPABILITY_GATE_KEYS, path)
140
+ for key in CAPABILITY_GATE_KEYS:
141
+ _require_type(gate[key], list, f"{path}.{key}")
142
+
124
143
  conditional = data["conditionalStages"]
125
144
  _require_type(conditional, dict, "conditionalStages")
126
145
  for stage_name, stage in conditional.items():
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "codebuddy",
5
- "coreHash": "43088896a4556b69"
5
+ "coreHash": "8dd8ce9aa1ac47f5"
6
6
  }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hunter-harness.dev/contracts/ci-metrics.schema.json",
4
+ "title": "Hunter Harness CI Metrics",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "runner",
10
+ "source",
11
+ "suites",
12
+ "passed",
13
+ "failed",
14
+ "skipped",
15
+ "setupErrors",
16
+ "durationMs",
17
+ "headSha"
18
+ ],
19
+ "properties": {
20
+ "schemaVersion": { "const": 1 },
21
+ "runner": { "enum": ["junit", "vitest", "playwright"] },
22
+ "source": { "type": ["string", "null"] },
23
+ "suites": { "type": ["integer", "null"], "minimum": 0 },
24
+ "passed": { "type": ["integer", "null"], "minimum": 0 },
25
+ "failed": { "type": ["integer", "null"], "minimum": 0 },
26
+ "skipped": { "type": ["integer", "null"], "minimum": 0 },
27
+ "setupErrors": { "type": ["integer", "null"], "minimum": 0 },
28
+ "durationMs": { "type": ["integer", "null"], "minimum": 0 },
29
+ "headSha": {
30
+ "anyOf": [
31
+ { "type": "null" },
32
+ { "type": "string", "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$" }
33
+ ]
34
+ }
35
+ }
36
+ }
@@ -138,6 +138,36 @@
138
138
  "submit": [],
139
139
  "package": ["package"]
140
140
  },
141
+ "validationPhases": {
142
+ "compile": "run",
143
+ "unitTest": "run",
144
+ "unitTestFull": "test",
145
+ "apiTest": "test",
146
+ "dbCompatibility": "test",
147
+ "package": "package"
148
+ },
149
+ "capabilityGates": {
150
+ "deployment": {
151
+ "signals": ["deploy-required"],
152
+ "requiredStages": ["package"],
153
+ "requiredValidations": ["package"]
154
+ },
155
+ "container": {
156
+ "signals": ["install-verification"],
157
+ "requiredStages": ["package"],
158
+ "requiredValidations": ["package"]
159
+ },
160
+ "api": {
161
+ "signals": ["api-diff-present"],
162
+ "requiredStages": ["apidoc"],
163
+ "requiredValidations": ["apiTest"]
164
+ },
165
+ "database": {
166
+ "signals": ["migration"],
167
+ "requiredStages": [],
168
+ "requiredValidations": ["dbCompatibility"]
169
+ }
170
+ },
141
171
  "conditionalStages": {
142
172
  "review": {
143
173
  "tiers": ["standard", "full"],
@@ -3,7 +3,7 @@ name: harness-archive
3
3
  description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到
4
4
  .harness/archive/,含归档元数据和可视化最终总结。使用场景:归档、完成归档、收尾、打包产出物
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-archive — 变更归档
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-codebase-map
3
3
  description: 分析现有代码库并生成 .harness/codebase/map/
4
4
  下的结构化代码库地图。用于陌生项目理解、重大改造前梳理、harness-sync 提示 map 过期后的人工刷新。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-codebase-map — 代码库地图
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-knowledge-ingest
3
3
  description: 从 .harness/archive 归档整理、同步和维护项目知识索引。适用场景:ingest knowledge、sync
4
4
  knowledge、rebuild knowledge index、promote knowledge、确认知识条目、检查知识库是否过期。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-knowledge-ingest
8
8
 
9
9
  从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
@@ -1823,6 +1823,19 @@ def compute_inputs_hash(
1823
1823
  versions + preserved entries (active / manually demoted), so promote/demote
1824
1824
  correctly invalidate the no-op fast path.
1825
1825
  """
1826
+ return compute_inputs_hash_from_preserved(
1827
+ records,
1828
+ config,
1829
+ load_preserved_entries(knowledge),
1830
+ )
1831
+
1832
+
1833
+ def compute_inputs_hash_from_preserved(
1834
+ records: list[dict[str, Any]],
1835
+ config: dict[str, Any],
1836
+ preserved_entries: list[dict[str, Any]],
1837
+ ) -> str:
1838
+ """Hash knowledge inputs using an already-resolved preserved closure."""
1826
1839
  archive_fingerprint = json.dumps(
1827
1840
  sorted(
1828
1841
  (
@@ -1843,7 +1856,6 @@ def compute_inputs_hash(
1843
1856
  },
1844
1857
  sort_keys=True,
1845
1858
  )
1846
- preserved = load_preserved_entries(knowledge)
1847
1859
  preserved_fingerprint = json.dumps(
1848
1860
  sorted(
1849
1861
  (
@@ -1852,7 +1864,7 @@ def compute_inputs_hash(
1852
1864
  "status": e.get("status"),
1853
1865
  "lifecycle": e.get("lifecycle", {}),
1854
1866
  }
1855
- for e in preserved
1867
+ for e in preserved_entries
1856
1868
  ),
1857
1869
  key=lambda item: item["id"] or "",
1858
1870
  ),
@@ -2120,6 +2132,16 @@ def build_index(
2120
2132
  ingest_mode["confidenceScored"] = len(indexed_entries)
2121
2133
  ingest_mode["entriesPruned"] = prune_generated_entries(knowledge, indexed_entries)
2122
2134
 
2135
+ # Lifecycle policies above may relocate or mutate preserved entries. The
2136
+ # pre-build hash remains useful for the no-op gate, but the persisted index
2137
+ # must describe the final preserved closure or an immediate sync will mark
2138
+ # the freshly rebuilt index stale.
2139
+ ingest_mode["inputsHash"] = compute_inputs_hash_from_preserved(
2140
+ archive_records,
2141
+ config,
2142
+ load_preserved_entries(knowledge),
2143
+ )
2144
+
2123
2145
  sqlite_stats = write_sqlite(knowledge / "index.sqlite", indexed_entries)
2124
2146
  ingest_mode.update(sqlite_stats)
2125
2147
  index = make_manifest(
@@ -3,7 +3,7 @@ name: harness-knowledge-query
3
3
  description: 根据新需求、设计任务、代码修改请求或问题排查,在规划和编码前查询 .harness/knowledge
4
4
  历史上下文。适用场景:query knowledge、查找历史需求、根据归档理解需求、继续之前类似开发、生成需求上下文包。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-knowledge-query
8
8
 
9
9
  并行 change 中查询必须传 `--change <change-id>`;成功后以该 change 的 `meta/knowledge-context.json` 为稳定指针。全局 `context-packs/latest.json` 仅向后兼容,不得用于判断多个 active change 的上下文归属。
@@ -2,7 +2,7 @@
2
2
  name: harness-plan
3
3
  description: 将需求转化为设计文档+实施计划+完整测试场景表,必须在编码前完成。使用场景:需求分析、feature plan、技术方案设计、实现方案规划
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
6
6
  # harness-plan — 需求规划
7
7
 
8
8
  ## Purpose
@@ -58,7 +58,7 @@ description: 将需求转化为设计文档+实施计划+完整测试场景表
58
58
 
59
59
  | 阶段 | 动作 |
60
60
  |------|------|
61
- | 0 | git status;脏工作区 → baseline 隔离 + `decision`,不询问 |
61
+ | 0 | 用当前解释器运行 `harness_runtime.py doctor`,后续消费绝对 argv;git status;脏工作区 → baseline 隔离 + `decision`,不询问 |
62
62
  | 0.5 | 先初始化 change-name + `phase.start`,从第一条知识查询起保留事件证据 |
63
63
  | 1 | `harness-knowledge-query` 单次 query(内部 ensure-current;失败记 `issue`) |
64
64
  | 2 | 歧义优先检查 + 复杂度分级;先确认会改变实现方向的语义歧义 |
@@ -66,7 +66,7 @@ description: 将需求转化为设计文档+实施计划+完整测试场景表
66
66
  | 4 | **设计审批包** AskUserQuestion;确认事件早于 approved 设计文档和 `meta/worktree.json` |
67
67
  | 5–6 | plan + implementation-detail + test-scenarios → `plans/` |
68
68
  | 7.5 | 仅 `--adversarial` 对抗评审 |
69
- | 8 | 产物完整性必检 → `checklist.md` |
69
+ | 8 | 在临时产物集上运行 `harness_plan_finalize.py finalize`;原子发布、唯一 `phase.end`、render → `checklist.md` |
70
70
 
71
71
  change-name 范围变更 → 提示重命名或记 🟡WARN(→ `reference.md`)
72
72
 
@@ -82,7 +82,7 @@ change-name 范围变更 → 提示重命名或记 🟡WARN(→ `reference.md`
82
82
  |------|------|
83
83
  | 产物路径 | 只写 `.harness/changes/<cn>/`;禁止 superpowers 输入 |
84
84
  | 设计审批包 | 一次 AskUserQuestion 含 worktree(读 `harness.json` `defaultWorktree`) |
85
- | 阶段 8 | spec/plan/detail/scenarios/worktree.json/logs/events 缺一不可 ❌FAIL |
85
+ | 阶段 8 | spec/plan/detail/scenarios/worktree.json 先进入 staging;仅 finalizer 校验成功后发布并写唯一 `phase.end`/log,失败不得手工补终态 |
86
86
  | Plan 结束 | **禁止**询问执行模式;只提示 `/harness-run` |
87
87
  | 知识查询 | 0.5 失败不得假装已读历史 |
88
88
  | 歧义优先检查 | 否定、对比、动作对象或范围存在多种合理解释时,最小取证后先给推荐理解并一次一问;确认前不深挖错误方向 |
@@ -213,6 +213,12 @@ source: harness-plan
213
213
 
214
214
  > **缺任一文件 → ❌FAIL,不得宣称 plan 完成。**
215
215
 
216
+ - [ ] 所有待发布产物先写入 staging,不直接覆盖正式 change 目录
217
+ - [ ] 执行 `harness_plan_finalize.py finalize --change-dir ... --staging-dir ... --change ... --run-id ... --attempt ... --json`
218
+ - [ ] finalizer 返回 `ok=true` 与 `artifactsHash`;重复执行返回 `idempotent=true`
219
+ - [ ] finalizer 失败时正式目录无半发布产物、无成功 `phase.end`、无伪造 execution log
220
+ - [ ] 禁止在 finalizer 之前手工追加成功 `phase.end`
221
+
216
222
  | 文件 | 必须存在 | 检查结果 |
217
223
  |------|:---:|:---:|
218
224
  | `.harness/changes/<change>/spec/<change>-design.md` | ✅ | □ |
@@ -14,8 +14,11 @@ description: harness-plan 的需求提取模板、任务拆分规则、测试场
14
14
  {
15
15
  "requested": true,
16
16
  "created": false,
17
- "path": ".claude/worktrees/<change-name>",
18
- "branch": "worktree/<change-name>",
17
+ "agent": "<active-adapter>",
18
+ "worktreeRoot": "<adapter-worktree-root>",
19
+ "branchPrefix": "<adapter-branch-prefix>",
20
+ "path": "<adapter-worktree-root>/<change-name>",
21
+ "branch": "<adapter-branch-prefix><change-name>",
19
22
  "decisionBy": "user",
20
23
  "decisionAt": "YYYY-MM-DD HH:mm",
21
24
  "ownerSkill": "harness-run"
@@ -38,6 +41,8 @@ description: harness-plan 的需求提取模板、任务拆分规则、测试场
38
41
 
39
42
  ### 决策事件 note 示例
40
43
 
44
+ `path` 与 `branch` 必须来自 `harness_runtime.py adapter`/`meta/runtime.json`,不得复制其他 agent 的历史路径。Codex 的合同固定为 `.codex/worktrees/<change-name>` 与 `codex/<change-name>`。
45
+
41
46
  `用户选择使用 Worktree;决策文件为 meta/worktree.json;requested=true, created=false;创建责任为 harness-run。`
42
47
 
43
48
  ## 参考 — 详细格式
@@ -3,7 +3,7 @@ name: harness-review
3
3
  description: 6维度代码审查(架构/安全/规范/兼容/测试/性能),对照项目规则(见
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。使用场景:代码审查、提交前检查、合并评审
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-review — 代码审查
8
8
 
9
9
  ## Purpose
@@ -2,7 +2,7 @@
2
2
  name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。使用场景:开始编码、实现功能、写代码、TDD 编码
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
6
6
  # harness-run — 需求编码
7
7
 
8
8
  ## Purpose
@@ -10,13 +10,14 @@ description: harness-run 的执行检查清单。仅在编码执行时读取。
10
10
 
11
11
  ### 步骤 0.0:Worktree 决策执行 ⚠️
12
12
 
13
+ - [ ] 运行 `harness_runtime.py doctor` 并读取 `meta/runtime.json`;后续使用绝对 executable 与 argv 数组
13
14
  - [ ] 读取 `.harness/changes/<change-name>/meta/worktree.json`(旧路径 `worktree.json` 兼容)
14
15
  - [ ] 如果文件缺失:🟡WARN,按 legacy 逻辑询问用户是否主目录执行或创建 worktree
15
16
  - [ ] `requested=false` → 主目录执行,记录原因
16
17
  - [ ] `requested=true` 且 worktree 存在 → 切换到 worktree 执行
17
18
  - [ ] `requested=true` 且 worktree 不存在 → 必须创建 worktree
18
19
  - [ ] worktree 创建命令必须使用 PowerShell:`git worktree add ...`
19
- - [ ] 创建后验证 `.claude/worktrees/<change-name>/.git` 存在
20
+ - [ ] 创建后验证 `<adapter-worktree-root>/<change-name>/.git` 存在;Codex 必须是 `.codex/worktrees/<change-name>`
20
21
  - [ ] 创建成功后更新 `meta/worktree.json` 的 `created=true/createdAt/createdBy`
21
22
  - [ ] 创建失败 → 停止,或 AskUserQuestion 询问是否改为主目录执行
22
23
  - [ ] 禁止 `requested=true && worktree 不存在` 时直接主目录执行
@@ -219,7 +220,7 @@ description: harness-run 的执行检查清单。仅在编码执行时读取。
219
220
 
220
221
  > 仅在 worktree 中执行时触发此步骤。在主目录执行时跳过(标记 ⏭️主目录跳过)。
221
222
 
222
- - [ ] 确认当前 cwd 在 `.claude/worktrees/<change-name>/`
223
+ - [ ] 确认当前 cwd 在 `<adapter-worktree-root>/<change-name>/` 下,且 branch 使用 adapter 的 prefix
223
224
  - [ ] 生成变更摘要:`git diff --stat` + `git diff --stat --cached`
224
225
  - [ ] 构建 commit message:`wip(<scope>): <change-name> 编码完成 — N任务/M文件变更`
225
226
  - [ ] **⚠️ 强制阻断**:用 AskUserQuestion 展示变更列表 + commit message,等待用户确认
@@ -49,20 +49,22 @@ requested=true + path missing
49
49
 
50
50
  ### PowerShell 命令模板
51
51
 
52
+ 先执行 runtime doctor,并从 `meta/runtime.json` 读取 adapter 的 `path`、`branch` 以及 PowerShell `argvPrefix`;不要依赖裸 `python`/`powershell.exe`,也不要硬编码 Claude 路径。
53
+
52
54
  ```powershell
53
- powershell.exe -NoProfile -Command "git worktree add '.claude/worktrees/<change-name>' -b 'worktree/<change-name>'"
55
+ & <powershell-argv-prefix> -Command "git worktree add -- '<adapter-worktree-path>' -b '<adapter-branch>'"
54
56
  ```
55
57
 
56
58
  如果分支已存在:
57
59
 
58
60
  ```powershell
59
- powershell.exe -NoProfile -Command "git worktree add '.claude/worktrees/<change-name>' 'worktree/<change-name>'"
61
+ & <powershell-argv-prefix> -Command "git worktree add -- '<adapter-worktree-path>' '<adapter-branch>'"
60
62
  ```
61
63
 
62
64
  验证:
63
65
 
64
66
  ```powershell
65
- powershell.exe -NoProfile -Command "Test-Path '.claude/worktrees/<change-name>/.git'"
67
+ & <powershell-argv-prefix> -Command "Test-Path -LiteralPath '<adapter-worktree-path>/.git'"
66
68
  ```
67
69
 
68
70
  ### 状态目录写入
@@ -72,7 +74,7 @@ powershell.exe -NoProfile -Command "Test-Path '.claude/worktrees/<change-name>/.
72
74
  ```json
73
75
  {
74
76
  "projectRoot": ".../udp",
75
- "worktreeRoot": ".../udp/.claude/worktrees/<change-name>",
77
+ "worktreeRoot": ".../udp/<adapter-worktree-root>/<change-name>",
76
78
  "stateDir": ".../udp/.harness/changes/<change-name>"
77
79
  }
78
80
  ```
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff
4
4
  合并回主分支。使用场景:提交代码、commit、push、合并分支、merge to main、完成开发
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose
@@ -2,7 +2,7 @@
2
2
  name: harness-sync
3
3
  description: 检查并更新项目AI元数据(CodeGraph索引、harness-codebase-map分析、CLAUDE.md、AGENTS.md、.harness/完整性),确保Claude对代码库的理解与最新代码一致。当用户说'同步/更新索引/刷新元数据/检查一致性'时使用
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=43088896a4556b69; overlay=none; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=8dd8ce9aa1ac47f5; overlay=none; agent=codebuddy; do not edit -->
6
6
  # harness-sync — 元数据同步
7
7
 
8
8
  ## Purpose