@hunter-harness/workflow-harness 0.2.86 → 0.3.1

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 (258) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/contracts/workflow-policy.json +46 -17
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +2 -2
  4. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +2 -2
  5. package/harness/bundles/general/claude-code/harness-execute/SKILL.md +189 -0
  6. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +2 -2
  7. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +2 -2
  8. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +10 -10
  9. package/harness/bundles/general/claude-code/harness-plan/checklist.md +2 -13
  10. package/harness/bundles/general/claude-code/harness-plan/reference.md +43 -81
  11. package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-review/SKILL.md +3 -3
  14. package/harness/bundles/general/claude-code/harness-review/protocols.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-run/SKILL.md +8 -2
  16. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
  17. package/harness/bundles/general/claude-code/harness-submit/checklist.md +1 -1
  18. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  19. package/harness/bundles/general/claude-code/harness-test/SKILL.md +11 -2
  20. package/harness/bundles/general/claude-code/scripts/harness_adoption_metrics.py +391 -0
  21. package/harness/bundles/general/claude-code/scripts/harness_archive.py +46 -15
  22. package/harness/bundles/general/claude-code/scripts/harness_context.py +94 -43
  23. package/harness/bundles/general/claude-code/scripts/harness_events.py +1 -1
  24. package/harness/bundles/general/claude-code/scripts/harness_events_sync.py +1 -0
  25. package/harness/bundles/general/claude-code/scripts/harness_fixback.py +10 -10
  26. package/harness/bundles/general/claude-code/scripts/harness_gate.py +92 -25
  27. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +4 -2
  28. package/harness/bundles/general/claude-code/scripts/harness_paths.py +120 -5
  29. package/harness/bundles/general/claude-code/scripts/harness_phase.py +41 -17
  30. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +16 -873
  31. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  32. package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +46 -17
  33. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +2 -2
  34. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +2 -2
  35. package/harness/bundles/general/codebuddy/harness-execute/SKILL.md +170 -0
  36. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +2 -2
  37. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +2 -2
  38. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +10 -10
  39. package/harness/bundles/general/codebuddy/harness-plan/checklist.md +2 -13
  40. package/harness/bundles/general/codebuddy/harness-plan/reference.md +43 -81
  41. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +3 -3
  44. package/harness/bundles/general/codebuddy/harness-review/protocols.md +1 -1
  45. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +8 -2
  46. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
  47. package/harness/bundles/general/codebuddy/harness-submit/checklist.md +1 -1
  48. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  49. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +11 -2
  50. package/harness/bundles/general/codebuddy/scripts/harness_adoption_metrics.py +391 -0
  51. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +46 -15
  52. package/harness/bundles/general/codebuddy/scripts/harness_context.py +94 -43
  53. package/harness/bundles/general/codebuddy/scripts/harness_events.py +1 -1
  54. package/harness/bundles/general/codebuddy/scripts/harness_events_sync.py +1 -0
  55. package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +10 -10
  56. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +92 -25
  57. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +4 -2
  58. package/harness/bundles/general/codebuddy/scripts/harness_paths.py +120 -5
  59. package/harness/bundles/general/codebuddy/scripts/harness_phase.py +41 -17
  60. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +16 -873
  61. package/harness/bundles/general/codex/.harness-build.json +1 -1
  62. package/harness/bundles/general/codex/contracts/workflow-policy.json +46 -17
  63. package/harness/bundles/general/codex/harness-archive/SKILL.md +2 -2
  64. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +2 -2
  65. package/harness/bundles/general/codex/harness-execute/SKILL.md +170 -0
  66. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +2 -2
  67. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +2 -2
  68. package/harness/bundles/general/codex/harness-plan/SKILL.md +10 -10
  69. package/harness/bundles/general/codex/harness-plan/checklist.md +2 -13
  70. package/harness/bundles/general/codex/harness-plan/reference.md +43 -81
  71. package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
  72. package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
  73. package/harness/bundles/general/codex/harness-review/SKILL.md +3 -3
  74. package/harness/bundles/general/codex/harness-review/protocols.md +1 -1
  75. package/harness/bundles/general/codex/harness-run/SKILL.md +8 -2
  76. package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
  77. package/harness/bundles/general/codex/harness-submit/checklist.md +1 -1
  78. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  79. package/harness/bundles/general/codex/harness-test/SKILL.md +11 -2
  80. package/harness/bundles/general/codex/scripts/harness_adoption_metrics.py +391 -0
  81. package/harness/bundles/general/codex/scripts/harness_archive.py +46 -15
  82. package/harness/bundles/general/codex/scripts/harness_context.py +94 -43
  83. package/harness/bundles/general/codex/scripts/harness_events.py +1 -1
  84. package/harness/bundles/general/codex/scripts/harness_events_sync.py +1 -0
  85. package/harness/bundles/general/codex/scripts/harness_fixback.py +10 -10
  86. package/harness/bundles/general/codex/scripts/harness_gate.py +92 -25
  87. package/harness/bundles/general/codex/scripts/harness_ledger.py +4 -2
  88. package/harness/bundles/general/codex/scripts/harness_paths.py +120 -5
  89. package/harness/bundles/general/codex/scripts/harness_phase.py +41 -17
  90. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +16 -873
  91. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  92. package/harness/bundles/general/cursor/contracts/workflow-policy.json +46 -17
  93. package/harness/bundles/general/cursor/harness-archive/SKILL.md +2 -2
  94. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +2 -2
  95. package/harness/bundles/general/cursor/harness-execute/SKILL.md +170 -0
  96. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +2 -2
  97. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +2 -2
  98. package/harness/bundles/general/cursor/harness-plan/SKILL.md +10 -10
  99. package/harness/bundles/general/cursor/harness-plan/checklist.md +2 -13
  100. package/harness/bundles/general/cursor/harness-plan/reference.md +43 -81
  101. package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
  102. package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
  103. package/harness/bundles/general/cursor/harness-review/SKILL.md +3 -3
  104. package/harness/bundles/general/cursor/harness-review/protocols.md +1 -1
  105. package/harness/bundles/general/cursor/harness-run/SKILL.md +8 -2
  106. package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
  107. package/harness/bundles/general/cursor/harness-submit/checklist.md +1 -1
  108. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  109. package/harness/bundles/general/cursor/harness-test/SKILL.md +11 -2
  110. package/harness/bundles/general/cursor/scripts/harness_adoption_metrics.py +391 -0
  111. package/harness/bundles/general/cursor/scripts/harness_archive.py +46 -15
  112. package/harness/bundles/general/cursor/scripts/harness_context.py +94 -43
  113. package/harness/bundles/general/cursor/scripts/harness_events.py +1 -1
  114. package/harness/bundles/general/cursor/scripts/harness_events_sync.py +1 -0
  115. package/harness/bundles/general/cursor/scripts/harness_fixback.py +10 -10
  116. package/harness/bundles/general/cursor/scripts/harness_gate.py +92 -25
  117. package/harness/bundles/general/cursor/scripts/harness_ledger.py +4 -2
  118. package/harness/bundles/general/cursor/scripts/harness_paths.py +120 -5
  119. package/harness/bundles/general/cursor/scripts/harness_phase.py +41 -17
  120. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +16 -873
  121. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  122. package/harness/bundles/java/claude-code/contracts/workflow-policy.json +46 -17
  123. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +2 -2
  124. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +2 -2
  125. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +2 -2
  126. package/harness/bundles/java/claude-code/harness-execute/SKILL.md +189 -0
  127. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +2 -2
  128. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +2 -2
  129. package/harness/bundles/java/claude-code/harness-package/SKILL.md +2 -2
  130. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +10 -10
  131. package/harness/bundles/java/claude-code/harness-plan/checklist.md +2 -13
  132. package/harness/bundles/java/claude-code/harness-plan/reference.md +43 -81
  133. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
  134. package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
  135. package/harness/bundles/java/claude-code/harness-review/SKILL.md +3 -3
  136. package/harness/bundles/java/claude-code/harness-review/protocols.md +1 -1
  137. package/harness/bundles/java/claude-code/harness-run/SKILL.md +8 -2
  138. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
  139. package/harness/bundles/java/claude-code/harness-submit/checklist.md +1 -1
  140. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  141. package/harness/bundles/java/claude-code/harness-test/SKILL.md +11 -2
  142. package/harness/bundles/java/claude-code/scripts/harness_adoption_metrics.py +391 -0
  143. package/harness/bundles/java/claude-code/scripts/harness_archive.py +46 -15
  144. package/harness/bundles/java/claude-code/scripts/harness_context.py +94 -43
  145. package/harness/bundles/java/claude-code/scripts/harness_events.py +1 -1
  146. package/harness/bundles/java/claude-code/scripts/harness_events_sync.py +1 -0
  147. package/harness/bundles/java/claude-code/scripts/harness_fixback.py +10 -10
  148. package/harness/bundles/java/claude-code/scripts/harness_gate.py +92 -25
  149. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +4 -2
  150. package/harness/bundles/java/claude-code/scripts/harness_paths.py +120 -5
  151. package/harness/bundles/java/claude-code/scripts/harness_phase.py +41 -17
  152. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +16 -873
  153. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  154. package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +46 -17
  155. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +2 -2
  156. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +2 -2
  157. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +2 -2
  158. package/harness/bundles/java/codebuddy/harness-execute/SKILL.md +170 -0
  159. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +2 -2
  160. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +2 -2
  161. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -2
  162. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +10 -10
  163. package/harness/bundles/java/codebuddy/harness-plan/checklist.md +2 -13
  164. package/harness/bundles/java/codebuddy/harness-plan/reference.md +43 -81
  165. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
  166. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
  167. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +3 -3
  168. package/harness/bundles/java/codebuddy/harness-review/protocols.md +1 -1
  169. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +8 -2
  170. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
  171. package/harness/bundles/java/codebuddy/harness-submit/checklist.md +1 -1
  172. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  173. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +11 -2
  174. package/harness/bundles/java/codebuddy/scripts/harness_adoption_metrics.py +391 -0
  175. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +46 -15
  176. package/harness/bundles/java/codebuddy/scripts/harness_context.py +94 -43
  177. package/harness/bundles/java/codebuddy/scripts/harness_events.py +1 -1
  178. package/harness/bundles/java/codebuddy/scripts/harness_events_sync.py +1 -0
  179. package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +10 -10
  180. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +92 -25
  181. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +4 -2
  182. package/harness/bundles/java/codebuddy/scripts/harness_paths.py +120 -5
  183. package/harness/bundles/java/codebuddy/scripts/harness_phase.py +41 -17
  184. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +16 -873
  185. package/harness/bundles/java/codex/.harness-build.json +1 -1
  186. package/harness/bundles/java/codex/contracts/workflow-policy.json +46 -17
  187. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +2 -2
  188. package/harness/bundles/java/codex/harness-archive/SKILL.md +2 -2
  189. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +2 -2
  190. package/harness/bundles/java/codex/harness-execute/SKILL.md +170 -0
  191. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +2 -2
  192. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +2 -2
  193. package/harness/bundles/java/codex/harness-package/SKILL.md +2 -2
  194. package/harness/bundles/java/codex/harness-plan/SKILL.md +10 -10
  195. package/harness/bundles/java/codex/harness-plan/checklist.md +2 -13
  196. package/harness/bundles/java/codex/harness-plan/reference.md +43 -81
  197. package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
  198. package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
  199. package/harness/bundles/java/codex/harness-review/SKILL.md +3 -3
  200. package/harness/bundles/java/codex/harness-review/protocols.md +1 -1
  201. package/harness/bundles/java/codex/harness-run/SKILL.md +8 -2
  202. package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
  203. package/harness/bundles/java/codex/harness-submit/checklist.md +1 -1
  204. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  205. package/harness/bundles/java/codex/harness-test/SKILL.md +11 -2
  206. package/harness/bundles/java/codex/scripts/harness_adoption_metrics.py +391 -0
  207. package/harness/bundles/java/codex/scripts/harness_archive.py +46 -15
  208. package/harness/bundles/java/codex/scripts/harness_context.py +94 -43
  209. package/harness/bundles/java/codex/scripts/harness_events.py +1 -1
  210. package/harness/bundles/java/codex/scripts/harness_events_sync.py +1 -0
  211. package/harness/bundles/java/codex/scripts/harness_fixback.py +10 -10
  212. package/harness/bundles/java/codex/scripts/harness_gate.py +92 -25
  213. package/harness/bundles/java/codex/scripts/harness_ledger.py +4 -2
  214. package/harness/bundles/java/codex/scripts/harness_paths.py +120 -5
  215. package/harness/bundles/java/codex/scripts/harness_phase.py +41 -17
  216. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +16 -873
  217. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  218. package/harness/bundles/java/cursor/contracts/workflow-policy.json +46 -17
  219. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +2 -2
  220. package/harness/bundles/java/cursor/harness-archive/SKILL.md +2 -2
  221. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +2 -2
  222. package/harness/bundles/java/cursor/harness-execute/SKILL.md +170 -0
  223. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +2 -2
  224. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +2 -2
  225. package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -2
  226. package/harness/bundles/java/cursor/harness-plan/SKILL.md +10 -10
  227. package/harness/bundles/java/cursor/harness-plan/checklist.md +2 -13
  228. package/harness/bundles/java/cursor/harness-plan/reference.md +43 -81
  229. package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
  230. package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
  231. package/harness/bundles/java/cursor/harness-review/SKILL.md +3 -3
  232. package/harness/bundles/java/cursor/harness-review/protocols.md +1 -1
  233. package/harness/bundles/java/cursor/harness-run/SKILL.md +8 -2
  234. package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
  235. package/harness/bundles/java/cursor/harness-submit/checklist.md +1 -1
  236. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  237. package/harness/bundles/java/cursor/harness-test/SKILL.md +11 -2
  238. package/harness/bundles/java/cursor/scripts/harness_adoption_metrics.py +391 -0
  239. package/harness/bundles/java/cursor/scripts/harness_archive.py +46 -15
  240. package/harness/bundles/java/cursor/scripts/harness_context.py +94 -43
  241. package/harness/bundles/java/cursor/scripts/harness_events.py +1 -1
  242. package/harness/bundles/java/cursor/scripts/harness_events_sync.py +1 -0
  243. package/harness/bundles/java/cursor/scripts/harness_fixback.py +10 -10
  244. package/harness/bundles/java/cursor/scripts/harness_gate.py +92 -25
  245. package/harness/bundles/java/cursor/scripts/harness_ledger.py +4 -2
  246. package/harness/bundles/java/cursor/scripts/harness_paths.py +120 -5
  247. package/harness/bundles/java/cursor/scripts/harness_phase.py +41 -17
  248. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +16 -873
  249. package/harness/manifests/general/claude-code.json +36 -28
  250. package/harness/manifests/general/codebuddy.json +36 -28
  251. package/harness/manifests/general/codex.json +36 -28
  252. package/harness/manifests/general/cursor.json +36 -28
  253. package/harness/manifests/java/claude-code.json +38 -30
  254. package/harness/manifests/java/codebuddy.json +38 -30
  255. package/harness/manifests/java/codex.json +38 -30
  256. package/harness/manifests/java/cursor.json +38 -30
  257. package/hunter-workflow-family.json +2 -2
  258. package/package.json +1 -1
