@hunter-harness/workflow-harness 0.2.11 → 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 (242) 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-query/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +4 -4
  9. package/harness/bundles/general/claude-code/harness-plan/checklist.md +6 -0
  10. package/harness/bundles/general/claude-code/harness-plan/reference.md +7 -2
  11. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-run/checklist.md +3 -2
  14. package/harness/bundles/general/claude-code/harness-run/reference.md +6 -4
  15. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  17. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  18. package/harness/bundles/general/claude-code/scripts/harness_archive.py +282 -120
  19. package/harness/bundles/general/claude-code/scripts/harness_events.py +400 -24
  20. package/harness/bundles/general/claude-code/scripts/harness_gate.py +585 -21
  21. package/harness/bundles/general/claude-code/scripts/harness_integration.py +403 -18
  22. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +225 -29
  23. package/harness/bundles/general/claude-code/scripts/harness_phase.py +926 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +361 -0
  25. package/harness/bundles/general/claude-code/scripts/harness_profile.py +50 -0
  26. package/harness/bundles/general/claude-code/scripts/harness_runtime.py +248 -0
  27. package/harness/bundles/general/claude-code/scripts/harness_state.py +15 -2
  28. package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +188 -0
  29. package/harness/bundles/general/claude-code/scripts/harness_workflow_policy.py +19 -0
  30. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  31. package/harness/bundles/general/codebuddy/contracts/ci-metrics.schema.json +36 -0
  32. package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +30 -0
  33. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  34. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  35. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  36. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +4 -4
  38. package/harness/bundles/general/codebuddy/harness-plan/checklist.md +6 -0
  39. package/harness/bundles/general/codebuddy/harness-plan/reference.md +7 -2
  40. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/harness-run/checklist.md +3 -2
  43. package/harness/bundles/general/codebuddy/harness-run/reference.md +6 -4
  44. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  45. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  46. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  47. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +282 -120
  48. package/harness/bundles/general/codebuddy/scripts/harness_events.py +400 -24
  49. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +585 -21
  50. package/harness/bundles/general/codebuddy/scripts/harness_integration.py +403 -18
  51. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +225 -29
  52. package/harness/bundles/general/codebuddy/scripts/harness_phase.py +926 -0
  53. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +361 -0
  54. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +50 -0
  55. package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +248 -0
  56. package/harness/bundles/general/codebuddy/scripts/harness_state.py +15 -2
  57. package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +188 -0
  58. package/harness/bundles/general/codebuddy/scripts/harness_workflow_policy.py +19 -0
  59. package/harness/bundles/general/codex/.harness-build.json +1 -1
  60. package/harness/bundles/general/codex/contracts/ci-metrics.schema.json +36 -0
  61. package/harness/bundles/general/codex/contracts/workflow-policy.json +30 -0
  62. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  63. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  64. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-plan/SKILL.md +4 -4
  67. package/harness/bundles/general/codex/harness-plan/checklist.md +6 -0
  68. package/harness/bundles/general/codex/harness-plan/reference.md +7 -2
  69. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  70. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  71. package/harness/bundles/general/codex/harness-run/checklist.md +3 -2
  72. package/harness/bundles/general/codex/harness-run/reference.md +6 -4
  73. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  74. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  75. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  76. package/harness/bundles/general/codex/scripts/harness_archive.py +282 -120
  77. package/harness/bundles/general/codex/scripts/harness_events.py +400 -24
  78. package/harness/bundles/general/codex/scripts/harness_gate.py +585 -21
  79. package/harness/bundles/general/codex/scripts/harness_integration.py +403 -18
  80. package/harness/bundles/general/codex/scripts/harness_ledger.py +225 -29
  81. package/harness/bundles/general/codex/scripts/harness_phase.py +926 -0
  82. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +361 -0
  83. package/harness/bundles/general/codex/scripts/harness_profile.py +50 -0
  84. package/harness/bundles/general/codex/scripts/harness_runtime.py +248 -0
  85. package/harness/bundles/general/codex/scripts/harness_state.py +15 -2
  86. package/harness/bundles/general/codex/scripts/harness_test_guard.py +188 -0
  87. package/harness/bundles/general/codex/scripts/harness_workflow_policy.py +19 -0
  88. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  89. package/harness/bundles/general/cursor/contracts/ci-metrics.schema.json +36 -0
  90. package/harness/bundles/general/cursor/contracts/workflow-policy.json +30 -0
  91. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  92. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  93. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  94. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  95. package/harness/bundles/general/cursor/harness-plan/SKILL.md +4 -4
  96. package/harness/bundles/general/cursor/harness-plan/checklist.md +6 -0
  97. package/harness/bundles/general/cursor/harness-plan/reference.md +7 -2
  98. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  99. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  100. package/harness/bundles/general/cursor/harness-run/checklist.md +3 -2
  101. package/harness/bundles/general/cursor/harness-run/reference.md +6 -4
  102. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  103. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  104. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  105. package/harness/bundles/general/cursor/scripts/harness_archive.py +282 -120
  106. package/harness/bundles/general/cursor/scripts/harness_events.py +400 -24
  107. package/harness/bundles/general/cursor/scripts/harness_gate.py +585 -21
  108. package/harness/bundles/general/cursor/scripts/harness_integration.py +403 -18
  109. package/harness/bundles/general/cursor/scripts/harness_ledger.py +225 -29
  110. package/harness/bundles/general/cursor/scripts/harness_phase.py +926 -0
  111. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +361 -0
  112. package/harness/bundles/general/cursor/scripts/harness_profile.py +50 -0
  113. package/harness/bundles/general/cursor/scripts/harness_runtime.py +248 -0
  114. package/harness/bundles/general/cursor/scripts/harness_state.py +15 -2
  115. package/harness/bundles/general/cursor/scripts/harness_test_guard.py +188 -0
  116. package/harness/bundles/general/cursor/scripts/harness_workflow_policy.py +19 -0
  117. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  118. package/harness/bundles/java/claude-code/contracts/ci-metrics.schema.json +36 -0
  119. package/harness/bundles/java/claude-code/contracts/workflow-policy.json +30 -0
  120. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  121. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  122. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  123. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  124. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  125. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  126. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +4 -4
  127. package/harness/bundles/java/claude-code/harness-plan/checklist.md +6 -0
  128. package/harness/bundles/java/claude-code/harness-plan/reference.md +7 -2
  129. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  130. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  131. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  132. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  133. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  134. package/harness/bundles/java/claude-code/scripts/harness_archive.py +282 -120
  135. package/harness/bundles/java/claude-code/scripts/harness_events.py +400 -24
  136. package/harness/bundles/java/claude-code/scripts/harness_gate.py +585 -21
  137. package/harness/bundles/java/claude-code/scripts/harness_integration.py +403 -18
  138. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +225 -29
  139. package/harness/bundles/java/claude-code/scripts/harness_phase.py +926 -0
  140. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +361 -0
  141. package/harness/bundles/java/claude-code/scripts/harness_profile.py +50 -0
  142. package/harness/bundles/java/claude-code/scripts/harness_runtime.py +248 -0
  143. package/harness/bundles/java/claude-code/scripts/harness_state.py +15 -2
  144. package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +188 -0
  145. package/harness/bundles/java/claude-code/scripts/harness_workflow_policy.py +19 -0
  146. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  147. package/harness/bundles/java/codebuddy/contracts/ci-metrics.schema.json +36 -0
  148. package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +30 -0
  149. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  150. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  151. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  152. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  153. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  154. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  155. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +4 -4
  156. package/harness/bundles/java/codebuddy/harness-plan/checklist.md +6 -0
  157. package/harness/bundles/java/codebuddy/harness-plan/reference.md +7 -2
  158. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  159. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  160. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  161. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  162. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  163. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +282 -120
  164. package/harness/bundles/java/codebuddy/scripts/harness_events.py +400 -24
  165. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +585 -21
  166. package/harness/bundles/java/codebuddy/scripts/harness_integration.py +403 -18
  167. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +225 -29
  168. package/harness/bundles/java/codebuddy/scripts/harness_phase.py +926 -0
  169. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +361 -0
  170. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +50 -0
  171. package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +248 -0
  172. package/harness/bundles/java/codebuddy/scripts/harness_state.py +15 -2
  173. package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +188 -0
  174. package/harness/bundles/java/codebuddy/scripts/harness_workflow_policy.py +19 -0
  175. package/harness/bundles/java/codex/.harness-build.json +1 -1
  176. package/harness/bundles/java/codex/contracts/ci-metrics.schema.json +36 -0
  177. package/harness/bundles/java/codex/contracts/workflow-policy.json +30 -0
  178. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  179. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  180. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  181. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  182. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  183. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  184. package/harness/bundles/java/codex/harness-plan/SKILL.md +4 -4
  185. package/harness/bundles/java/codex/harness-plan/checklist.md +6 -0
  186. package/harness/bundles/java/codex/harness-plan/reference.md +7 -2
  187. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  188. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  189. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  190. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  191. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  192. package/harness/bundles/java/codex/scripts/harness_archive.py +282 -120
  193. package/harness/bundles/java/codex/scripts/harness_events.py +400 -24
  194. package/harness/bundles/java/codex/scripts/harness_gate.py +585 -21
  195. package/harness/bundles/java/codex/scripts/harness_integration.py +403 -18
  196. package/harness/bundles/java/codex/scripts/harness_ledger.py +225 -29
  197. package/harness/bundles/java/codex/scripts/harness_phase.py +926 -0
  198. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +361 -0
  199. package/harness/bundles/java/codex/scripts/harness_profile.py +50 -0
  200. package/harness/bundles/java/codex/scripts/harness_runtime.py +248 -0
  201. package/harness/bundles/java/codex/scripts/harness_state.py +15 -2
  202. package/harness/bundles/java/codex/scripts/harness_test_guard.py +188 -0
  203. package/harness/bundles/java/codex/scripts/harness_workflow_policy.py +19 -0
  204. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  205. package/harness/bundles/java/cursor/contracts/ci-metrics.schema.json +36 -0
  206. package/harness/bundles/java/cursor/contracts/workflow-policy.json +30 -0
  207. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  208. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  209. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  210. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  211. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  212. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  213. package/harness/bundles/java/cursor/harness-plan/SKILL.md +4 -4
  214. package/harness/bundles/java/cursor/harness-plan/checklist.md +6 -0
  215. package/harness/bundles/java/cursor/harness-plan/reference.md +7 -2
  216. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  217. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  218. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  219. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  220. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  221. package/harness/bundles/java/cursor/scripts/harness_archive.py +282 -120
  222. package/harness/bundles/java/cursor/scripts/harness_events.py +400 -24
  223. package/harness/bundles/java/cursor/scripts/harness_gate.py +585 -21
  224. package/harness/bundles/java/cursor/scripts/harness_integration.py +403 -18
  225. package/harness/bundles/java/cursor/scripts/harness_ledger.py +225 -29
  226. package/harness/bundles/java/cursor/scripts/harness_phase.py +926 -0
  227. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +361 -0
  228. package/harness/bundles/java/cursor/scripts/harness_profile.py +50 -0
  229. package/harness/bundles/java/cursor/scripts/harness_runtime.py +248 -0
  230. package/harness/bundles/java/cursor/scripts/harness_state.py +15 -2
  231. package/harness/bundles/java/cursor/scripts/harness_test_guard.py +188 -0
  232. package/harness/bundles/java/cursor/scripts/harness_workflow_policy.py +19 -0
  233. package/harness/manifests/general/claude-code.json +42 -26
  234. package/harness/manifests/general/codebuddy.json +42 -26
  235. package/harness/manifests/general/codex.json +42 -26
  236. package/harness/manifests/general/cursor.json +42 -26
  237. package/harness/manifests/java/claude-code.json +42 -26
  238. package/harness/manifests/java/codebuddy.json +42 -26
  239. package/harness/manifests/java/codex.json +42 -26
  240. package/harness/manifests/java/cursor.json +42 -26
  241. package/hunter-workflow-family.json +2 -2
  242. 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,12 +2,12 @@
