@hunter-harness/workflow-harness 0.2.85 → 0.2.86

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 (218) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  3. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +3 -2
  7. package/harness/bundles/general/claude-code/harness-plan/checklist.md +18 -79
  8. package/harness/bundles/general/claude-code/harness-plan/protocols.md +3 -1
  9. package/harness/bundles/general/claude-code/harness-plan/reference.md +37 -8
  10. package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
  11. package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-run/SKILL.md +6 -4
  14. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  17. package/harness/bundles/general/claude-code/scripts/harness_change.py +25 -7
  18. package/harness/bundles/general/claude-code/scripts/harness_context.py +62 -19
  19. package/harness/bundles/general/claude-code/scripts/harness_fixback.py +4 -0
  20. package/harness/bundles/general/claude-code/scripts/harness_gate.py +208 -72
  21. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +43 -0
  22. package/harness/bundles/general/claude-code/scripts/harness_paths.py +60 -0
  23. package/harness/bundles/general/claude-code/scripts/harness_phase.py +24 -6
  24. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +124 -1
  25. package/harness/bundles/general/claude-code/scripts/harness_review.py +25 -4
  26. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  27. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  28. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +3 -2
  32. package/harness/bundles/general/codebuddy/harness-plan/checklist.md +18 -79
  33. package/harness/bundles/general/codebuddy/harness-plan/protocols.md +3 -1
  34. package/harness/bundles/general/codebuddy/harness-plan/reference.md +37 -8
  35. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
  36. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  38. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +6 -4
  39. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  40. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/scripts/harness_change.py +25 -7
  43. package/harness/bundles/general/codebuddy/scripts/harness_context.py +62 -19
  44. package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +4 -0
  45. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +208 -72
  46. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +43 -0
  47. package/harness/bundles/general/codebuddy/scripts/harness_paths.py +60 -0
  48. package/harness/bundles/general/codebuddy/scripts/harness_phase.py +24 -6
  49. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +124 -1
  50. package/harness/bundles/general/codebuddy/scripts/harness_review.py +25 -4
  51. package/harness/bundles/general/codex/.harness-build.json +1 -1
  52. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  53. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  54. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  55. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  56. package/harness/bundles/general/codex/harness-plan/SKILL.md +3 -2
  57. package/harness/bundles/general/codex/harness-plan/checklist.md +18 -79
  58. package/harness/bundles/general/codex/harness-plan/protocols.md +3 -1
  59. package/harness/bundles/general/codex/harness-plan/reference.md +37 -8
  60. package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
  61. package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
  62. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  63. package/harness/bundles/general/codex/harness-run/SKILL.md +6 -4
  64. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  67. package/harness/bundles/general/codex/scripts/harness_change.py +25 -7
  68. package/harness/bundles/general/codex/scripts/harness_context.py +62 -19
  69. package/harness/bundles/general/codex/scripts/harness_fixback.py +4 -0
  70. package/harness/bundles/general/codex/scripts/harness_gate.py +208 -72
  71. package/harness/bundles/general/codex/scripts/harness_ledger.py +43 -0
  72. package/harness/bundles/general/codex/scripts/harness_paths.py +60 -0
  73. package/harness/bundles/general/codex/scripts/harness_phase.py +24 -6
  74. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +124 -1
  75. package/harness/bundles/general/codex/scripts/harness_review.py +25 -4
  76. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  77. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  78. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  79. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  80. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  81. package/harness/bundles/general/cursor/harness-plan/SKILL.md +3 -2
  82. package/harness/bundles/general/cursor/harness-plan/checklist.md +18 -79
  83. package/harness/bundles/general/cursor/harness-plan/protocols.md +3 -1
  84. package/harness/bundles/general/cursor/harness-plan/reference.md +37 -8
  85. package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
  86. package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
  87. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  88. package/harness/bundles/general/cursor/harness-run/SKILL.md +6 -4
  89. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  90. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  91. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  92. package/harness/bundles/general/cursor/scripts/harness_change.py +25 -7
  93. package/harness/bundles/general/cursor/scripts/harness_context.py +62 -19
  94. package/harness/bundles/general/cursor/scripts/harness_fixback.py +4 -0
  95. package/harness/bundles/general/cursor/scripts/harness_gate.py +208 -72
  96. package/harness/bundles/general/cursor/scripts/harness_ledger.py +43 -0
  97. package/harness/bundles/general/cursor/scripts/harness_paths.py +60 -0
  98. package/harness/bundles/general/cursor/scripts/harness_phase.py +24 -6
  99. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +124 -1
  100. package/harness/bundles/general/cursor/scripts/harness_review.py +25 -4
  101. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  102. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  103. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  104. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  105. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  106. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  107. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  108. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +3 -2
  109. package/harness/bundles/java/claude-code/harness-plan/checklist.md +18 -79
  110. package/harness/bundles/java/claude-code/harness-plan/protocols.md +3 -1
  111. package/harness/bundles/java/claude-code/harness-plan/reference.md +37 -8
  112. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
  113. package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
  114. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  115. package/harness/bundles/java/claude-code/harness-run/SKILL.md +6 -4
  116. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  117. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  118. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  119. package/harness/bundles/java/claude-code/scripts/harness_change.py +25 -7
  120. package/harness/bundles/java/claude-code/scripts/harness_context.py +62 -19
  121. package/harness/bundles/java/claude-code/scripts/harness_fixback.py +4 -0
  122. package/harness/bundles/java/claude-code/scripts/harness_gate.py +208 -72
  123. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +43 -0
  124. package/harness/bundles/java/claude-code/scripts/harness_paths.py +60 -0
  125. package/harness/bundles/java/claude-code/scripts/harness_phase.py +24 -6
  126. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +124 -1
  127. package/harness/bundles/java/claude-code/scripts/harness_review.py +25 -4
  128. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  129. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  130. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  131. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  132. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  133. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  134. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  135. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +3 -2
  136. package/harness/bundles/java/codebuddy/harness-plan/checklist.md +18 -79
  137. package/harness/bundles/java/codebuddy/harness-plan/protocols.md +3 -1
  138. package/harness/bundles/java/codebuddy/harness-plan/reference.md +37 -8
  139. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
  140. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
  141. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  142. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +6 -4
  143. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  144. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  145. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  146. package/harness/bundles/java/codebuddy/scripts/harness_change.py +25 -7
  147. package/harness/bundles/java/codebuddy/scripts/harness_context.py +62 -19
  148. package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +4 -0
  149. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +208 -72
  150. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +43 -0
  151. package/harness/bundles/java/codebuddy/scripts/harness_paths.py +60 -0
  152. package/harness/bundles/java/codebuddy/scripts/harness_phase.py +24 -6
  153. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +124 -1
  154. package/harness/bundles/java/codebuddy/scripts/harness_review.py +25 -4
  155. package/harness/bundles/java/codex/.harness-build.json +1 -1
  156. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  157. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  158. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  159. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  160. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  161. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  162. package/harness/bundles/java/codex/harness-plan/SKILL.md +3 -2
  163. package/harness/bundles/java/codex/harness-plan/checklist.md +18 -79
  164. package/harness/bundles/java/codex/harness-plan/protocols.md +3 -1
  165. package/harness/bundles/java/codex/harness-plan/reference.md +37 -8
  166. package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
  167. package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
  168. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  169. package/harness/bundles/java/codex/harness-run/SKILL.md +6 -4
  170. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  171. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  172. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  173. package/harness/bundles/java/codex/scripts/harness_change.py +25 -7
  174. package/harness/bundles/java/codex/scripts/harness_context.py +62 -19
  175. package/harness/bundles/java/codex/scripts/harness_fixback.py +4 -0
  176. package/harness/bundles/java/codex/scripts/harness_gate.py +208 -72
  177. package/harness/bundles/java/codex/scripts/harness_ledger.py +43 -0
  178. package/harness/bundles/java/codex/scripts/harness_paths.py +60 -0
  179. package/harness/bundles/java/codex/scripts/harness_phase.py +24 -6
  180. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +124 -1
  181. package/harness/bundles/java/codex/scripts/harness_review.py +25 -4
  182. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  183. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  184. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  185. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  186. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  187. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  188. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  189. package/harness/bundles/java/cursor/harness-plan/SKILL.md +3 -2
  190. package/harness/bundles/java/cursor/harness-plan/checklist.md +18 -79
  191. package/harness/bundles/java/cursor/harness-plan/protocols.md +3 -1
  192. package/harness/bundles/java/cursor/harness-plan/reference.md +37 -8
  193. package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
  194. package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
  195. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  196. package/harness/bundles/java/cursor/harness-run/SKILL.md +6 -4
  197. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  198. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  199. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  200. package/harness/bundles/java/cursor/scripts/harness_change.py +25 -7
  201. package/harness/bundles/java/cursor/scripts/harness_context.py +62 -19
  202. package/harness/bundles/java/cursor/scripts/harness_fixback.py +4 -0
  203. package/harness/bundles/java/cursor/scripts/harness_gate.py +208 -72
  204. package/harness/bundles/java/cursor/scripts/harness_ledger.py +43 -0
  205. package/harness/bundles/java/cursor/scripts/harness_paths.py +60 -0
  206. package/harness/bundles/java/cursor/scripts/harness_phase.py +24 -6
  207. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +124 -1
  208. package/harness/bundles/java/cursor/scripts/harness_review.py +25 -4
  209. package/harness/manifests/general/claude-code.json +26 -26
  210. package/harness/manifests/general/codebuddy.json +26 -26
  211. package/harness/manifests/general/codex.json +26 -26
  212. package/harness/manifests/general/cursor.json +26 -26
  213. package/harness/manifests/java/claude-code.json +28 -28
  214. package/harness/manifests/java/codebuddy.json +28 -28
  215. package/harness/manifests/java/codex.json +28 -28
  216. package/harness/manifests/java/cursor.json +28 -28
  217. package/hunter-workflow-family.json +4 -4
  218. package/package.json +1 -1
