@hunter-harness/workflow-harness 0.2.5 → 0.2.6

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 (202) 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 +5 -2
  3. package/harness/bundles/general/claude-code/harness-archive/reference.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +78 -155
  5. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +4 -2
  6. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +5 -3
  7. package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  8. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +4 -2
  9. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +6 -3
  10. package/harness/bundles/general/claude-code/harness-review/SKILL.md +6 -3
  11. package/harness/bundles/general/claude-code/harness-run/SKILL.md +5 -2
  12. package/harness/bundles/general/claude-code/harness-run/reference.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +5 -2
  14. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +5 -3
  15. package/harness/bundles/general/claude-code/harness-test/SKILL.md +5 -2
  16. package/harness/bundles/general/claude-code/harness-test/reference.md +1 -1
  17. package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +1 -1
  18. package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +11 -4
  19. package/harness/bundles/general/claude-code/scripts/harness_archive.py +190 -32
  20. package/harness/bundles/general/claude-code/scripts/harness_events.py +134 -40
  21. package/harness/bundles/general/claude-code/scripts/harness_preflight.py +46 -3
  22. package/harness/bundles/general/claude-code/scripts/harness_profile.py +2 -2
  23. package/harness/bundles/general/claude-code/scripts/harness_state.py +148 -0
  24. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  25. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +5 -2
  26. package/harness/bundles/general/codebuddy/harness-archive/reference.md +1 -1
  27. package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +78 -155
  28. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +4 -2
  29. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +5 -3
  30. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  31. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +4 -2
  32. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +6 -3
  33. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +6 -3
  34. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +5 -2
  35. package/harness/bundles/general/codebuddy/harness-run/reference.md +1 -1
  36. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +5 -2
  37. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +5 -3
  38. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +5 -2
  39. package/harness/bundles/general/codebuddy/harness-test/reference.md +1 -1
  40. package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +1 -1
  41. package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +11 -4
  42. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +190 -32
  43. package/harness/bundles/general/codebuddy/scripts/harness_events.py +134 -40
  44. package/harness/bundles/general/codebuddy/scripts/harness_preflight.py +46 -3
  45. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +2 -2
  46. package/harness/bundles/general/codebuddy/scripts/harness_state.py +148 -0
  47. package/harness/bundles/general/codex/.harness-build.json +1 -1
  48. package/harness/bundles/general/codex/harness-archive/SKILL.md +5 -2
  49. package/harness/bundles/general/codex/harness-archive/reference.md +1 -1
  50. package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +78 -155
  51. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +4 -2
  52. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +5 -3
  53. package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  54. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +4 -2
  55. package/harness/bundles/general/codex/harness-plan/SKILL.md +6 -3
  56. package/harness/bundles/general/codex/harness-review/SKILL.md +5 -2
  57. package/harness/bundles/general/codex/harness-run/SKILL.md +5 -2
  58. package/harness/bundles/general/codex/harness-run/reference.md +1 -1
  59. package/harness/bundles/general/codex/harness-submit/SKILL.md +5 -2
  60. package/harness/bundles/general/codex/harness-sync/SKILL.md +5 -3
  61. package/harness/bundles/general/codex/harness-test/SKILL.md +5 -2
  62. package/harness/bundles/general/codex/harness-test/reference.md +1 -1
  63. package/harness/bundles/general/codex/protocols/ledger-protocol.md +1 -1
  64. package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +11 -4
  65. package/harness/bundles/general/codex/scripts/harness_archive.py +190 -32
  66. package/harness/bundles/general/codex/scripts/harness_events.py +134 -40
  67. package/harness/bundles/general/codex/scripts/harness_preflight.py +46 -3
  68. package/harness/bundles/general/codex/scripts/harness_profile.py +2 -2
  69. package/harness/bundles/general/codex/scripts/harness_state.py +148 -0
  70. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  71. package/harness/bundles/general/cursor/harness-archive/SKILL.md +5 -2
  72. package/harness/bundles/general/cursor/harness-archive/reference.md +1 -1
  73. package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +78 -155
  74. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +4 -2
  75. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +5 -3
  76. package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  77. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +4 -2
  78. package/harness/bundles/general/cursor/harness-plan/SKILL.md +6 -3
  79. package/harness/bundles/general/cursor/harness-review/SKILL.md +5 -2
  80. package/harness/bundles/general/cursor/harness-run/SKILL.md +5 -2
  81. package/harness/bundles/general/cursor/harness-run/reference.md +1 -1
  82. package/harness/bundles/general/cursor/harness-submit/SKILL.md +5 -2
  83. package/harness/bundles/general/cursor/harness-sync/SKILL.md +5 -3
  84. package/harness/bundles/general/cursor/harness-test/SKILL.md +5 -2
  85. package/harness/bundles/general/cursor/harness-test/reference.md +1 -1
  86. package/harness/bundles/general/cursor/protocols/ledger-protocol.md +1 -1
  87. package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +11 -4
  88. package/harness/bundles/general/cursor/scripts/harness_archive.py +190 -32
  89. package/harness/bundles/general/cursor/scripts/harness_events.py +134 -40
  90. package/harness/bundles/general/cursor/scripts/harness_preflight.py +46 -3
  91. package/harness/bundles/general/cursor/scripts/harness_profile.py +2 -2
  92. package/harness/bundles/general/cursor/scripts/harness_state.py +148 -0
  93. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  94. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +5 -2
  95. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +5 -2
  96. package/harness/bundles/java/claude-code/harness-archive/reference.md +1 -1
  97. package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +78 -155
  98. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +4 -2
  99. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +5 -3
  100. package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  101. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +4 -2
  102. package/harness/bundles/java/claude-code/harness-package/SKILL.md +5 -2
  103. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +6 -3
  104. package/harness/bundles/java/claude-code/harness-review/SKILL.md +6 -3
  105. package/harness/bundles/java/claude-code/harness-run/SKILL.md +5 -2
  106. package/harness/bundles/java/claude-code/harness-run/reference.md +1 -1
  107. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +5 -2
  108. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +5 -3
  109. package/harness/bundles/java/claude-code/harness-test/SKILL.md +5 -2
  110. package/harness/bundles/java/claude-code/harness-test/reference.md +1 -1
  111. package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +1 -1
  112. package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +11 -4
  113. package/harness/bundles/java/claude-code/scripts/harness_archive.py +190 -32
  114. package/harness/bundles/java/claude-code/scripts/harness_events.py +134 -40
  115. package/harness/bundles/java/claude-code/scripts/harness_preflight.py +46 -3
  116. package/harness/bundles/java/claude-code/scripts/harness_profile.py +2 -2
  117. package/harness/bundles/java/claude-code/scripts/harness_state.py +148 -0
  118. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  119. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +5 -2
  120. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +5 -2
  121. package/harness/bundles/java/codebuddy/harness-archive/reference.md +1 -1
  122. package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +78 -155
  123. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +4 -2
  124. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +5 -3
  125. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  126. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +4 -2
  127. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +5 -2
  128. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +6 -3
  129. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +6 -3
  130. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +5 -2
  131. package/harness/bundles/java/codebuddy/harness-run/reference.md +1 -1
  132. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +5 -2
  133. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +5 -3
  134. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +5 -2
  135. package/harness/bundles/java/codebuddy/harness-test/reference.md +1 -1
  136. package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +1 -1
  137. package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +11 -4
  138. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +190 -32
  139. package/harness/bundles/java/codebuddy/scripts/harness_events.py +134 -40
  140. package/harness/bundles/java/codebuddy/scripts/harness_preflight.py +46 -3
  141. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +2 -2
  142. package/harness/bundles/java/codebuddy/scripts/harness_state.py +148 -0
  143. package/harness/bundles/java/codex/.harness-build.json +1 -1
  144. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +5 -2
  145. package/harness/bundles/java/codex/harness-archive/SKILL.md +5 -2
  146. package/harness/bundles/java/codex/harness-archive/reference.md +1 -1
  147. package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +78 -155
  148. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +4 -2
  149. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +5 -3
  150. package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  151. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +4 -2
  152. package/harness/bundles/java/codex/harness-package/SKILL.md +5 -2
  153. package/harness/bundles/java/codex/harness-plan/SKILL.md +6 -3
  154. package/harness/bundles/java/codex/harness-review/SKILL.md +5 -2
  155. package/harness/bundles/java/codex/harness-run/SKILL.md +5 -2
  156. package/harness/bundles/java/codex/harness-run/reference.md +1 -1
  157. package/harness/bundles/java/codex/harness-submit/SKILL.md +5 -2
  158. package/harness/bundles/java/codex/harness-sync/SKILL.md +5 -3
  159. package/harness/bundles/java/codex/harness-test/SKILL.md +5 -2
  160. package/harness/bundles/java/codex/harness-test/reference.md +1 -1
  161. package/harness/bundles/java/codex/protocols/ledger-protocol.md +1 -1
  162. package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +11 -4
  163. package/harness/bundles/java/codex/scripts/harness_archive.py +190 -32
  164. package/harness/bundles/java/codex/scripts/harness_events.py +134 -40
  165. package/harness/bundles/java/codex/scripts/harness_preflight.py +46 -3
  166. package/harness/bundles/java/codex/scripts/harness_profile.py +2 -2
  167. package/harness/bundles/java/codex/scripts/harness_state.py +148 -0
  168. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  169. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +5 -2
  170. package/harness/bundles/java/cursor/harness-archive/SKILL.md +5 -2
  171. package/harness/bundles/java/cursor/harness-archive/reference.md +1 -1
  172. package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +78 -155
  173. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +4 -2
  174. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +5 -3
  175. package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
  176. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +4 -2
  177. package/harness/bundles/java/cursor/harness-package/SKILL.md +5 -2
  178. package/harness/bundles/java/cursor/harness-plan/SKILL.md +6 -3
  179. package/harness/bundles/java/cursor/harness-review/SKILL.md +5 -2
  180. package/harness/bundles/java/cursor/harness-run/SKILL.md +5 -2
  181. package/harness/bundles/java/cursor/harness-run/reference.md +1 -1
  182. package/harness/bundles/java/cursor/harness-submit/SKILL.md +5 -2
  183. package/harness/bundles/java/cursor/harness-sync/SKILL.md +5 -3
  184. package/harness/bundles/java/cursor/harness-test/SKILL.md +5 -2
  185. package/harness/bundles/java/cursor/harness-test/reference.md +1 -1
  186. package/harness/bundles/java/cursor/protocols/ledger-protocol.md +1 -1
  187. package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +11 -4
  188. package/harness/bundles/java/cursor/scripts/harness_archive.py +190 -32
  189. package/harness/bundles/java/cursor/scripts/harness_events.py +134 -40
  190. package/harness/bundles/java/cursor/scripts/harness_preflight.py +46 -3
  191. package/harness/bundles/java/cursor/scripts/harness_profile.py +2 -2
  192. package/harness/bundles/java/cursor/scripts/harness_state.py +148 -0
  193. package/harness/manifests/general/claude-code.json +24 -24
  194. package/harness/manifests/general/codebuddy.json +24 -24
  195. package/harness/manifests/general/codex.json +24 -24
  196. package/harness/manifests/general/cursor.json +24 -24
  197. package/harness/manifests/java/claude-code.json +26 -26
  198. package/harness/manifests/java/codebuddy.json +26 -26
  199. package/harness/manifests/java/codex.json +26 -26
  200. package/harness/manifests/java/cursor.json +26 -26
  201. package/hunter-workflow-family.json +1 -1
  202. package/package.json +1 -1