2
2
  "schema_version": 2,
3
3
  "profile": "general",
4
4
  "adapter": "claude-code",
5
- "bundle_version": "0.2.11",
5
+ "bundle_version": "0.2.12",
6
6
  "generator": "harness_deploy.py",
7
7
  "files": [
8
8
  {
9
9
  "path": ".harness-build.json",
10
- "sha256": "40d31470c7bcb1767951cf358f5e16e2e021757382cb07a4148d42b699671867"
10
+ "sha256": "b1e7fdc6c66ac7aed5ff8a8e381ff36d28d49955394e145cbfb2f0f8132b26ed"
11
11
  },
12
12
  {
13
13
  "path": "agents/harness-evaluator.md",
@@ -25,9 +25,13 @@
25
25
  "path": "CONTEXT.md",
26
26
  "sha256": "19c75b493f6f760d0e607a88bd355acdd232fcb66660887ef94d10425f39507b"
27
27
  },
28
+ {
29
+ "path": "contracts/ci-metrics.schema.json",
30
+ "sha256": "63e05a5ee620069dac7f63ab5780a07900114534c201e3f449b1bd8de9365757"
31
+ },
28
32
  {
29
33
  "path": "contracts/workflow-policy.json",
30
- "sha256": "e642d840fa90aa5943cb80bee6538e0aa11577fcaab0c4e534641ebfae8d8f00"
34
+ "sha256": "3f39160c6905338a527170c0f15d83715ad56ac9fb449f4f568a6714ccdd3c9a"
31
35
  },
32
36
  {
33
37
  "path": "harness-archive/checklist.md",
@@ -43,7 +47,7 @@
43
47
  },
44
48
  {
45
49
  "path": "harness-archive/SKILL.md",
46
- "sha256": "13d1957acf85bf3c933474a28410be0601d64f5235821c37325021bbe208b1a5"
50
+ "sha256": "85db6ae3e7c040e489a0fc8fd3c1d16785a57b4aea0e146f07a783808d90ee34"
47
51
  },
48
52
  {
49
53
  "path": "harness-archive/templates/render-summary.mjs",
@@ -63,7 +67,7 @@
63
67
  },
64
68
  {
65
69
  "path": "harness-codebase-map/SKILL.md",
66
- "sha256": "09f36dc649a1f9d3011fddd04e22e58e537210e7ae52415a2759bd5665633b7f"
70
+ "sha256": "7936882e87ddb799fd9023b3289285cf777219a13e9b6675c57f61c48611a554"
67
71
  },
68
72
  {
69
73
  "path": "harness-codebase-map/templates/ARCHITECTURE.md",
@@ -127,15 +131,15 @@
127
131
  },
128
132
  {
129
133
  "path": "harness-knowledge-ingest/SKILL.md",
130
- "sha256": "d25d7f2661de42398dbd8b0fdc02664bfe1a00f9db0c5f9e53026f98c080de9b"
134
+ "sha256": "7ad661113a23a9d18bc68e54dd9e425bbb6a4be240e84aaeba299cfe70c43114"
131
135
  },
132
136
  {
133
137
  "path": "harness-knowledge-query/SKILL.md",
134
- "sha256": "f3350f4904db95e9c01125b169fcbd344b902a151599e0a60d30f228df63649a"
138
+ "sha256": "d1501e58969944afb8184659d15608e7679a59fcc793d0cab21475b72e08fbce"
135
139
  },
136
140
  {
137
141
  "path": "harness-plan/checklist.md",
138
- "sha256": "efd49a7430aae916d2cd6da82f812c0fdc9777eaabe8212a92904d5dcfa38534"
142
+ "sha256": "897649058dea86440bcbf4fcb7f2323a6c361318da48737ca69b282f7a9b1870"
139
143
  },
140
144
  {
141
145
  "path": "harness-plan/protocols.md",
@@ -143,11 +147,11 @@
143
147
  },
144
148
  {
145
149
  "path": "harness-plan/reference.md",
146
- "sha256": "93b1a26397cb411ca64700f3022ca92d199f85f5fc221b745bcc10a9c2ba9970"
150
+ "sha256": "5f3ee154ad79f4b5906efffbd09f14d26ca629540002fb06e35208c21bd5efa4"
147
151
  },
148
152
  {
149
153
  "path": "harness-plan/SKILL.md",
150
- "sha256": "a521f4093cf364006f33b6e59196308c055ff9ee24f28cab04407e2315560426"
154
+ "sha256": "b9e45a9128cb936baef0af42555e87758f6c66926a071c84ea4fdeb65d85e52d"
151
155
  },
152
156
  {
153
157
  "path": "harness-review/checklist.md",
@@ -163,11 +167,11 @@
163
167
  },
164
168
  {
165
169
  "path": "harness-review/SKILL.md",
166
- "sha256": "b0968731379ef9cd344f6daf4c80edc3d446dc52c518cbe46b09cd302e0325df"
170
+ "sha256": "f7be0a500962ba4393cd438bf9d59250eede7b10beb433cdfcf30b496d8b009f"
167
171
  },
168
172
  {
169
173
  "path": "harness-run/checklist.md",
170
- "sha256": "4a829a9a1521402d508f2b76ba16246f8d5b860cc5eeaa40faa96cbc874992c2"
174
+ "sha256": "091bde8a02224f8da1078c7022bb431c2ea696779efef5a8b9129b2e51522505"
171
175
  },
172
176
  {
173
177
  "path": "harness-run/protocols.md",
@@ -175,11 +179,11 @@
175
179
  },
176
180
  {
177
181
  "path": "harness-run/reference.md",
178
- "sha256": "9c0b34303310de06ab56ab0c38b35134dcd4b0d3bde10c9275313ff2962010f3"
182
+ "sha256": "2be487368210d50b22062cefcf6e9b4ef4ff2eb148531e0c85b180f41f2e8b33"
179
183
  },
180
184
  {
181
185
  "path": "harness-run/SKILL.md",
182
- "sha256": "d05d156aff4a852cd84de3e0c228a7a11ff132d1fcedc939e54688908204ca42"
186
+ "sha256": "0ffe532fe94448923b178cfcd04f5f448b6a259ae800bcf6523382b51a1a9ffa"
183
187
  },
184
188
  {
185
189
  "path": "harness-submit/checklist.md",
@@ -191,7 +195,7 @@
191
195
  },
192
196
  {
193
197
  "path": "harness-submit/SKILL.md",
194
- "sha256": "4fe6f295e5df8e52ef6808777e203f5999b575e80acc8c7744c31b111e537821"
198
+ "sha256": "87ea4e3d91eba4b6a75609ea521f36e96fd29502479a0f9f5a1d9ecd3eaa2a85"
195
199
  },
196
200
  {
197
201
  "path": "harness-sync/reference.md",
@@ -199,7 +203,7 @@
199
203
  },
200
204
  {
201
205
  "path": "harness-sync/SKILL.md",
202
- "sha256": "c72e07daa1eb3c59a0a8da2b1e60b2b5097537f38c1c6f314b9f8b18b0ec42f1"
206
+ "sha256": "7c708fd321c79242c85799b223517ba812c0d43972933d74e8012940d84c1535"
203
207
  },
204
208
  {
205
209
  "path": "harness-test/checklist.md",
@@ -223,7 +227,7 @@
223
227
  },
224
228
  {
225
229
  "path": "harness-test/SKILL.md",
226
- "sha256": "1e7be5f528b51b8124d3e3ef27e03e2d7965ab266370be91b34d3113775443ec"
230
+ "sha256": "8463f54ca8a41b89a09e92cae35c8f1c125371c26564e6545354286d415016c8"
227
231
  },
228
232
  {
229
233
  "path": "protocols/archive-report-protocol.md",
@@ -267,7 +271,7 @@
267
271
  },
268
272
  {
269
273
  "path": "scripts/harness_archive.py",
270
- "sha256": "52f3a7aef19cc84d4820f90d0fa320e7e963e7b7710f607954cd67608c67be55"
274
+ "sha256": "b0c29fd8505de3ece4efa7415424c68564116a17f0ba9e3d2718f3ee0cdec0f2"
271
275
  },
272
276
  {
273
277
  "path": "scripts/harness_change.py",
@@ -283,43 +287,55 @@
283
287
  },
284
288
  {
285
289
  "path": "scripts/harness_events.py",
286
- "sha256": "700ce2741ec80144c8880cdb85c8026dc9c7aec9bcbdee1c1d3ccb77aed58648"
290
+ "sha256": "287436f60ce4aded0869233f9148112d612b74e61d79d1d4be358f802751c736"
287
291
  },
288
292
  {
289
293
  "path": "scripts/harness_gate.py",
290
- "sha256": "5fdb48040a396403dc9584561316d6181b9b70c5234407d662093bad739cc3bc"
294
+ "sha256": "76c7a4e82d89ef121b47bcb3b3e7f180b9f421a9406bfb9d1de9cfbb9d27bfba"
291
295
  },
292
296
  {
293
297
  "path": "scripts/harness_integration.py",
294
- "sha256": "a5e616ed1ba04b390d431132f6eeedef8789b87bee1e1512667e696f6a970471"
298
+ "sha256": "8c4d143affe1fc37ea028cc9d85581774ddecc4849f298094f1d4d831f3929f7"
295
299
  },
296
300
  {
297
301
  "path": "scripts/harness_ledger.py",
298
- "sha256": "79815b4a1fb6f5c43d63dcb64a4a9cde26ccd7f6206c86ba3d0c795854668ee5"
302
+ "sha256": "28e87fbdbac579f87e7c374bce59c60e689c61d2ac1298b4fdd868562e2abbac"
299
303
  },
300
304
  {
301
305
  "path": "scripts/harness_paths.py",
302
306
  "sha256": "75560c0141172b335811014320001a299846e4fba45dd0589cba86da058ab01a"
303
307
  },
308
+ {
309
+ "path": "scripts/harness_phase.py",
310
+ "sha256": "5f16f9b03dc01a08f3dbc0bb0a416856833836f4c3fcb2c4c0799a876079c43d"
311
+ },
312
+ {
313
+ "path": "scripts/harness_plan_finalize.py",
314
+ "sha256": "8557e392b34711811a7fa220a2c388c60fec79e916fbd61b42f3e6e6dc50b22c"
315
+ },
304
316
  {
305
317
  "path": "scripts/harness_preflight.py",
306
318
  "sha256": "245f70d6c1f9979f2921e14e5d7a6078bd5126ae6c5d8c0bcd6cfd9c4fd8e135"
307
319
  },
308
320
  {
309
321
  "path": "scripts/harness_profile.py",
310
- "sha256": "52c9fb0d7c22823f06a8fd49bc1b648b0bda974f0851e8a12b0959be7c1c4183"
322
+ "sha256": "7c02010be79352e552d028ac7a5884f273b832af304cb2336824d41a4371a800"
311
323
  },
312
324
  {
313
325
  "path": "scripts/harness_review.py",
314
326
  "sha256": "78009e09b79867a13c66a8c71114b6cd04fe75c72054f5ec35d63c7be990bb04"
315
327
  },
328
+ {
329
+ "path": "scripts/harness_runtime.py",
330
+ "sha256": "8959e8702bf703380dc2de92bf33aef3f9520d66ffd790c2295ce6cae1c28cab"
331
+ },
316
332
  {
317
333
  "path": "scripts/harness_service.py",
318
334
  "sha256": "17c26b40ed3e7d110b74ef61e627fa0ad1bc84718a1721a066e6d72ee0e37649"
319
335
  },
320
336
  {
321
337
  "path": "scripts/harness_state.py",
322
- "sha256": "27073a1d7b1ea6d54a75bf6e679eb250a3507c95311101b60a1c4d32e00f909e"
338
+ "sha256": "5a35c8ceb1d1cbb1e52347f150139029dd78e57ed47b6d9f2a69066ce33b6084"
323
339
  },
324
340
  {
325
341
  "path": "scripts/harness_sync.py",
@@ -327,11 +343,11 @@
327
343
  },
328
344
  {
329
345
  "path": "scripts/harness_test_guard.py",
330
- "sha256": "76713c4af83486b513979f40ba8055fefd0ff10b487d31de9d7f9d9a4ee2dce0"
346
+ "sha256": "1817c2b0ceeb14a98149e704decd4e6cbcaf16121f95320ef61cd36805abe03d"
331
347
  },
332
348
  {
333
349
  "path": "scripts/harness_workflow_policy.py",
334
- "sha256": "3e754da7041ebec852c3f0be8736cec423d7a0ee98424164bfe4a06fc4d8ca4f"
350
+ "sha256": "a01530d0992cd589129b847c15f1d038cb54992387f694748c09fa182a2de240"
335
351
  }
336
352
  ]
337
353
  }