@@ -351,7 +351,8 @@ status: approved
351
351
  | `.harness/changes/<change>/plans/<change>-plan.md` | ✅(派生) | ✅ |
352
352
  | `.harness/changes/<change>/plans/<change>-implementation-detail.md` | ✅(派生) | ✅ |
353
353
  | `.harness/changes/<change>/plans/<change>-test-scenarios.md` | ✅(派生) | ✅ |
354
- | `.harness/changes/<change>/meta/gate-policy.json` | | ✅ |
354
+ | `.harness/changes/<change>/meta/gate-policy.json` | ✅(**classify 写,非发布产物**) | ✅ |
355
+ | `.harness/changes/<change>/meta/plan-profile.json` | ✅(派生) | — |
355
356
  | `.harness/changes/<change>/meta/worktree.json` | ✅ | ✅ |
356
357
  | `.harness/changes/<change>/meta/implementation-checkpoints.json` | ✅ | ✅ |
357
358
  | `.harness/changes/<change>/meta/scenario-manifest.json` | ✅ | ✅ |
@@ -363,6 +364,25 @@ status: approved
363
364
 
364
365
  legacy 的 `plan-finalization.json.files` 必须完整列出 design、plan、implementation-detail、test-scenarios、gate-policy、worktree 六项标准输入。`verify` 对缺项、重复项、越界路径以及 symlink/junction/reparse point 一律 fail-closed;不得通过删减收据文件集后重算哈希来绕过完整性检查。