@@ -25,10 +25,9 @@ import harness_paths as hpaths # noqa: E402
25
25
 
26
26
 
27
27
  PHASE_GRAPH = {
28
- "plan": ("run",),
29
- "run": ("test",),
30
- "test": ("review", "run"),
31
- "review": ("submit", "run"),
28
+ "plan": ("execute",),
29
+ "execute": ("review", "execute"),
30
+ "review": ("submit", "execute"),
32
31
  "submit": (),
33
32
  }
34
33
 
@@ -271,14 +270,16 @@ def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
271
270
 
272
271
  未知名先过 LEGACY_PHASE_ALIASES 读时映射(阶段改名后历史 change 靠它继续可读),
273
272
  仍然解析不了才降级,并把原因与具体阶段名带在 source 里。
273
+
274
+ 权威切换(2026-08):v2 发布快照(plan-profile.json)优先,classify 工作
275
+ 副本回退——发布后的阶段计划以已审批快照为准。
274
276
  """
275
- policy_path = contract_root / "meta" / "gate-policy.json"
276
- if not policy_path.is_file():
277
+ loaded = hpaths.load_change_gate_policy(contract_root)
278
+ policy = loaded.get("policy")
279
+ if policy is None:
280
+ if loaded.get("working_error"):
281
+ return None, "legacy:policy-unreadable"
277
282
  return None, "legacy"
278
- try:
279
- policy = _read_json(policy_path)
280
- except (OSError, ValueError, json.JSONDecodeError):
281
- return None, "legacy:policy-unreadable"
282
283
  unknown: list[str] = []
283
284
  for field, source in (
284
285
  ("plannedPhases", "change"),
@@ -304,14 +305,15 @@ def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
304
305
 
305
306
  def _allowed_next_phases(contract_root: Path, from_phase: str) -> list[str]:
306
307
  planned, _source = _phase_plan(contract_root)
308
+ resolved_from = hpaths.resolve_phase_name(from_phase) or from_phase
307
309
  if planned is None:
308
- return list(PHASE_GRAPH.get(from_phase, ()))
309
- if from_phase not in planned:
310
+ return list(PHASE_GRAPH.get(resolved_from, ()))
311
+ if resolved_from not in planned:
310
312
  return []
311
- index = planned.index(from_phase)
313
+ index = planned.index(resolved_from)
312
314
  allowed = planned[index + 1 : index + 2]
313
- if from_phase in {"test", "review"} and "run" in planned:
314
- allowed = [*allowed, "run"]
315
+ if resolved_from in {"execute", "review"} and "execute" in planned:
316
+ allowed = [*allowed, "execute"]
315
317
  return list(dict.fromkeys(allowed))
316
318
 
317
319
 
@@ -330,12 +332,14 @@ def configure_phase_plan(
330
332
  except (OSError, ValueError, json.JSONDecodeError) as exc:
331
333
  return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
332
334
  normalized = [str(item).strip() for item in phases if str(item).strip()]
335
+ resolved = [hpaths.resolve_phase_name(item) for item in normalized]
333
336
  if (
334
337
  not normalized
335
338
  or len(normalized) != len(set(normalized))
336
- or any(item not in WORKFLOW_PHASES for item in normalized)
337
- or normalized[0] != "plan"
338
- or normalized[-1] != "archive"
339
+ or any(item is None for item in resolved)
340
+ or len(set(resolved)) != len(resolved)
341
+ or resolved[0] != "plan"
342
+ or resolved[-1] != "archive"
339
343
  ):
340
344
  return {
341
345
  "ok": False,
@@ -348,6 +352,9 @@ def configure_phase_plan(
348
352
  "ok": False,
349
353
  "code": "PHASE_PLAN_JUSTIFICATION_REQUIRED",
350
354
  }
355
+ # 旧名入参(plan,run,test,review,archive)归一为现阶段名后落盘;
356
+ # 上面已校验 resolved 无 None 且保序唯一。
357
+ normalized = [str(item) for item in resolved]
351
358
  worktree_path = contract_root / "meta" / "worktree.json"
352
359
  if worktree_path.is_file():
353
360
  try:
@@ -451,7 +458,7 @@ def _reselect_review_fixback(
451
458
  state_root: Path,
452
459
  executor: str,
453
460
  ) -> dict[str, Any]:
454
- """Supersede an unbegun review→submit choice with review→run."""
461
+ """Supersede an unbegun review→submit choice with review→execute."""
455
462
 
456
463
  paths = _paths(state_root)
457
464
  with _exclusive_state_lock(paths["runtime"] / "branch-selection.lock"):
@@ -459,8 +466,8 @@ def _reselect_review_fixback(
459
466
  latest = transitions[-1] if transitions else None
460
467
  if (
461
468
  isinstance(latest, dict)
462
- and latest.get("fromPhase") == "review"
463
- and latest.get("toPhase") == "run"
469
+ and hpaths.resolve_phase_name(latest.get("fromPhase")) == "review"
470
+ and hpaths.resolve_phase_name(latest.get("toPhase")) == "execute"
464
471
  and latest.get("trigger") == "review-fixback"
465
472
  ):
466
473
  return {
@@ -512,14 +519,14 @@ def _reselect_review_fixback(
512
519
  }
513
520
  paths["lease"].unlink(missing_ok=True)
514
521
  # Any submit context present here was only prepared, never begun. The
515
- # new run context is written by prepare_context after this atomic choice.
522
+ # new execute context is written by prepare_context after this atomic choice.
516
523
  paths["current"].unlink(missing_ok=True)
517
524
 
518
525
  receipt: dict[str, Any] = {
519
526
  "schemaVersion": 1,
520
527
  "changeName": change,
521
528
  "fromPhase": "review",
522
- "toPhase": "run",
529
+ "toPhase": "execute",
523
530
  "status": "OK",
524
531
  "executor": executor,
525
532
  "productCommit": _head(project),
@@ -528,8 +535,8 @@ def _reselect_review_fixback(
528
535
  + sum(
529
536
  1
530
537
  for item in transitions
531
- if item.get("fromPhase") == "review"
532
- and item.get("toPhase") == "run"
538
+ if hpaths.resolve_phase_name(item.get("fromPhase")) == "review"
539
+ and hpaths.resolve_phase_name(item.get("toPhase")) == "execute"
533
540
  ),
534
541
  "closedAt": _now().isoformat(),
535
542
  "previousReceiptHash": latest.get("receiptHash"),
@@ -543,7 +550,7 @@ def _reselect_review_fixback(
543
550
  state_root,
544
551
  transition_hash=receipt["receiptHash"],
545
552
  from_phase="review",
546
- to_phase="run",
553
+ to_phase="execute",
547
554
  )
548
555
  return {
549
556
  "ok": True,
@@ -620,12 +627,13 @@ def _claim_prepared_context(
620
627
  # 「租约已过期」和「租约已被上一阶段 close 删除」两种情况。
621
628
  adoption: dict[str, Any] | None = None
622
629
  if recovery is None and isinstance(current, dict) and (
623
- current.get("phase") != phase
630
+ hpaths.resolve_phase_name(current.get("phase")) != phase
624
631
  ):
625
632
  valid_receipt = (
626
633
  isinstance(latest, dict)
627
- and latest.get("toPhase") == phase
628
- and latest.get("fromPhase") == current.get("phase")
634
+ and hpaths.resolve_phase_name(latest.get("toPhase")) == phase
635
+ and hpaths.resolve_phase_name(latest.get("fromPhase"))
636
+ == hpaths.resolve_phase_name(current.get("phase"))
629
637
  )
630
638
  if not valid_receipt:
631
639
  return {
@@ -711,6 +719,20 @@ def _claim_prepared_context(
711
719
  }
712
720
 
713
721
 
722
+ def _unknown_phase_error(phase: Any) -> dict[str, Any]:
723
+ return {
724
+ "ok": False,
725
+ "code": "PHASE_UNKNOWN",
726
+ "phase": str(phase),
727
+ "allowedPhases": list(WORKFLOW_PHASES),
728
+ "legacyAliases": dict(hpaths.LEGACY_PHASE_ALIASES),
729
+ "message": (
730
+ f"未知阶段名 {phase!r};合法阶段 {list(WORKFLOW_PHASES)},"
731
+ f"旧名别名 {hpaths.LEGACY_PHASE_ALIASES}。"
732
+ ),
733
+ }
734
+
735
+
714
736
  def prepare_context(
715
737
  project: Path,
716
738
  *,
@@ -723,6 +745,10 @@ def prepare_context(
723
745
  preparation_id: str | None = None,
724
746
  ) -> dict[str, Any]:
725
747
  project = Path(project).resolve()
748
+ canonical_phase = hpaths.resolve_phase_name(phase)
749
+ if canonical_phase is None:
750
+ return _unknown_phase_error(phase)
751
+ phase = canonical_phase
726
752
  candidates = _active_changes(project)
727
753
  if change is None:
728
754
  if not candidates:
@@ -769,7 +795,7 @@ def prepare_context(
769
795
  paths = _paths(state_root)
770
796
  paths["runtime"].mkdir(parents=True, exist_ok=True)
771
797
  reselection: dict[str, Any] | None = None
772
- if trigger == "review-fixback" and phase == "run":
798
+ if trigger == "review-fixback" and phase == "execute":
773
799
  reselection = _reselect_review_fixback(
774
800
  project,
775
801
  change,
@@ -811,9 +837,14 @@ def prepare_context(
811
837
  _allowed_next_phases(contract_root, phase)
812
838
  if planned_phases is not None
813
839
  else (
814
- list(PHASE_GRAPH.get(str(latest.get("toPhase")), ()))
840
+ list(
841
+ PHASE_GRAPH.get(
842
+ hpaths.resolve_phase_name(str(latest.get("toPhase"))) or "",
843
+ (),
844
+ )
845
+ )
815
846
  if latest
816
- else ["plan", "run"]
847
+ else ["plan", "execute"]
817
848
  )
818
849
  )
819
850
  return {
@@ -1057,7 +1088,7 @@ def _bootstrap_v2_plan_transition(
1057
1088
  """v2 发布已 committed 却没有交接凭证时,补录 plan → to_phase 凭证。