@@ -3,7 +3,7 @@ name: harness-review
3
3
  description: 6维度代码审查(架构/安全/规范/兼容/测试/性能),对照项目规则(见
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。使用场景:代码审查、提交前检查、合并评审
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a90894841bbf814e; overlay=java; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=9fa8511a55ff544c; overlay=java; agent=codebuddy; do not edit -->
7
7
  # harness-review — 代码审查
8
8
 
9
9
  ## Purpose
@@ -39,6 +39,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
39
39
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
40
40
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
41
41
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
42
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
42
43
 
43
44
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
44
45
 
@@ -49,7 +50,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
49
50
  <!-- @section-id review.delegate -->
50
51
  ### 2. 委派 harness-reviewer
51
52
 
52
- 先运行 `python <skills-root>/scripts/harness_preflight.py check-agents --skills-root <skills-root> --agent harness-reviewer --json`。`usable=false` → **直接主会话审查**,记 `decision` 事件,**不委派**。`usable=true` 时用 Agent spawn `harness-reviewer`(只读, 6 维度)。返回空 / 无报告正文 → **不 retry**,降级主会话审查。
53
+ 先运行 `python <skills-root>/scripts/harness_preflight.py check-agents --skills-root <skills-root> --agent harness-reviewer --json`。`usable=false` → **直接主会话审查**,记 `decision` 事件,**不委派**。`reasonCode=CUSTOM_AGENTS_UNSUPPORTED` 是当前工具没有自定义 agent 能力的正常 inline 路径,不显示缺失告警。`usable=true` 时用 Agent spawn `harness-reviewer`(只读, 6 维度)。返回空 / 无报告正文 → **不 retry**,降级主会话审查。
53
54
 
54
55
  3. **持久化报告(强制,主会话)** — Agent 返回后主会话 Write 到 `reports/review/review-report-*.md`。未 Write → 🟡WARN,不得宣称 review 完成。
55
56
  4. **生成修复反馈(原生协议)** — 若报告存在 RED/YELLOW 问题,执行 `protocols.md` 的 `review-fixback-protocol`,将问题转化为结构化 fixback 清单并落盘到 `.harness/changes/<change-name>/reports/review/fixback-YYYYMMDD-HHmm.md`。若无 RED/YELLOW,记录 `review-fixback-protocol: skipped(no findings)`。不调用 Superpowers `receiving-code-review`,也不记录外部 skill 降级。
@@ -135,7 +136,7 @@ review 读取 `state-snapshot.json`(`harness_state.py` / state-layout-protocol
135
136
 
136
137
  ## 执行日志
137
138
 
138
- `events.ndjson` 为唯一事实源(schema_version 2,`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
139
+ `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
139
140
 
140
141
  ```powershell
141
142
  python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
@@ -144,3 +145,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
144
145
  > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
145
146
 
146
147
  > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
148
+
149
+ 每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
@@ -2,7 +2,7 @@
2
2
  name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。使用场景:开始编码、实现功能、写代码、TDD 编码
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=a90894841bbf814e; overlay=java; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=9fa8511a55ff544c; overlay=java; agent=codebuddy; do not edit -->
6
6
  # harness-run — 需求编码
7
7
 
8
8
  ## Purpose
@@ -44,6 +44,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
44
44
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
45
45
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
46
46
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
47
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
47
48
 
48
49
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
49
50
 
@@ -132,7 +133,7 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
132
133
 
133
134
  ## 执行日志
134
135
 
135
- `events.ndjson` 为唯一事实源(schema_version 2,`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
136
+ `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
136
137
 
137
138
  ```powershell
138
139
  python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
@@ -141,3 +142,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
141
142
  > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
142
143
 
143
144
  > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
145
+
146
+ 每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
@@ -792,7 +792,7 @@ powershell.exe -Command "git -C '<project-path>' diff --check"
792
792
 
793
793
  ## 执行日志记录
794
794
 
795
- `/harness-run` 只向 `events.ndjson` 追加事件(schema_version 2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。步骤 0 之前 append `phase.start`;各阶段写入 `command` / `verification` / `decision` / `issue`,人类可读摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-run/SKILL.md`。
795
+ `/harness-run` 只向 `events.ndjson` 追加事件(schema_version 3,兼容读取 v1/v2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。步骤 0 之前 append `phase.start`;各阶段写入 `command` / `verification` / `decision` / `issue`,人类可读摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-run/SKILL.md`。
796
796
 
797
797
  ## verification-ledger 可复用判定
798
798
 
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff
4
4
  合并回主分支。使用场景:提交代码、commit、push、合并分支、merge to main、完成开发
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a90894841bbf814e; overlay=java; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=9fa8511a55ff544c; overlay=java; agent=codebuddy; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose
@@ -40,6 +40,7 @@ description: 最终提交封装:验证→中文 commit→提交/推送;workt
40
40
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
41
41
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
42
42
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
43
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
43
44
 
44
45
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
45
46
 
@@ -140,7 +141,7 @@ git 经 PowerShell;commit/报告不得含明文密钥。遵循 `../protocols/s
140
141
 
141
142
  ## 执行日志
142
143
 
143
- `events.ndjson` 为唯一事实源(schema_version 2,`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
144
+ `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
144
145
 
145
146
  ```powershell
146
147
  python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
@@ -149,3 +150,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
149
150
  > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
150
151
 
151
152
  > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
153
+
154
+ 每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
@@ -2,7 +2,7 @@
2
2
  name: harness-sync
3
3
  description: 检查并更新项目AI元数据(CodeGraph索引、harness-codebase-map分析、CLAUDE.md、AGENTS.md、.harness/完整性),确保Claude对代码库的理解与最新代码一致。当用户说'同步/更新索引/刷新元数据/检查一致性'时使用
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=a90894841bbf814e; overlay=java; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=9fa8511a55ff544c; overlay=java; agent=codebuddy; do not edit -->
6
6
  # harness-sync — 元数据同步
7
7
 
8
8
  ## Purpose
@@ -22,7 +22,7 @@ description: 检查并更新项目AI元数据(CodeGraph索引、harness-codeba
22
22
  | 2 | CodeGraph 索引是否需重建 |
23
23
  | 3 | `.harness/codebase/map/` 是否过期 → 报告建议 `/harness-codebase-map` |
24
24
  | 3.5 | `harness_knowledge.py sync`;可 `sync --update`;失败不得假装可用。**知识闭环主入口**是 `/harness-knowledge-ingest auto`(含 Agent judge),sync 不重复列人工知识待办 |
25
- | 3.6 | 扫描 `.harness/knowledge/maintenance-outbox/{pending,failed}`;对每项运行 `harness_knowledge.py maintain --project . --archive-id <id> --json`(§8.2:archive close 只 enqueue,sync 异步推进 outbox 到 completed/completed_rules_pending_judge) |
25
+ | 3.6 | 单次运行 `harness_knowledge.py maintain --project . --drain --json`,有界推进全部 `.harness/knowledge/maintenance-outbox/{pending,failed}`;不得为每个条目重复启动 Python/重建索引(§8.2:archive close 只 enqueue,sync 异步推进 outbox 到 completed/pending-judge) |
26
26
  | 4 | CLAUDE.md 完整性/行数 → 超限 AskUserQuestion 瘦身 |
27
27
  | 5 | AGENTS.md 与 CLAUDE.md 一致 |
28
28
  | 6 | `.harness/` 结构(init 规程 → `reference.md` 第 6 步);可选 `harness_deploy.py diff` 检查已装 skill 是否过期 |
@@ -54,7 +54,7 @@ description: 检查并更新项目AI元数据(CodeGraph索引、harness-codeba
54
54
 
55
55
  ## 执行日志
56
56
 
57
- `events.ndjson` 为唯一事实源(schema_version 2,`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
57
+ `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
58
58
 
59
59
  ```powershell
60
60
  python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
@@ -63,3 +63,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
63
63
  > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
64
64
 
65
65
  > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
66
+
67
+ 每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
@@ -2,7 +2,7 @@
2
2
  name: harness-test
3
3
  description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。当用户说'跑测试/验证/跑用例/接口测试/单元测试'时使用
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=a90894841bbf814e; overlay=java; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=9fa8511a55ff544c; overlay=java; agent=codebuddy; do not edit -->
6
6
  # harness-test — 测试执行
7
7
 
8
8
  ## Purpose
@@ -36,6 +36,7 @@ description: 测试执行:读取场景表,执行单元测试+API接口测试
36
36
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
37
37
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
38
38
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
39
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
39
40
 
40
41
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
41
42
 
@@ -175,7 +176,7 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
175
176
 
176
177
  ## 执行日志
177
178
 
178
- `events.ndjson` 为唯一事实源(schema_version 2,`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
179
+ `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
179
180
 
180
181
  ```powershell
181
182
  python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
@@ -184,3 +185,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
184
185
  > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
185
186
 
186
187
  > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
188
+
189
+ 每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
@@ -787,4 +787,4 @@ powershell.exe -NoProfile -Command "$base = '<baseCommit>'; $patch = '.harness/c
787
787
 
788
788
  ## 执行日志记录
789
789
 
790
- `/harness-test` 只向 `events.ndjson` 追加事件(schema_version 2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。Phase 0 之前 append `phase.start`;写入 `command` / `verification` / `decision` / `issue` / `artifact`,摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-test/SKILL.md`。
790
+ `/harness-test` 只向 `events.ndjson` 追加事件(schema_version 3,兼容读取 v1/v2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。Phase 0 之前 append `phase.start`;写入 `command` / `verification` / `decision` / `issue` / `artifact`,摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-test/SKILL.md`。
@@ -86,7 +86,7 @@ verification-ledger 把每次验证(compile / unit test / api test / package
86
86
  },
87
87
  "package": {
88
88
  "status": "OK",
89
- "command": "<打包命令(按技术栈:Java=mvn package -pl <module> -am -DskipTests;前端=npm run build;Python=python -m build)>",
89
+ "command": "<打包命令(按技术栈:Java=mvn clean package -pl <module> -am -DskipTests;前端=npm run build;Python=python -m build)>",
90
90
  "baseCommit": "<final pushed hash>",
91
91
  "deployArtifact": "<构建产物路径(Java=<module>/target/<artifact>.jar;前端=<module>/dist/;Python=<module>/dist/)>",
92
92
  "sha256": "<artifact sha256>",
@@ -43,11 +43,11 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
43
43
 
44
44
  `append` 写入契约(Task 4 §6.1):普通 append = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex`);仅 `--type phase.end` append 成功后渲染一次 `logs/execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。人类可读摘要(触发指令、降级原因、阶段结论)写入事件的 `note` 字段;阶段开始/结束、命令、验证、artifact、问题、决策各写对应 `type` 事件。旧 archive 缺少 events 可回放兼容;新变更不得以 execution-log 已存在为理由跳过 events。
45
45
 
46
- 基础事件结构(schema_version 2):
46
+ 基础事件结构(schema_version 3;兼容读取 v1/v2):
47
47
 
48
48
  ```json
49
49
  {
50
- "schema_version": 2,
50
+ "schema_version": 3,
51
51
  "id": "evt-...",
52
52
  "timestamp": "2026-07-02T00:00:00.000Z",
53
53
  "phase": "run",
@@ -55,6 +55,13 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
55
55
  "command": "npm test",
56
56
  "exit_code": 0,
57
57
  "duration_ms": 42100,
58
+ "run_id": "run-...",
59
+ "attempt": 1,
60
+ "executor_tool": "codex",
61
+ "executor_agent": "main",
62
+ "executor_model": "gpt-5",
63
+ "handoff_from_tool": "claude-code",
64
+ "handoff_reason": "continue implementation",
58
65
  "note": "可选:触发指令、降级原因、阶段摘要"
59
66
  }
60
67
  ```
@@ -64,7 +71,7 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
64
71
  | type | 必填/常用字段 | 用途 |
65
72
  |---|---|---|
66
73
  | `phase.start` | phase, timestamp | 阶段开始 |
67
- | `phase.end` | phase, timestamp | 阶段结束,计算耗时 |
74
+ | `phase.end` | phase, timestamp, status | 阶段结束并记录 `OK/WARN/FAIL/BLOCKED`;计算当前 attempt 耗时 |
68
75
  | `command` | command, exit_code, duration_ms | 命令事实 |
69
76
  | `verification` | name, status, command | 验证事实 |
70
77
  | `artifact` | path, kind | 报告、包、manifest 等产物 |
@@ -79,7 +86,7 @@ summary-data 必须保留原 archive final report 维度,并增加事件层摘
79
86
  {
80
87
  "schemaVersion": "2.2",
81
88
  "reportPipeline": {
82
- "schema_version": 2,
89
+ "schema_version": 1,
83
90
  "generated_at": "2026-07-02T00:00:00.000Z",
84
91
  "event_count": 0,
85
92
  "sources": ["events.ndjson", "evidence/verification-ledger.json"],
@@ -622,13 +622,16 @@ def _ledger_unit_tests(ledger: dict[str, Any] | None) -> dict[str, Any]:
622
622
  status = str(unit.get("status") or "").upper()
623
623
  evidence = unit.get("evidence") or {}
624
624
  if isinstance(evidence, dict):
625
- run = evidence.get("run", unit.get("run", 0))
625
+ run = evidence.get(
626
+ "run",
627
+ evidence.get("testsRun", unit.get("run", unit.get("testsRun", 0))),
628
+ )
626
629
  failures = evidence.get("failures", unit.get("failures", 0))
627
630
  errors = evidence.get("errors", unit.get("errors", 0))
628
631
  skipped = evidence.get("skipped", unit.get("skipped", 0))
629
632
  pass_rate = evidence.get("passRate") or unit.get("passRate")
630
633
  else:
631
- run = unit.get("run", 0)
634
+ run = unit.get("run", unit.get("testsRun", 0))
632
635
  failures = unit.get("failures", 0)
633
636
  errors = unit.get("errors", 0)
634
637
  skipped = unit.get("skipped", 0)
@@ -886,16 +889,21 @@ def _durations_from_event_phases(event_summary: dict[str, Any]) -> dict[str, Any
886
889
  minutes = round((dur or 0) / 60000, 2) if dur is not None else 0
887
890
  if dur:
888
891
  total_ms += int(dur)
889
- stages.append(
890
- {
891
- "stage": str(name),
892
- "skill": f"harness-{name}",
893
- "startedAt": info.get("started_at") or NOT_AVAILABLE,
894
- "endedAt": info.get("ended_at") or NOT_AVAILABLE,
895
- "minutes": minutes,
896
- "result": "OK",
897
- }
898
- )
892
+ attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
893
+ result = str(info.get("status") or "UNKNOWN").upper()
894
+ if result in {"PASS", "PASSED", "SUCCESS"}:
895
+ result = "OK"
896
+ elif result in {"FAILED", "ERROR"}:
897
+ result = "FAIL"
898
+ stages.append({
899
+ "stage": str(name),
900
+ "skill": f"harness-{name}",
901
+ "startedAt": info.get("started_at") or NOT_AVAILABLE,
902
+ "endedAt": info.get("ended_at") or NOT_AVAILABLE,
903
+ "minutes": minutes,
904
+ "result": result,
905
+ "attempts": attempts,
906
+ })
899
907
  total_min = round(total_ms / 60000, 2)
900
908
  return {
901
909
  "totalLabel": f"约 {int(round(total_min))} 分",
@@ -917,11 +925,31 @@ def _stage_status_from_sources(
917
925
  "submit": "OK",
918
926
  "archive": "OK",
919
927
  }
928
+ for event in events:
929
+ if event.get("type") != "phase.end":
930
+ continue
931
+ phase = str(event.get("phase") or "").lower()
932
+ raw = str(event.get("status") or "").upper()
933
+ if phase not in status or not raw:
934
+ continue
935
+ if raw in {"PASS", "PASSED", "SUCCESS"}:
936
+ raw = "OK"
937
+ elif raw in {"FAILED", "ERROR"}:
938
+ raw = "FAIL"
939
+ elif raw in {"SKIP", "SKIPPED"}:
940
+ raw = "USER_SKIPPED"
941
+ status[phase] = raw
920
942
  # Issues in events can downgrade
921
943
  for e in events:
922
944
  if e.get("type") != "issue":
923
945
  continue
924
946
  sev = str(e.get("severity") or "").lower()
947
+ issue_text = " ".join(str(e.get(key) or "") for key in ("message", "note", "code")).lower()
948
+ if not sev:
949
+ if any(token in issue_text for token in ("fail", "error", "blocked", "失败", "阻塞")):
950
+ sev = "error"
951
+ elif any(token in issue_text for token in ("warn", "skip", "风险", "警告")):
952
+ sev = "warn"
925
953
  phase = str(e.get("phase") or "").lower()
926
954
  if phase in status and sev in {"error", "fail", "failed", "critical"}:
927
955
  status[phase] = "FAIL"
@@ -930,14 +958,19 @@ def _stage_status_from_sources(
930
958
 
931
959
  api = _ledger_api_tests(ledger)
932
960
  db = _ledger_db_compat(ledger)
933
- if api.get("status") == "USER_SKIPPED":
961
+ api_status = str(api.get("status") or "").upper()
962
+ if api_status in {"FAIL", "FAILED", "ERROR"} or int(api.get("failed") or 0) > 0:
963
+ status["test"] = "FAIL"
964
+ elif api_status in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA"}:
965
+ status["test"] = api_status
966
+ elif api_status == "USER_SKIPPED":
934
967
  status["test"] = "USER_SKIPPED"
935
968
  elif db == "BLOCKED_BY_DBA":
936
969
  status["test"] = "BLOCKED_BY_DBA"
937
970
  elif api.get("status") == "NOT_RUN" and not find_test_reports(change_dir):
938
971
  unit = _ledger_unit_tests(ledger)
939
972
  if unit.get("source") == "not-run" and unit.get("run", 0) == 0:
940
- status["test"] = "USER_SKIPPED"
973
+ status["test"] = "NOT_RUN"
941
974
 
942
975
  if not find_review_reports(change_dir):
943
976
  log_text = load_execution_log(change_dir)
@@ -954,8 +987,6 @@ def _compute_final_status(
954
987
  api = verification.get("apiTests") or {}
955
988
  db = str(verification.get("dbCompatibility") or "")
956
989
  api_status = str(api.get("status") or "")
957
- if api_status == "USER_SKIPPED" or db == "BLOCKED_BY_DBA":
958
- return "CONDITIONAL_OK"
959
990
  for v in stage_status.values():
960
991
  if v == "FAIL":
961
992
  return "FAIL"
@@ -964,8 +995,14 @@ def _compute_final_status(
964
995
  return "FAIL"
965
996
  if int(api.get("failed") or 0) > 0:
966
997
  return "FAIL"
998
+ conditional = {
999
+ "USER_SKIPPED", "BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA",
1000
+ "NOT_RUN", "PARTIAL",
1001
+ }
1002
+ if api_status in conditional or db in conditional:
1003
+ return "CONDITIONAL_OK"
967
1004
  for v in stage_status.values():
968
- if v in {"WARN", "PARTIAL", "USER_SKIPPED", "BLOCKED_BY_DBA"}:
1005
+ if v in {"WARN", *conditional}:
969
1006
  return "WARN" if v == "WARN" else "CONDITIONAL_OK"
970
1007
  return "OK"
971
1008
 
@@ -984,6 +1021,8 @@ def _changed_files_from_git(
984
1021
  changed: list[dict[str, Any]] = []
985
1022
  if not base or not head:
986
1023
  return diff_stat, changed
1024
+ # The ledger bounds the complete task. Using only ``head^..head`` would
1025
+ # silently omit earlier checkpoint commits from the same change.
987
1026
  rng = f"{base}..{head}"
988
1027
  code, out, _ = git_run(project, "diff", "--numstat", rng)
989
1028
  if code != 0 or not out:
@@ -1040,6 +1079,97 @@ def _review_summary(change_dir: Path, existing: dict[str, Any] | None) -> dict[s
1040
1079
  return base
1041
1080
 
1042
1081
 
1082
+ def _final_commit_from_sources(
1083
+ ledger: dict[str, Any] | None,
1084
+ events: list[dict[str, Any]],
1085
+ existing: dict[str, Any] | None,
1086
+ project: Path,
1087
+ ) -> str:
1088
+ if ledger:
1089
+ for key in (
1090
+ "mergeFinalHash", "finalCommit", "finalHash", "changeCommit", "headCommit",
1091
+ ):
1092
+ value = str(ledger.get(key) or "").strip()
1093
+ if value:
1094
+ return value
1095
+ commit_pattern = re.compile(r"\b[0-9a-f]{7,40}\b", re.IGNORECASE)
1096
+ for event in reversed(events):
1097
+ if str(event.get("phase") or "").lower() not in {"submit", "merge", "archive"}:
1098
+ continue
1099
+ text = " ".join(str(event.get(key) or "") for key in ("note", "message", "command"))
1100
+ match = commit_pattern.search(text)
1101
+ if match:
1102
+ return match.group(0)
1103
+ if existing and existing.get("finalCommit"):
1104
+ return str(existing["finalCommit"])
1105
+ code, head, _ = git_run(project, "rev-parse", "HEAD")
1106
+ return head if code == 0 and head else ""
1107
+
1108
+
1109
+ def _business_goal_from_sources(change_dir: Path, events: list[dict[str, Any]]) -> str:
1110
+ plans_root = change_dir / "plans"
1111
+ primary = sorted(plans_root.glob("*-plan.md"))
1112
+ secondary = [
1113
+ path for path in sorted(plans_root.glob("*.md"))
1114
+ if path not in primary
1115
+ and "implementation-detail" not in path.name
1116
+ and "test-scenarios" not in path.name
1117
+ ]
1118
+ for plan in [*primary, *secondary]:
1119
+ try:
1120
+ text = plan.read_text(encoding="utf-8-sig")
1121
+ except OSError:
1122
+ continue
1123
+ body = re.sub(r"\A---\s*\n.*?\n---\s*\n", "", text, count=1, flags=re.DOTALL)
1124
+ goal = re.search(r"(?im)^\s*(?:goal|目标|业务目标|需求)\s*[::]\s*(.+)$", body)
1125
+ if goal:
1126
+ return goal.group(1).strip()
1127
+ scope = re.search(r"(?im)^\s*>?\s*(?:变更范围|目标)\s*[::]\s*(.+)$", body)
1128
+ if scope:
1129
+ return scope.group(1).strip()
1130
+ first_task = re.search(r"(?m)^\s*\|\s*1\s*\|\s*([^|]+?)\s*\|", body)
1131
+ if first_task:
1132
+ return first_task.group(1).strip()
1133
+ for line in body.splitlines():
1134
+ clean = line.strip().lstrip("#").strip()
1135
+ if clean and not clean.startswith(("---", ">", "|")) and len(clean) > 8:
1136
+ return clean
1137
+ for event in events:
1138
+ if event.get("type") == "decision":
1139
+ value = str(event.get("decision") or event.get("note") or "").strip()
1140
+ if value:
1141
+ return re.sub(r"^(?:需求收敛|目标)\s*[::]\s*", "", value)
1142
+ return ""
1143
+
1144
+
1145
+ def _timeline_from_events(event_summary: dict[str, Any], events: list[dict[str, Any]]) -> list[dict[str, Any]]:
1146
+ timeline: list[dict[str, Any]] = []
1147
+ for phase, info in (event_summary.get("phases") or {}).items():
1148
+ attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
1149
+ for attempt in attempts:
1150
+ timeline.append({
1151
+ "phase": phase,
1152
+ "attempt": attempt.get("attempt"),
1153
+ "startedAt": attempt.get("started_at"),
1154
+ "endedAt": attempt.get("ended_at"),
1155
+ "durationMs": attempt.get("duration_ms"),
1156
+ "status": attempt.get("status") or "UNKNOWN",
1157
+ "executorTool": attempt.get("executor_tool"),
1158
+ "executorAgent": attempt.get("executor_agent"),
1159
+ "handoffFromTool": attempt.get("handoff_from_tool"),
1160
+ })
1161
+ for event in events:
1162
+ if event.get("type") not in {"decision", "issue"}:
1163
+ continue
1164
+ timeline.append({
1165
+ "phase": event.get("phase"),
1166
+ "timestamp": event.get("timestamp"),
1167
+ "type": event.get("type"),
1168
+ "summary": event.get("decision") or event.get("message") or event.get("note") or "",
1169
+ })
1170
+ return timeline
1171
+
1172
+
1043
1173
  def collect_summary_data(
1044
1174
  change_dir: Path,
1045
1175
  *,
@@ -1118,18 +1248,14 @@ def collect_summary_data(
1118
1248
  if existing and existing.get("businessGoal"):
1119
1249
  data["businessGoal"] = existing["businessGoal"]
1120
1250
  else:
1121
- data["businessGoal"] = NOT_AVAILABLE if for_replay else ""
1251
+ inferred_goal = _business_goal_from_sources(change_dir, events)
1252
+ data["businessGoal"] = inferred_goal or (NOT_AVAILABLE if for_replay else "")
1122
1253
 
1123
1254
  # commits
1124
1255
  project = find_project_root(change_dir)
1125
1256
  if not data.get("finalCommit") or str(data.get("finalCommit")).startswith("<"):
1126
- code, head, _ = git_run(project, "rev-parse", "HEAD")
1127
- if code == 0 and head:
1128
- data["finalCommit"] = head
1129
- elif existing and existing.get("finalCommit"):
1130
- data["finalCommit"] = existing["finalCommit"]
1131
- else:
1132
- data["finalCommit"] = NOT_AVAILABLE if for_replay else ""
1257
+ final_commit = _final_commit_from_sources(ledger, events, existing, project)
1258
+ data["finalCommit"] = final_commit or (NOT_AVAILABLE if for_replay else "")
1133
1259
 
1134
1260
  if not data.get("baseCommit") or str(data.get("baseCommit")).startswith("<"):
1135
1261
  if ledger and ledger.get("baseCommit"):
@@ -1235,18 +1361,43 @@ def collect_summary_data(
1235
1361
  if not isinstance(data.get("artifacts"), list):
1236
1362
  data["artifacts"] = []
1237
1363
  if not for_replay:
1238
- # Keep empty placeholder for build artifacts; event artifacts go to reportPipeline
1239
- pass
1364
+ data["artifacts"] = _artifacts_from_events(events)
1240
1365
 
1241
1366
  data["reviewSummary"] = _review_summary(change_dir, existing if for_replay else None)
1242
- data.setdefault("timeline", [])
1367
+ if not for_replay:
1368
+ data["timeline"] = _timeline_from_events(event_summary, events)
1369
+ else:
1370
+ data.setdefault("timeline", [])
1243
1371
  data.setdefault("uncommittedTestEvidence", [])
1244
1372
 
1245
- # Model-owned fields: leave empty placeholders on finalize; preserve on replay
1373
+ # Derive risks/actions from evidence. These fields are facts, not model prose.
1246
1374
  if not for_replay:
1247
- data["maintenanceNotes"] = []
1248
- data["knownRisks"] = []
1375
+ data["maintenanceNotes"] = [
1376
+ str(event.get("note") or event.get("message") or "")
1377
+ for event in events
1378
+ if event.get("type") == "decision" and (event.get("note") or event.get("message"))
1379
+ ]
1380
+ data["knownRisks"] = [
1381
+ {
1382
+ "phase": event.get("phase"),
1383
+ "severity": event.get("severity") or "unknown",
1384
+ "message": event.get("message") or event.get("note") or event.get("code") or "",
1385
+ }
1386
+ for event in events
1387
+ if event.get("type") == "issue"
1388
+ and not (
1389
+ event.get("phase") == "archive"
1390
+ and event.get("code") == "missing-command"
1391
+ )
1392
+ ]
1249
1393
  data["manualActions"] = []
1394
+ for name, value in (data.get("stageStatus") or {}).items():
1395
+ if value in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA", "NOT_RUN", "USER_SKIPPED"}:
1396
+ data["manualActions"].append({
1397
+ "stage": name,
1398
+ "status": value,
1399
+ "action": "补充或确认该阶段的真实验证证据",
1400
+ })
1250
1401
  else:
1251
1402
  data.setdefault("maintenanceNotes", [])
1252
1403
  data.setdefault("knownRisks", [])
@@ -1978,6 +2129,7 @@ def cmd_finalize(
1978
2129
  _restore_target,
1979
2130
  phase="archive",
1980
2131
  type_="phase.end",
2132
+ status="FAIL",
1981
2133
  note="finalize aborted; render failed; original preserved",
1982
2134
  )
1983
2135
  except OSError:
@@ -2117,6 +2269,7 @@ def cmd_finalize(
2117
2269
  _safe_append_restored,
2118
2270
  phase="archive",
2119
2271
  type_="phase.end",
2272
+ status="FAIL",
2120
2273
  note="finalize aborted; original preserved",
2121
2274
  )
2122
2275
  except OSError:
@@ -2168,7 +2321,12 @@ def cmd_finalize(
2168
2321
  if service_result.get("warning"):
2169
2322
  warnings.append(str(service_result["warning"]))
2170
2323
 
2171
- _safe_append(phase="archive", type_="phase.end", note="finalize complete")
2324
+ _safe_append(
2325
+ phase="archive",
2326
+ type_="phase.end",
2327
+ status="WARN" if warnings else "OK",
2328
+ note="finalize complete",
2329
+ )
2172
2330
 
2173
2331
  payload["ok"] = True
2174
2332
  payload["warnings"] = warnings