365
366
 
367
+ ### 两套分类模型的边界(别把它们当同一件事)
368
+
369
+ | | Python `tier` | TS `mode` |
370
+ |---|---|---|
371
+ | 取值 | `fast` / `standard` / `full` | `quick` / `standard` / `assurance` |
372
+ | 写到哪 | `meta/gate-policy.json`(`schemaVersion:1`,camelCase) | `meta/plan-profile.json`(artifact 包装体,snake_case) |
373
+ | 谁写 | `harness_gate.py classify`(阶段 0.5 由 bootstrap-plan 调起) | `hunter-harness plan finalize`(阶段 8 发布) |
374
+ | 地位 | **门禁权威**:驱动 `requiredGateDag`、`requiredValidationsByPhase`,run/test 开门读它 | **派生视图**:供展示与下游只读消费,门禁不读 |
375
+ | 输入 | 计划文档的「风险等级」正则 + capabilityGates 信号 | 自然输入里手填的 `risk_signals` |
376
+
377
+ **两者不共用文件名。** v2 发布对 `binding.ownership_paths` 逐个原子覆盖,若派生视图占用 `meta/gate-policy.json`,阶段 8 会把 classify 写的那份换成包装体,之后 `gate begin --phase run` 直接 `POLICY_LOAD_FAILED`——`harness_gate.effective_workflow_policy` 读 `schemaVersion` 拿不到就 raise。
378
+
379
+ **为什么派生视图目前不能当权威**(将来要接通就得先解决这两条):
380
+
381
+ 1. `plan evidence-pack` 对 `risk_signals` **不做任何推断**——不读 git diff、不扫路径、不解析 frontmatter,值完全来自自然输入里手填的那个数组。拿它当门禁权威等于让 agent 自声明低风险来跳门禁。
382
+ 2. 命令里的 `capabilities` 是写死的(`is_git:true, has_remote:true, uses_worktree:false`),用户在阶段 0.6 通过 `configure-plan` 选的阶段计划被完全忽略,`planned_phases` 退化成 mode 的纯函数。
383
+
384
+ `meta/implementation-checkpoints.json` 的情况不同:v2 包装体里的 `content.foundation_gate` 信息是够的,所以门禁在**只读侧**解包(`checkpoint_status` 同时认 `checkpoints[]`、顶层 `foundationGate` 与 v2 包装体三种形状),不改文件名。注意写回路径(`gate checkpoint approve`)仍然操作原始文档——用归一化结构覆盖会破坏 v2 产物的哈希绑定。
385
+
366
386
  ### 阶段 8 v2 路径(结构化证据包流程,新 change 优先)