1058
1089
 
1059
1090
  v2 plan finalize 只写 plan-events.ndjson 与发布 journal,不碰 context 事务
1060
- 存储,于是 run 阶段 prepare 必报 HANDOFF_REQUIRED、begin 必报
1091
+ 存储,于是 execute 阶段 prepare 必报 HANDOFF_REQUIRED、begin 必报
1061
1092
  LEGACY_BOOTSTRAP_REQUIRED——两个错误都不给恢复路径,调用方只能读脚本源码
1062
1093
  自己拼出 classify + configure-plan + close,参数全靠现编,可审计性更差。
1063
1094
 
@@ -1104,6 +1135,14 @@ def close_transition(
1104
1135
  status: str = "OK",
1105
1136
  ) -> dict[str, Any]:
1106
1137
  project = Path(project).resolve()
1138
+ canonical_from = hpaths.resolve_phase_name(from_phase)
1139
+ canonical_to = hpaths.resolve_phase_name(to_phase)
1140
+ if canonical_from is None:
1141
+ return _unknown_phase_error(from_phase)
1142
+ if canonical_to is None:
1143
+ return _unknown_phase_error(to_phase)
1144
+ from_phase = canonical_from
1145
+ to_phase = canonical_to
1107
1146
  try:
1108
1147
  contract_root, _contract_data, state_root = _contract(project, change)
1109
1148
  except (OSError, ValueError, json.JSONDecodeError) as exc:
@@ -1179,8 +1218,8 @@ def close_transition(
1179
1218
  latest = transitions[-1] if transitions else None
1180
1219
  if (
1181
1220
  isinstance(latest, dict)
1182
- and latest.get("fromPhase") == from_phase
1183
- and latest.get("toPhase") == to_phase
1221
+ and hpaths.resolve_phase_name(latest.get("fromPhase")) == from_phase
1222
+ and hpaths.resolve_phase_name(latest.get("toPhase")) == to_phase
1184
1223
  and latest.get("status") == status
1185
1224
  and latest.get("artifacts") == artifact_entries
1186
1225
  and (executor is None or latest.get("executor") == executor)
@@ -1201,7 +1240,9 @@ def close_transition(
1201
1240
  effective_executor = executor or str(lease.get("owner") or "")
1202
1241
  if not effective_executor:
1203
1242
  return {"ok": False, "code": "CONTEXT_EXECUTOR_REQUIRED"}
1204
- if lease.get("owner") != effective_executor or lease.get("phase") != from_phase:
1243
+ if lease.get("owner") != effective_executor or (
1244
+ hpaths.resolve_phase_name(lease.get("phase")) != from_phase
1245
+ ):
1205
1246
  return {
1206
1247
  "ok": False,
1207
1248
  "code": "CONTEXT_LEASE_MISMATCH",
@@ -1220,8 +1261,8 @@ def close_transition(
1220
1261
  + sum(
1221
1262
  1
1222
1263
  for item in transitions
1223
- if item.get("fromPhase") == from_phase
1224
- and item.get("toPhase") == to_phase
1264
+ if hpaths.resolve_phase_name(item.get("fromPhase")) == from_phase
1265
+ and hpaths.resolve_phase_name(item.get("toPhase")) == to_phase
1225
1266
  )
1226
1267
  )
1227
1268
  receipt: dict[str, Any] = {
@@ -1247,7 +1288,7 @@ def close_transition(
1247
1288
  _append_ndjson(paths["transitions"], receipt)
1248
1289
  paths["lease"].unlink(missing_ok=True)
1249
1290
  invalidation = None
1250
- if to_phase == "run" and from_phase in {"test", "review"}:
1291
+ if to_phase == "execute" and from_phase in {"execute", "review"}:
1251
1292
  invalidation = _invalidate_for_fixback(
1252
1293
  state_root,
1253
1294
  transition_hash=receipt["receiptHash"],
@@ -1326,7 +1367,7 @@ def _begin_transition_unlocked(
1326
1367
  ),
1327
1368
  }
1328
1369
  receipt = transitions[-1]
1329
- if receipt.get("toPhase") != phase:
1370
+ if hpaths.resolve_phase_name(receipt.get("toPhase")) != phase:
1330
1371
  return {
1331
1372
  "ok": False,
1332
1373
  "code": "HANDOFF_REQUIRED",
@@ -1403,6 +1444,10 @@ def begin_transition(
1403
1444
  """Acknowledge a transition while serializing branch selection."""
1404
1445
 
1405
1446
  project = Path(project).resolve()
1447
+ canonical_phase = hpaths.resolve_phase_name(phase)
1448
+ if canonical_phase is None:
1449
+ return _unknown_phase_error(phase)
1450
+ phase = canonical_phase
1406
1451
  try:
1407
1452
  _contract_root, _contract_data, state_root = _contract(project, change)
1408
1453
  except (OSError, ValueError, json.JSONDecodeError) as exc:
@@ -1429,6 +1474,10 @@ def cancel_prepared_context(
1429
1474
  """Remove a target-phase preparation that never obtained its gate."""
1430
1475
 
1431
1476
  project = Path(project).resolve()
1477
+ canonical_phase = hpaths.resolve_phase_name(phase)
1478
+ if canonical_phase is None:
1479
+ return _unknown_phase_error(phase)
1480
+ phase = canonical_phase
1432
1481
  try:
1433
1482
  _contract_root, _contract_data, state_root = _contract(project, change)
1434
1483
  except (OSError, ValueError, json.JSONDecodeError) as exc:
@@ -1443,7 +1492,8 @@ def cancel_prepared_context(
1443
1492
  except (OSError, ValueError, json.JSONDecodeError):
1444
1493
  current = None
1445
1494
  if isinstance(current, dict) and (
1446
- current.get("phase") == phase and current.get("executor") == executor
1495
+ hpaths.resolve_phase_name(current.get("phase")) == phase
1496
+ and current.get("executor") == executor
1447
1497
  and (
1448
1498
  preparation_id is None
1449
1499
  or current.get("preparationId") == preparation_id
@@ -1458,7 +1508,8 @@ def cancel_prepared_context(
1458
1508
  except (OSError, ValueError, json.JSONDecodeError):
1459
1509
  lease = None
1460
1510
  if isinstance(lease, dict) and (
1461
- lease.get("phase") == phase and lease.get("owner") == executor
1511
+ hpaths.resolve_phase_name(lease.get("phase")) == phase
1512
+ and lease.get("owner") == executor
1462
1513
  and (
1463
1514
  preparation_id is None
1464
1515
  or lease.get("preparationId") == preparation_id
@@ -1472,7 +1523,7 @@ def cancel_prepared_context(
1472
1523
  for item in begins
1473
1524
  if not (
1474
1525
  receipt_hash is not None
1475
- and item.get("phase") == phase
1526
+ and hpaths.resolve_phase_name(item.get("phase")) == phase
1476
1527
  and item.get("executor") == executor
1477
1528
  and item.get("receiptHash") == receipt_hash
1478
1529
  and (
@@ -2014,7 +2014,7 @@ def cmd_append(args: argparse.Namespace) -> int:
2014
2014
  "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same "
2015
2015
  "attempt. A retry needs a fresh run id AND the next attempt number "
2016
2016
  "(attempts are global per phase, not per run id). For a published "
2017
- "plan use harness_plan_finalize.py republish, which allocates both.",
2017
+ "plan rerun plan evidence-pack + plan finalize with the next attempt.",
2018
2018
  as_json=as_json,
2019
2019
  error_code="PHASE_ALREADY_CLOSED",
2020
2020
  )
@@ -76,6 +76,7 @@ EVENT_SUMMARY_MAX_LENGTH = 500
76
76
 
77
77
  PHASE_LABELS = {
78
78
  "plan": "计划",
79
+ "execute": "执行",
79
80
  "run": "编码",
80
81
  "test": "测试",
81
82
  "review": "评审",
@@ -333,7 +333,7 @@ def open_review_batch(
333
333
  _write_json(_session_path(change_dir), session)
334
334
  he.append_event(
335
335
  change_dir,
336
- phase="run",
336
+ phase="execute",
337
337
  type_="decision",
338
338
  note=f"已从评审结果创建修复批次,共 {len(issue_entries)} 个代码修复项。",
339
339
  run_id=run_id,
@@ -355,7 +355,7 @@ def _default_context_prepare(**kwargs: Any) -> dict[str, Any]:
355
355
 
356
356
  return hctx.prepare_context(
357
357
  kwargs["project"],
358
- phase="run",
358
+ phase="execute",
359
359
  executor=kwargs["executor"],
360
360
  change=kwargs["change"],
361
361
  trigger="review-fixback",
@@ -369,7 +369,7 @@ def _default_context_begin(**kwargs: Any) -> dict[str, Any]:
369
369
  return hctx.begin_transition(
370
370
  kwargs["project"],
371
371
  kwargs["change"],
372
- phase="run",
372
+ phase="execute",
373
373
  executor=kwargs["executor"],
374
374
  preparation_id=kwargs["preparation_id"],
375
375
  )
@@ -381,7 +381,7 @@ def _default_context_cancel(**kwargs: Any) -> dict[str, Any]:
381
381
  return hctx.cancel_prepared_context(
382
382
  kwargs["project"],
383
383
  kwargs["change"],
384
- phase="run",
384
+ phase="execute",
385
385
  executor=kwargs["executor"],
386
386
  preparation_id=kwargs["preparation_id"],
387
387
  )
@@ -394,7 +394,7 @@ def _default_gate_begin(**kwargs: Any) -> dict[str, Any]:
394
394
  "begin",
395
395
  "--json",
396
396
  "--phase",
397
- "run",
397
+ "execute",
398
398
  "--change",
399
399
  str(kwargs["change"]),
400
400
  "--project",
@@ -487,7 +487,7 @@ def launch_review_fixback(
487
487
  started_at = time.monotonic()
488
488
  start_event = he.append_event(
489
489
  change_dir,
490
- phase="run",
490
+ phase="execute",
491
491
  type_="phase.prepare.start",
492
492
  note="正在检查评审修复的前置条件。",
493
493
  run_id=effective_run_id,
@@ -522,7 +522,7 @@ def launch_review_fixback(
522
522
  message = str(result.get("message") or result.get("error") or "前置条件未满足,修复未启动。")
523
523
  he.append_event(
524
524
  change_dir,
525
- phase="run",
525
+ phase="execute",
526
526
  type_="phase.prepare.end",
527
527
  status="BLOCKED",
528
528
  code=code,
@@ -541,7 +541,7 @@ def launch_review_fixback(
541
541
  "ok": False,
542
542
  "code": code,
543
543
  "message": message,
544
- "recoveryAction": "处理提示的前置条件后重新执行 /harness-run --fixback",
544
+ "recoveryAction": "处理提示的前置条件后重新执行 /harness-execute --fixback",
545
545
  "preparationDurationMs": duration,
546
546
  "contextCancellation": cancellation,
547
547
  }
@@ -566,7 +566,7 @@ def launch_review_fixback(
566
566
  duration = max(0, round((time.monotonic() - started_at) * 1000))
567
567
  he.append_event(
568
568
  change_dir,
569
- phase="run",
569
+ phase="execute",
570
570
  type_="phase.prepare.end",
571
571
  status="STARTED",
572
572
  message="前置条件已满足,修复编码已启动。",
@@ -665,7 +665,7 @@ def resume_batch(
665
665
  _write_json(session_path, session)
666
666
  he.append_event(
667
667
  change_dir,
668
- phase="run",
668
+ phase="execute",
669
669
  type_="decision",
670
670
  note=(
671
671
  "已恢复现有修复批次,将从未完成步骤继续。"