367
387
 
368
388
  阶段 8 v2 标准三步流(证据包自举,阶段 14 起可用):
@@ -422,13 +442,22 @@ npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidenc
422
442
  | `context` | project_id/run_id/branch_name/attempt(复用 plan-run-id 与 attempt) | 0.5 |
423
443
  | `expected_baseline` | 首次发布 `{state:"absent", manifest_hash:null, generation:0}` | 8 |
424
444
 
425
- > ⚠️ **已知缺口:v2 派生的 `meta/scenario-manifest.json` 目前喂不了 run/test 门禁。**
426
- > 它是 artifact 包装体,每条场景只有 `scenario_id/coverage_dimension/execution_level/
427
- > evidence_requirements/risk_level/task_refs/requirement_refs`;而门禁按
428
- > `id/priority/requiredEvidenceKind/ownerPhase/executableTestId/testFile/testTitle`
429
- > 判定哪些场景需要 ledger 证据。缺 `priority` 与 `requiredEvidenceKind` 时"必需场景"会算成空集,
430
- > 所以门禁**明确报 `SCENARIO_MANIFEST_V2_UNSUPPORTED` 并列出 `missingFields`,绝不静默放行**。
431
- > 需要 ledger 证据闭环的变更,在 v2 场景契约补齐这些字段前请走 legacy 路径。
445
+ > **场景契约与门禁的对接**(曾经的已知缺口,现已打通)
446
+ >
447
+ > v2 派生的 `meta/scenario-manifest.json` 是 artifact 包装体,键名与门禁消费的不同。消费端
448
+ > (`harness_gate` 的 C9、`harness_ledger` 的三处)统一调 `harness_plan_finalize.unpack_v2_scenario_manifest`
449
+ > 解成 legacy 形状再判定:`scenario_id→id`、`owner_phase→ownerPhase`、`required_evidence_kind→requiredEvidenceKind`
450
+ > 、可执行三元 `executable_test_id/test_file/test_title→executableTestId/testFile/testTitle`。
451
+ > `required_evidence_kind` `priority` 派生(P0/P1→`ledger`,P2→`advisory`),写在 artifact 里,
452
+ > 消费侧不重推。
453
+ >
454
+ > 因此自然输入的 `scenarios` **必须**带 `priority`(P0/P1/P2)与 `owner_phase`。缺任一项,
455
+ > 门禁**逐场景**校验后报 `SCENARIO_MANIFEST_V2_UNSUPPORTED` 并列出 `missingFields`,绝不静默放行
456
+ > ——逐场景而不是取键的并集:并集只要有一条场景带了 `priority` 就算通过,其余缺字段的会静默
457
+ > 落进非必需集,"必需场景"随之缩水。
458
+ >
459
+ > 可执行三元是**可选**的,但要么整组给全、要么整组省略。ledger 场景全部带齐 → manifest 声明
460
+ > `schemaVersion 2`,关门可绑结构化执行收据;否则降为 1。
432
461
 
433
462
  - **证据包**(`plan-evidence.json`)是命令推导的产物(trusted/publication/context/baseline),不得手改;任何字段变化必须改自然输入后重跑 evidence-pack。
434
463
  - **成功语义**:finalize exit 0 且 `code:"PLAN_FINALIZED"`。落盘事实 = 八 target(plans/*.md ×4 + meta/*.json ×4)+ `meta/publication-journals/<op>.json`(状态 committed)+ `meta/plan-events.ndjson`(artifact_published/phase_ended)。确定性门失败 exit 1 且 `code:"PLAN_FINALIZE_DETERMINISTIC_FAILED"` 附 findings——此时必须回到对应阶段修正规划内容,**不得**手改证据包或 staged 内容绕过。
@@ -3,7 +3,7 @@ name: harness-pull
3
3
  description: 从 Hunter Platform 下拉配置/规则/架构/指令(及显式来源分支的分支文件恢复)。仅当用户显式调用
4
4
  /harness-pull 或明确说'从平台拉取/恢复'时使用;不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
7
7
  # harness-pull — 从 Hunter Platform 下拉与恢复
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-push
3
3
  description: 上传本地配置/规则/架构/指令(及显式归档)到 Hunter Platform。仅当用户显式调用 /harness-push
4
4
  或明确说'上传到平台'时使用;不得因存在本地修改就自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
7
7
  # harness-push — 上传到 Hunter Platform
8
8
 
9
9
  ## Purpose
@@ -4,7 +4,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。仅当用户显式调用 /harness-review 时使用;不得在
5
5
  test 结束后自动接续执行。
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
8
8
  # harness-review — 代码审查
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。仅当用户显式调用
4
4
  /harness-run 时使用;不得因用户提到编码/实现就自动触发,也不得被其他阶段 skill 自动接续。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -83,13 +83,15 @@ python <skills-root>/scripts/harness_test_runner.py exec --project . --timeout-s
83
83
 
84
84
  返回 `TEST_RUN_ALREADY_ACTIVE`(退出码 3)说明**已有构建在跑**:等它结束或用 `lock-status` 查明持有者,**不得**改用裸跑绕开锁,也不得另起一个并行构建。确认持有进程已消失时才用 `lock-reap` 回收。
85
85
 
86
- **长阶段租约续期**:`gate begin` 的租约默认 TTL 3600 秒,而 run 阶段常常跑得更久。租约过期不会中断执行,只会让最后的 `gate close` `LEASE_ABSENT`。凡预计超过 1 小时的阶段,每完成一个变更簇就用本阶段**原 run-id** 续租一次(同 run-id 重复 claim 即刷新,不会新开 attempt):
86
+ **长阶段租约**:`gate begin` 的租约默认 TTL 3600 秒,而 run 阶段常常跑得更久。租约过期不会中断执行,**也不再阻断收尾**:`gate close` 发现租约过期而 run-id 仍是本阶段的,会自动用原 run-id 重取并照常关门,只在返回体的 `leaseLapsed` 里记下这个阶段跑过了 TTL。过期本身不说明所有权变了——抢占会重写租约文件把 run-id 换掉,那时报的是 `LEASE_OWNER_MISMATCH`。
87
+
88
+ 因此**不需要**为了长阶段定期续租。确实想让租约始终有效时,用本阶段原 run-id 刷新即可(同 run-id 重复 claim 即刷新,不会新开 attempt):
87
89
 
88
90
  ```text
89
91
  python <skills-root>/scripts/harness_change.py claim --change <id> --phase run --run-id <本阶段 run-id> --ttl-seconds 3600 --json
90
92
  ```
91
93
 
92
- 已经报了 `LEASE_ABSENT` 也按同一条命令恢复——错误响应的 `resumeRunId` 就是要用的 run-id;**不要**重跑 `gate begin`,那会新开 attempt 并丢失本轮 capsule。
94
+ 仍然报 `LEASE_ABSENT` 说明这个阶段**根本没有租约记录**(从未 begin,或已被释放),不是超时;先确认 `gate begin` 真的跑过。报 `LEASE_INVALID` 说明租约文件损坏,无法证明没被抢占,需人工确认无并发后再用上面的命令重取。两种情况都**不要**重跑 `gate begin`——那会新开 attempt 并丢失本轮 capsule。
93
95
 
94
96
  **执行器边界**:优先使用项目 build profile 和已有测试入口。禁止为了绕过 ESM、路径或参数问题临时生成 `.js`、`require` 脚本;需要文件式 runner 时使用项目已有入口,确需新增时遵循项目模块类型(例如 ESM 使用 `.mjs`)。runner 包装说明写入 `runnerCommand` 元数据,不得拼进账本的规范 `command`。
95
97
 
@@ -123,7 +125,7 @@ python <skills-root>/scripts/harness_change.py claim --change <id> --phase run -
123
125
  | **关门/状态** | 10 项关门检查;持久化 run-task-status;仅 run-owned P0 静态-only 导致 WARN;test-owned 待办正常移交 |
124
126
  | **Worktree** | `requested=true` 时代码只写 worktree |
125
127
  | **构建/测试** | 一律经 `harness_test_runner.py exec`;禁止裸跑 mvn/gradle/npm test;`TEST_RUN_ALREADY_ACTIVE` 表示已有构建在跑,等待而非另起 |
126
- | **租约** | 阶段超 1 小时按变更簇用原 run-id 续租;`LEASE_ABSENT` `harness_change.py claim` 恢复,不重跑 begin |
128
+ | **租约** | 阶段超 TTL close 自动用原 run-id 重取,无需续租;`LEASE_ABSENT`(无记录)/`LEASE_INVALID`(文件损坏)才需人工 `harness_change.py claim`,一律不重跑 begin |
127
129
  | **PowerShell** | 所有 git 经 `powershell.exe -NoProfile -Command` |
128
130
 
129
131
  ### 陈旧测试安全修复与精确跟踪
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff 合并回主分支。仅当用户显式调用
4
4
  /harness-submit(或 /harness-merge 重入合并段)时使用;用户口头说'提交/commit/push'时必须先确认,不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose
@@ -4,7 +4,7 @@ description: Use when the user asks to synchronize, refresh, or validate Harness
4
4
  metadata, adapters, remote knowledge ownership, instruction entrypoints,
5
5
  config origins, or CodeGraph status.
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
8
8
  # harness-sync
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-test
3
3
  description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。仅当用户显式调用 /harness-test
4
4
  时使用;不得在 run 结束后自动接续执行。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=c0c77f0c226fcac9; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=91d10993155551bc; overlay=none; agent=codex; do not edit -->
7
7
  # harness-test — 测试执行
8
8
 
9
9
  ## Purpose
@@ -564,18 +564,36 @@ def _lease_expired(lease: dict[str, Any]) -> bool:
564
564
  return dt.datetime.now().astimezone() >= exp_dt
565
565
 
566
566
 
567
- def inspect_lease(project_root: Path, change_id: str) -> dict[str, Any] | None:
568
- """Return the current non-expired lease without mutating it."""
567
+ def inspect_lease_state(project_root: Path, change_id: str) -> dict[str, Any]:
568
+ """Read the lease without mutating it, keeping the three failure modes apart.
569
+
570
+ ``inspect_lease`` collapses absent / expired / corrupt into ``None``, so a
571
+ caller cannot tell "nobody ever held this" from "the phase outlived its
572
+ TTL". Those need different answers: an expired lease still carries the
573
+ runId that proves who owns the phase, a corrupt one proves nothing, and an
574
+ absent one may just mean the phase already closed.
575
+
576
+ state is one of ``active`` | ``expired`` | ``absent`` | ``corrupt``. The
577
+ lease dict is returned for ``active`` and ``expired``; ``None`` otherwise.
578
+ """
569
579
  path = _lease_path(project_root, change_id)
570
580
  if not path.is_file():
571
- return None
581
+ return {"state": "absent", "lease": None}
572
582
  try:
573
583
  lease = _read_json(path)
574
584
  except (OSError, json.JSONDecodeError):
575
- return None
576
- if not isinstance(lease, dict) or _lease_expired(lease):
577
- return None
578
- return lease
585
+ return {"state": "corrupt", "lease": None}
586
+ if not isinstance(lease, dict):
587
+ return {"state": "corrupt", "lease": None}
588
+ if _lease_expired(lease):
589
+ return {"state": "expired", "lease": lease}
590
+ return {"state": "active", "lease": lease}
591
+
592
+
593
+ def inspect_lease(project_root: Path, change_id: str) -> dict[str, Any] | None:
594
+ """Return the current non-expired lease without mutating it."""
595
+ state = inspect_lease_state(project_root, change_id)
596
+ return state["lease"] if state["state"] == "active" else None
579
597
 
580
598
 
581
599
  def _claim_lease_locked(
@@ -21,6 +21,7 @@ if str(SCRIPTS_DIR) not in sys.path:
21
21
  sys.path.insert(0, str(SCRIPTS_DIR))
22
22
 
23
23
  import harness_ledger as hl # noqa: E402
24
+ import harness_paths as hpaths # noqa: E402
24
25
 
25
26
 
26
27
  PHASE_GRAPH = {
@@ -31,17 +32,8 @@ PHASE_GRAPH = {
31
32
  "submit": (),
32
33
  }
33
34
 
34
- WORKFLOW_PHASES = (
35
- "plan",
36
- "run",
37
- "test",
38
- "review",
39
- "package",
40
- "apidoc",
41
- "submit",
42
- "merge",
43
- "archive",
44
- )
35
+ # 唯一权威清单在 harness_paths(叶子模块,harness_phase 引的是同一个对象)。
36
+ WORKFLOW_PHASES = hpaths.WORKFLOW_PHASES
45
37
 
46
38
  DISPLAY_TITLE_MAX_LENGTH = 80
47
39
 
@@ -270,13 +262,24 @@ def _contract(project: Path, change: str) -> tuple[Path, dict[str, Any], Path]:
270
262
 
271
263
 
272
264
  def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
265
+ """读出阶段计划;无法采信时说明原因,而不是悄悄退成 legacy。
266
+
267
+ 以前这里遇到未知阶段名一律返回 (None, "legacy"):阶段计划整个失效,
268
+ `_allowed_next_phases` 退回硬编码的 PHASE_GRAPH,而调用方看不出发生过这件事。
269
+ 未知阶段名不是假想问题——阶段清单少一个 merge 时,worktree 变更的
270
+ plannedPhases 就会带着 merge 走到这里。
271
+
272
+ 未知名先过 LEGACY_PHASE_ALIASES 读时映射(阶段改名后历史 change 靠它继续可读),
273
+ 仍然解析不了才降级,并把原因与具体阶段名带在 source 里。
274
+ """
273
275
  policy_path = contract_root / "meta" / "gate-policy.json"
274
276
  if not policy_path.is_file():
275
277
  return None, "legacy"
276
278
  try:
277
279
  policy = _read_json(policy_path)
278
280
  except (OSError, ValueError, json.JSONDecodeError):
279
- return None, "legacy"
281
+ return None, "legacy:policy-unreadable"
282
+ unknown: list[str] = []
280
283
  for field, source in (
281
284
  ("plannedPhases", "change"),
282
285
  ("defaultPhases", "policy-default"),
@@ -285,12 +288,17 @@ def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
285
288
  if not isinstance(raw, list):
286
289
  continue
287
290
  phases = [str(item).strip() for item in raw if str(item).strip()]
288
- if (
289
- phases
290
- and len(phases) == len(set(phases))
291
- and all(item in WORKFLOW_PHASES for item in phases)
292
- ):
293
- return phases, source
291
+ if not phases or len(phases) != len(set(phases)):
292
+ continue
293
+ resolved = [hpaths.resolve_phase_name(item) for item in phases]
294
+ missing = [item for item, hit in zip(phases, resolved) if hit is None]
295
+ if missing:
296
+ unknown.extend(missing)
297
+ continue
298
+ # 映射后可能出现重复(两个旧名合并成同一个新阶段),保序去重。
299
+ return list(dict.fromkeys(str(item) for item in resolved)), source
300
+ if unknown:
301
+ return None, "legacy:unknown-phases=" + ",".join(dict.fromkeys(unknown))
294
302
  return None, "legacy"
295
303
 
296
304
 
@@ -405,6 +413,7 @@ def _paths(state_root: Path) -> dict[str, Path]:
405
413
  "begins": runtime / "transition-begins.ndjson",
406
414
  "current": runtime / "current-context.json",
407
415
  "lease": runtime / "context-lease.json",
416
+ "adoptions": runtime / "context-adoptions.ndjson",
408
417
  }
409
418
 
410
419
 
@@ -604,8 +613,14 @@ def _claim_prepared_context(
604
613
  )
605
614
  except (OSError, ValueError, json.JSONDecodeError):
606
615
  return {"ok": False, "code": "CONTEXT_CURRENT_INVALID"}
616
+ # 跨阶段推进必须有 receipt 证明它合法;同一个阶段换执行者不需要。
617
+ # 以前两者都要 receipt,于是上一阶段崩溃没能正常 close 时,接手的工具
618
+ # 连"继续把这个阶段干完"都做不到。并发保护不受影响:租约未过期而
619
+ # owner 不同,上面 CONTEXT_LEASE_HELD 已经拦下了,能走到这里只剩
620
+ # 「租约已过期」和「租约已被上一阶段 close 删除」两种情况。
621
+ adoption: dict[str, Any] | None = None
607
622
  if recovery is None and isinstance(current, dict) and (
608
- current.get("executor") != executor or current.get("phase") != phase
623
+ current.get("phase") != phase
609
624
  ):
610
625
  valid_receipt = (
611
626
  isinstance(latest, dict)
@@ -621,6 +636,19 @@ def _claim_prepared_context(
621
636
  "toExecutor": executor,
622
637
  "toPhase": phase,
623
638
  }
639
+ executor_changed = (
640
+ isinstance(current, dict) and current.get("executor") != executor
641
+ )
642
+ if recovery is not None or executor_changed:
643
+ adoption = {
644
+ "previousExecutor": (
645
+ current.get("executor") if isinstance(current, dict) else None
646
+ ),
647
+ "newExecutor": executor,
648
+ "reason": (
649
+ "lease_expired" if recovery is not None else "same_phase_handoff"
650
+ ),
651
+ }
624
652
 
625
653
  try:
626
654
  execution_root = _execution_root(project, contract_root, state_root)
@@ -657,6 +685,20 @@ def _claim_prepared_context(
657
685
  current_context["preparationId"] = preparation_id
658
686
  _write_json_atomic(paths["lease"], lease)
659
687
  _write_json_atomic(paths["current"], current_context)
688
+ if adoption is not None:
689
+ # 接管必须留痕。过期租约本来就允许任何执行者顶替,但以前只在返回体
690
+ # 里挂一个 recovery,退出进程就没了——事后查"这个阶段是谁接手的"
691
+ # 无据可依。收据形状照 harness_archive.adopt_existing_range。
692
+ adoption = {
693
+ "schemaVersion": 1,
694
+ "changeName": change,
695
+ "phase": phase,
696
+ **adoption,
697
+ "previousOwner": recovery.get("previousOwner") if recovery else None,
698
+ "expiredAt": recovery.get("expiredAt") if recovery else None,
699
+ "adoptedAt": now.isoformat(),
700
+ }
701
+ _append_ndjson(paths["adoptions"], adoption)
660
702
  return {
661
703
  "ok": True,
662
704
  "lease": lease,
@@ -664,6 +706,7 @@ def _claim_prepared_context(
664
706
  "transitions": transitions,
665
707
  "latestTransition": latest,
666
708
  "recovery": recovery,
709
+ "adoption": adoption,
667
710
  "executionRoot": str(execution_root),
668
711
  }
669
712
 
@@ -405,6 +405,10 @@ def _default_gate_begin(**kwargs: Any) -> dict[str, Any]:
405
405
  str(kwargs["run_id"]),
406
406
  "--note",
407
407
  "开始处理评审中确认需要修改的代码问题。",
408
+ # 显式标记这一轮是 review fixback。以前靠 gate 嗅探 note 里有没有
409
+ # "fixback" 字样——而上面这条 note 一个都没有,于是 fixback 的 run 事件
410
+ # 从来没被打上 trigger/from_phase。
411
+ "--fixback",
408
412
  ]
409
413
  for option, key in (
410
414
  ("--executor-tool", "executor_tool"),