@hunter-harness/workflow-harness 0.2.59 → 0.2.61

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 (282) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/README.md +14 -16
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +43 -46
  4. package/harness/bundles/general/claude-code/harness-archive/checklist.md +10 -10
  5. package/harness/bundles/general/claude-code/harness-archive/reference.md +29 -22
  6. package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +1 -7
  7. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  10. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +4 -4
  11. package/harness/bundles/general/claude-code/harness-review/SKILL.md +2 -2
  12. package/harness/bundles/general/claude-code/harness-run/SKILL.md +5 -3
  13. package/harness/bundles/general/claude-code/harness-run/checklist.md +10 -9
  14. package/harness/bundles/general/claude-code/harness-run/protocols.md +2 -0
  15. package/harness/bundles/general/claude-code/harness-run/reference.md +19 -16
  16. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
  17. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  18. package/harness/bundles/general/claude-code/harness-test/SKILL.md +2 -2
  19. package/harness/bundles/general/claude-code/harness-test/checklist.md +2 -2
  20. package/harness/bundles/general/claude-code/harness-test/pitfalls.md +3 -3
  21. package/harness/bundles/general/claude-code/harness-test/reference.md +2 -2
  22. package/harness/bundles/general/claude-code/protocols/archive-report-protocol.md +18 -25
  23. package/harness/bundles/general/claude-code/protocols/evidence-based-reporting-protocol.md +5 -5
  24. package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +7 -7
  25. package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +10 -9
  26. package/harness/bundles/general/claude-code/protocols/sensitive-info-protocol.md +3 -3
  27. package/harness/bundles/general/claude-code/protocols/state-layout-protocol.md +5 -6
  28. package/harness/bundles/general/claude-code/scripts/harness_archive.py +1043 -1108
  29. package/harness/bundles/general/claude-code/scripts/harness_context.py +8 -2
  30. package/harness/bundles/general/claude-code/scripts/harness_events.py +5 -0
  31. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +79 -2
  32. package/harness/bundles/general/claude-code/scripts/harness_profile.py +7 -0
  33. package/harness/bundles/general/claude-code/scripts/harness_state.py +22 -3
  34. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  35. package/harness/bundles/general/codebuddy/README.md +14 -16
  36. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +43 -46
  37. package/harness/bundles/general/codebuddy/harness-archive/checklist.md +10 -10
  38. package/harness/bundles/general/codebuddy/harness-archive/reference.md +29 -22
  39. package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +1 -7
  40. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +4 -4
  44. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +2 -2
  45. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +5 -3
  46. package/harness/bundles/general/codebuddy/harness-run/checklist.md +10 -9
  47. package/harness/bundles/general/codebuddy/harness-run/protocols.md +2 -0
  48. package/harness/bundles/general/codebuddy/harness-run/reference.md +19 -16
  49. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
  50. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  51. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +2 -2
  52. package/harness/bundles/general/codebuddy/harness-test/checklist.md +2 -2
  53. package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +3 -3
  54. package/harness/bundles/general/codebuddy/harness-test/reference.md +2 -2
  55. package/harness/bundles/general/codebuddy/protocols/archive-report-protocol.md +18 -25
  56. package/harness/bundles/general/codebuddy/protocols/evidence-based-reporting-protocol.md +5 -5
  57. package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +7 -7
  58. package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +10 -9
  59. package/harness/bundles/general/codebuddy/protocols/sensitive-info-protocol.md +3 -3
  60. package/harness/bundles/general/codebuddy/protocols/state-layout-protocol.md +5 -6
  61. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +1043 -1108
  62. package/harness/bundles/general/codebuddy/scripts/harness_context.py +8 -2
  63. package/harness/bundles/general/codebuddy/scripts/harness_events.py +5 -0
  64. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +79 -2
  65. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +7 -0
  66. package/harness/bundles/general/codebuddy/scripts/harness_state.py +22 -3
  67. package/harness/bundles/general/codex/.harness-build.json +1 -1
  68. package/harness/bundles/general/codex/README.md +14 -16
  69. package/harness/bundles/general/codex/harness-archive/SKILL.md +43 -46
  70. package/harness/bundles/general/codex/harness-archive/checklist.md +10 -10
  71. package/harness/bundles/general/codex/harness-archive/reference.md +29 -22
  72. package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +1 -7
  73. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  74. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  75. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  76. package/harness/bundles/general/codex/harness-plan/SKILL.md +4 -4
  77. package/harness/bundles/general/codex/harness-review/SKILL.md +2 -2
  78. package/harness/bundles/general/codex/harness-run/SKILL.md +5 -3
  79. package/harness/bundles/general/codex/harness-run/checklist.md +10 -9
  80. package/harness/bundles/general/codex/harness-run/protocols.md +2 -0
  81. package/harness/bundles/general/codex/harness-run/reference.md +19 -16
  82. package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
  83. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  84. package/harness/bundles/general/codex/harness-test/SKILL.md +2 -2
  85. package/harness/bundles/general/codex/harness-test/checklist.md +2 -2
  86. package/harness/bundles/general/codex/harness-test/pitfalls.md +3 -3
  87. package/harness/bundles/general/codex/harness-test/reference.md +2 -2
  88. package/harness/bundles/general/codex/protocols/archive-report-protocol.md +18 -25
  89. package/harness/bundles/general/codex/protocols/evidence-based-reporting-protocol.md +5 -5
  90. package/harness/bundles/general/codex/protocols/ledger-protocol.md +7 -7
  91. package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +10 -9
  92. package/harness/bundles/general/codex/protocols/sensitive-info-protocol.md +3 -3
  93. package/harness/bundles/general/codex/protocols/state-layout-protocol.md +5 -6
  94. package/harness/bundles/general/codex/scripts/harness_archive.py +1043 -1108
  95. package/harness/bundles/general/codex/scripts/harness_context.py +8 -2
  96. package/harness/bundles/general/codex/scripts/harness_events.py +5 -0
  97. package/harness/bundles/general/codex/scripts/harness_ledger.py +79 -2
  98. package/harness/bundles/general/codex/scripts/harness_profile.py +7 -0
  99. package/harness/bundles/general/codex/scripts/harness_state.py +22 -3
  100. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  101. package/harness/bundles/general/cursor/README.md +14 -16
  102. package/harness/bundles/general/cursor/harness-archive/SKILL.md +43 -46
  103. package/harness/bundles/general/cursor/harness-archive/checklist.md +10 -10
  104. package/harness/bundles/general/cursor/harness-archive/reference.md +29 -22
  105. package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +1 -7
  106. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  107. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  108. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  109. package/harness/bundles/general/cursor/harness-plan/SKILL.md +4 -4
  110. package/harness/bundles/general/cursor/harness-review/SKILL.md +2 -2
  111. package/harness/bundles/general/cursor/harness-run/SKILL.md +5 -3
  112. package/harness/bundles/general/cursor/harness-run/checklist.md +10 -9
  113. package/harness/bundles/general/cursor/harness-run/protocols.md +2 -0
  114. package/harness/bundles/general/cursor/harness-run/reference.md +19 -16
  115. package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
  116. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  117. package/harness/bundles/general/cursor/harness-test/SKILL.md +2 -2
  118. package/harness/bundles/general/cursor/harness-test/checklist.md +2 -2
  119. package/harness/bundles/general/cursor/harness-test/pitfalls.md +3 -3
  120. package/harness/bundles/general/cursor/harness-test/reference.md +2 -2
  121. package/harness/bundles/general/cursor/protocols/archive-report-protocol.md +18 -25
  122. package/harness/bundles/general/cursor/protocols/evidence-based-reporting-protocol.md +5 -5
  123. package/harness/bundles/general/cursor/protocols/ledger-protocol.md +7 -7
  124. package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +10 -9
  125. package/harness/bundles/general/cursor/protocols/sensitive-info-protocol.md +3 -3
  126. package/harness/bundles/general/cursor/protocols/state-layout-protocol.md +5 -6
  127. package/harness/bundles/general/cursor/scripts/harness_archive.py +1043 -1108
  128. package/harness/bundles/general/cursor/scripts/harness_context.py +8 -2
  129. package/harness/bundles/general/cursor/scripts/harness_events.py +5 -0
  130. package/harness/bundles/general/cursor/scripts/harness_ledger.py +79 -2
  131. package/harness/bundles/general/cursor/scripts/harness_profile.py +7 -0
  132. package/harness/bundles/general/cursor/scripts/harness_state.py +22 -3
  133. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  134. package/harness/bundles/java/claude-code/README.md +14 -16
  135. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +2 -2
  136. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +43 -46
  137. package/harness/bundles/java/claude-code/harness-archive/checklist.md +10 -10
  138. package/harness/bundles/java/claude-code/harness-archive/reference.md +29 -22
  139. package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +1 -7
  140. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  141. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  142. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  143. package/harness/bundles/java/claude-code/harness-package/SKILL.md +2 -2
  144. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +4 -4
  145. package/harness/bundles/java/claude-code/harness-review/SKILL.md +2 -2
  146. package/harness/bundles/java/claude-code/harness-run/SKILL.md +5 -3
  147. package/harness/bundles/java/claude-code/harness-run/protocols.md +2 -0
  148. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
  149. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  150. package/harness/bundles/java/claude-code/harness-test/SKILL.md +2 -2
  151. package/harness/bundles/java/claude-code/harness-test/checklist.md +2 -2
  152. package/harness/bundles/java/claude-code/harness-test/pitfalls.md +3 -3
  153. package/harness/bundles/java/claude-code/harness-test/reference.md +2 -2
  154. package/harness/bundles/java/claude-code/protocols/archive-report-protocol.md +18 -25
  155. package/harness/bundles/java/claude-code/protocols/evidence-based-reporting-protocol.md +5 -5
  156. package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +7 -7
  157. package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +10 -9
  158. package/harness/bundles/java/claude-code/protocols/sensitive-info-protocol.md +3 -3
  159. package/harness/bundles/java/claude-code/protocols/state-layout-protocol.md +5 -6
  160. package/harness/bundles/java/claude-code/scripts/harness_archive.py +1043 -1108
  161. package/harness/bundles/java/claude-code/scripts/harness_context.py +8 -2
  162. package/harness/bundles/java/claude-code/scripts/harness_events.py +5 -0
  163. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +79 -2
  164. package/harness/bundles/java/claude-code/scripts/harness_profile.py +7 -0
  165. package/harness/bundles/java/claude-code/scripts/harness_state.py +22 -3
  166. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  167. package/harness/bundles/java/codebuddy/README.md +14 -16
  168. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +2 -2
  169. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +43 -46
  170. package/harness/bundles/java/codebuddy/harness-archive/checklist.md +10 -10
  171. package/harness/bundles/java/codebuddy/harness-archive/reference.md +29 -22
  172. package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +1 -7
  173. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  174. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  175. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  176. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -2
  177. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +4 -4
  178. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +2 -2
  179. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +5 -3
  180. package/harness/bundles/java/codebuddy/harness-run/protocols.md +2 -0
  181. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
  182. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  183. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +2 -2
  184. package/harness/bundles/java/codebuddy/harness-test/checklist.md +2 -2
  185. package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +3 -3
  186. package/harness/bundles/java/codebuddy/harness-test/reference.md +2 -2
  187. package/harness/bundles/java/codebuddy/protocols/archive-report-protocol.md +18 -25
  188. package/harness/bundles/java/codebuddy/protocols/evidence-based-reporting-protocol.md +5 -5
  189. package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +7 -7
  190. package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +10 -9
  191. package/harness/bundles/java/codebuddy/protocols/sensitive-info-protocol.md +3 -3
  192. package/harness/bundles/java/codebuddy/protocols/state-layout-protocol.md +5 -6
  193. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +1043 -1108
  194. package/harness/bundles/java/codebuddy/scripts/harness_context.py +8 -2
  195. package/harness/bundles/java/codebuddy/scripts/harness_events.py +5 -0
  196. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +79 -2
  197. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +7 -0
  198. package/harness/bundles/java/codebuddy/scripts/harness_state.py +22 -3
  199. package/harness/bundles/java/codex/.harness-build.json +1 -1
  200. package/harness/bundles/java/codex/README.md +14 -16
  201. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +2 -2
  202. package/harness/bundles/java/codex/harness-archive/SKILL.md +43 -46
  203. package/harness/bundles/java/codex/harness-archive/checklist.md +10 -10
  204. package/harness/bundles/java/codex/harness-archive/reference.md +29 -22
  205. package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +1 -7
  206. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  207. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  208. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  209. package/harness/bundles/java/codex/harness-package/SKILL.md +2 -2
  210. package/harness/bundles/java/codex/harness-plan/SKILL.md +4 -4
  211. package/harness/bundles/java/codex/harness-review/SKILL.md +2 -2
  212. package/harness/bundles/java/codex/harness-run/SKILL.md +5 -3
  213. package/harness/bundles/java/codex/harness-run/protocols.md +2 -0
  214. package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
  215. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  216. package/harness/bundles/java/codex/harness-test/SKILL.md +2 -2
  217. package/harness/bundles/java/codex/harness-test/checklist.md +2 -2
  218. package/harness/bundles/java/codex/harness-test/pitfalls.md +3 -3
  219. package/harness/bundles/java/codex/harness-test/reference.md +2 -2
  220. package/harness/bundles/java/codex/protocols/archive-report-protocol.md +18 -25
  221. package/harness/bundles/java/codex/protocols/evidence-based-reporting-protocol.md +5 -5
  222. package/harness/bundles/java/codex/protocols/ledger-protocol.md +7 -7
  223. package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +10 -9
  224. package/harness/bundles/java/codex/protocols/sensitive-info-protocol.md +3 -3
  225. package/harness/bundles/java/codex/protocols/state-layout-protocol.md +5 -6
  226. package/harness/bundles/java/codex/scripts/harness_archive.py +1043 -1108
  227. package/harness/bundles/java/codex/scripts/harness_context.py +8 -2
  228. package/harness/bundles/java/codex/scripts/harness_events.py +5 -0
  229. package/harness/bundles/java/codex/scripts/harness_ledger.py +79 -2
  230. package/harness/bundles/java/codex/scripts/harness_profile.py +7 -0
  231. package/harness/bundles/java/codex/scripts/harness_state.py +22 -3
  232. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  233. package/harness/bundles/java/cursor/README.md +14 -16
  234. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +2 -2
  235. package/harness/bundles/java/cursor/harness-archive/SKILL.md +43 -46
  236. package/harness/bundles/java/cursor/harness-archive/checklist.md +10 -10
  237. package/harness/bundles/java/cursor/harness-archive/reference.md +29 -22
  238. package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +1 -7
  239. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  240. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  241. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  242. package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -2
  243. package/harness/bundles/java/cursor/harness-plan/SKILL.md +4 -4
  244. package/harness/bundles/java/cursor/harness-review/SKILL.md +2 -2
  245. package/harness/bundles/java/cursor/harness-run/SKILL.md +5 -3
  246. package/harness/bundles/java/cursor/harness-run/protocols.md +2 -0
  247. package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
  248. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  249. package/harness/bundles/java/cursor/harness-test/SKILL.md +2 -2
  250. package/harness/bundles/java/cursor/harness-test/checklist.md +2 -2
  251. package/harness/bundles/java/cursor/harness-test/pitfalls.md +3 -3
  252. package/harness/bundles/java/cursor/harness-test/reference.md +2 -2
  253. package/harness/bundles/java/cursor/protocols/archive-report-protocol.md +18 -25
  254. package/harness/bundles/java/cursor/protocols/evidence-based-reporting-protocol.md +5 -5
  255. package/harness/bundles/java/cursor/protocols/ledger-protocol.md +7 -7
  256. package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +10 -9
  257. package/harness/bundles/java/cursor/protocols/sensitive-info-protocol.md +3 -3
  258. package/harness/bundles/java/cursor/protocols/state-layout-protocol.md +5 -6
  259. package/harness/bundles/java/cursor/scripts/harness_archive.py +1043 -1108
  260. package/harness/bundles/java/cursor/scripts/harness_context.py +8 -2
  261. package/harness/bundles/java/cursor/scripts/harness_events.py +5 -0
  262. package/harness/bundles/java/cursor/scripts/harness_ledger.py +79 -2
  263. package/harness/bundles/java/cursor/scripts/harness_profile.py +7 -0
  264. package/harness/bundles/java/cursor/scripts/harness_state.py +22 -3
  265. package/harness/manifests/general/claude-code.json +35 -39
  266. package/harness/manifests/general/codebuddy.json +35 -39
  267. package/harness/manifests/general/codex.json +35 -39
  268. package/harness/manifests/general/cursor.json +35 -39
  269. package/harness/manifests/java/claude-code.json +35 -39
  270. package/harness/manifests/java/codebuddy.json +35 -39
  271. package/harness/manifests/java/codex.json +35 -39
  272. package/harness/manifests/java/cursor.json +35 -39
  273. package/hunter-workflow-family.json +5 -5
  274. package/package.json +1 -1
  275. package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +0 -299
  276. package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +0 -299
  277. package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +0 -299
  278. package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +0 -299
  279. package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +0 -299
  280. package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +0 -299
  281. package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +0 -299
  282. package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +0 -299
@@ -1,299 +0,0 @@
1
- #!/usr/bin/env node
2
- // Deterministic executive renderer for normalized Harness archive reports.
3
- import fs from "node:fs";
4
- import path from "node:path";
5
-
6
- const args = process.argv.slice(2);
7
- const arg = (name, fallback) => {
8
- const index = args.indexOf(name);
9
- return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
10
- };
11
- const summaryPath = arg("--summary", args[0]);
12
- const outPath = arg("--out", args[1] || "final-summary.html");
13
- if (!summaryPath) {
14
- console.error("Usage: node render-summary.mjs --summary summary-data.json --out final-summary.html");
15
- process.exit(2);
16
- }
17
-
18
- const data = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
19
- const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({
20
- "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"
21
- })[character]);
22
- const list = (value) => Array.isArray(value) ? value : [];
23
- const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
24
- const numeric = (value) => Number.isFinite(Number(value)) ? Number(value) : null;
25
- const normalized = record(data.normalizedReport);
26
- const outcomes = record(normalized.outcomes);
27
- const current = Object.keys(record(outcomes.current)).length
28
- ? record(outcomes.current)
29
- : {
30
- status: data.finalStatus || "UNKNOWN",
31
- reasons: list(data.finalStatusReasons),
32
- stages: record(data.stageStatus),
33
- knownRisks: list(data.knownRisks),
34
- findings: list(data.reviewFindings)
35
- };
36
- const history = record(outcomes.history);
37
- const release = Object.keys(record(outcomes.release)).length
38
- ? record(outcomes.release)
39
- : {
40
- decision: data.archiveIntent === "record-only" ? "NOT_REQUESTED" : record(data.releaseDecision).code,
41
- eligible: Boolean(record(data.releaseDecision).releaseEligible),
42
- candidate: record(data.candidateVerification),
43
- intent: data.archiveIntent || ""
44
- };
45
- const identity = Object.keys(record(normalized.identity)).length
46
- ? record(normalized.identity)
47
- : record(data.changeIdentity);
48
- const verification = Object.keys(record(normalized.verification)).length
49
- ? record(normalized.verification)
50
- : record(data.verification);
51
- const scenarioCoverage = record(data.scenarioCoverage);
52
- const unexecutedScenarios = list(scenarioCoverage.unexecuted);
53
- const timing = Object.keys(record(normalized.timing)).length
54
- ? record(normalized.timing)
55
- : record(data.timing);
56
- const measurements = record(normalized.measurements);
57
- const recordOnly = release.intent === "record-only" || data.archiveIntent === "record-only";
58
- const files = list(data.changedFiles);
59
- const commands = list(record(data.reportPipeline).commands);
60
- const timeline = list(history.timeline).length ? list(history.timeline) : list(data.timeline);
61
- const attempts = list(history.attempts).length
62
- ? list(history.attempts)
63
- : list(timing.attempts);
64
- const actions = list(data.manualActions);
65
- const risks = list(current.findings).length
66
- ? list(current.findings)
67
- : list(current.knownRisks).length
68
- ? list(current.knownRisks)
69
- : list(data.knownRisks);
70
-
71
- const statusTone = (raw) => {
72
- const status = String(raw || "UNKNOWN").toUpperCase();
73
- if (/FAIL|ERROR|BLOCKED/.test(status)) return "danger";
74
- if (/WARN|CONDITIONAL|PARTIAL|NOT_RUN|UNKNOWN|SKIP|EVIDENCE_MISSING/.test(status)) return "warning";
75
- if (/ADVISORY|REUSED|NOT_APPLICABLE/.test(status)) return "neutral";
76
- return "success";
77
- };
78
- const STATUS_LABELS = {
79
- OK: "通过", PASS: "通过", PASSED: "通过", CONDITIONAL_OK: "有条件通过",
80
- WARN: "警告", ADVISORY: "建议", FAIL: "失败", FAILED: "失败", ERROR: "错误",
81
- BLOCKED: "阻塞", NOT_RUN: "未运行", SKIPPED: "已跳过",
82
- NOT_APPLICABLE: "不适用", UNKNOWN: "未知", EVIDENCE_MISSING: "证据缺失"
83
- };
84
- const pill = (raw) => {
85
- const status = String(raw || "UNKNOWN").toUpperCase();
86
- return `<span class="pill ${statusTone(status)}" title="${esc(status)}">${esc(STATUS_LABELS[status] || status)}</span>`;
87
- };
88
- const describe = (value) => {
89
- if (typeof value === "string") return value;
90
- const item = record(value);
91
- return item.title || item.message || item.summary || item.action || item.remediation || item.note || JSON.stringify(item);
92
- };
93
- const shortHash = (value) => String(value || "N/A").slice(0, 10);
94
- const duration = (raw) => {
95
- const value = numeric(raw);
96
- if (value === null) return "N/A";
97
- if (value < 1000) return `${Math.round(value)}ms`;
98
- if (value < 60000) return `${Math.round(value / 100) / 10} 秒`;
99
- if (value < 3600000) return `${Math.round(value / 6000) / 10} 分钟`;
100
- return `${Math.floor(value / 3600000)} 小时 ${Math.round(value % 3600000 / 60000)} 分钟`;
101
- };
102
- const measurement = (value, unit = "") => {
103
- const item = record(value);
104
- if (item.state === "unknown" || item.state === "not_applicable") return "N/A";
105
- if (item.state === "zero") return `0${unit}`;
106
- if (item.state === "known") return `${esc(item.value)}${unit}`;
107
- if (value === null || value === undefined || value === "") return "N/A";
108
- return `${esc(value)}${unit}`;
109
- };
110
- const verificationStatus = (value) => {
111
- if (typeof value === "string") return value;
112
- const item = record(value);
113
- if (item.status) return item.status;
114
- if ((numeric(item.failures) || 0) + (numeric(item.errors) || 0) > 0) return "FAIL";
115
- if ((numeric(item.run) || numeric(item.total) || 0) > 0) return "OK";
116
- return "NOT_RUN";
117
- };
118
- const verificationNote = (key, raw) => {
119
- const value = record(raw);
120
- if (key === "unitTests") {
121
- const unique = numeric(value.uniqueTestCount);
122
- const uniqueLabel = unique === null ? "唯一用例未记录" : `唯一 ${unique} 项`;
123
- return `${value.passRate || `${value.run ?? 0} 次执行`} · ${uniqueLabel} · 重跑 ${value.rerunCount ?? 0}`;
124
- }
125
- if (key === "dbCompatibility") {
126
- const evidence = record(verification.dbCompatibilityEvidence);
127
- return evidence.status === "EVIDENCE_MISSING"
128
- ? "缺少可验证的类型化账本/收据"
129
- : evidence.reason || evidence.evidenceHash || "类型化兼容性证据";
130
- }
131
- if (key === "apiTests") return `${value.passRate || `${value.passed ?? 0}/${value.total ?? 0}`} · 阻塞 ${value.blocked ?? 0}`;
132
- if (key === "browserE2E") return `${value.passed ?? 0}/${value.total ?? 0} 通过 · ${value.failed ?? 0} 失败 · 重试 ${value.retries ?? 0}`;
133
- if (value.checks !== undefined) return `${value.checks} 项检查`;
134
- if (value.coverageDisplay) return value.coverageDisplay;
135
- return Object.keys(value).filter((name) => name !== "status").slice(0, 3)
136
- .map((name) => `${name}=${value[name]}`).join(" · ") || "无额外指标";
137
- };
138
- const groups = [
139
- ["后端", ["unitTests", "dbCompatibility"]],
140
- ["Geo", ["geo"]],
141
- ["前端", ["frontend"]],
142
- ["浏览器", ["browserE2E"]],
143
- ["API", ["apiTests"]]
144
- ].map(([label, keys]) => {
145
- const present = keys.filter((key) => verification[key] !== undefined);
146
- const values = present.map((key) => verification[key]);
147
- const statuses = values.map(verificationStatus);
148
- const groupStatus = statuses.some((status) => /FAIL|ERROR|BLOCKED/.test(String(status)))
149
- ? "FAIL"
150
- : statuses.some((status) => /EVIDENCE_MISSING/.test(String(status)))
151
- ? "EVIDENCE_MISSING"
152
- : statuses.some((status) => /WARN|CONDITIONAL|PARTIAL/.test(String(status)))
153
- ? "WARN"
154
- : statuses.length && statuses.every((status) => /NOT_APPLICABLE/.test(String(status)))
155
- ? "NOT_APPLICABLE"
156
- : statuses.length && statuses.every((status) => /OK|PASS|NOT_APPLICABLE/.test(String(status)))
157
- ? "OK"
158
- : "NOT_RUN";
159
- const note = present.map((key) => verificationNote(key, verification[key])).join(" · ") || "未配置该组验证";
160
- return { label, status: groupStatus, note, present };
161
- });
162
- const passedGroups = groups.filter((group) => group.status === "OK").length;
163
- const productCommit = identity.productCommit || data.productCommit || data.finalCommit;
164
- const identityChain = [
165
- ["检查点", identity.checkpointCommit || data.checkpointCommit],
166
- ["产品", productCommit],
167
- ["功能尖端", identity.featureTip || data.featureTip],
168
- ["合并", identity.mergeCommit || data.mergeCommit || identity.featureMergeHash],
169
- ["发布尖端", identity.releaseTip || data.releaseTip || identity.releaseTipHash]
170
- ].filter(([, value]) => value).map(([label, value]) => `${label} ${shortHash(value)}`).join(" → ") || "N/A";
171
- const wallClock = timing.workflowWallClockMs ?? record(data.durations).totalMs;
172
-
173
- const groupHtml = groups.map((group) => `
174
- <article class="verify-card" title="${esc(group.present.map((key) => {
175
- const value = record(verification[key]);
176
- return `${key} · status=${verificationStatus(verification[key])}${value.failed === undefined ? "" : ` · failed=${value.failed}`}`;
177
- }).join(" / "))}">
178
- <div><span class="verify-name">${esc(group.label)}</span>${pill(group.status)}</div>
179
- <p>${esc(group.note)}</p>
180
- </article>`).join("");
181
- const risksHtml = risks.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>当前没有未处置风险</li>";
182
- const actionsHtml = actions.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>无需人工后续动作</li>";
183
- const stageHtml = Object.entries(record(current.stages)).map(([name, status]) =>
184
- `<tr><td>${esc(name)}</td><td>${pill(status)}</td></tr>`
185
- ).join("") || '<tr><td colspan="2">没有阶段状态记录</td></tr>';
186
- const fileHtml = files.map((item) =>
187
- `<tr><td><code>${esc(item.path || item.file)}</code></td><td class="positive">+${esc(item.insertions ?? 0)}</td><td class="negative">-${esc(item.deletions ?? 0)}</td></tr>`
188
- ).join("") || '<tr><td colspan="3">没有变更文件证据</td></tr>';
189
- const timelineHtml = timeline.map((item) =>
190
- `<tr><td>${esc(item.phase || item.stage || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.status || item.result || item.type)}</td><td>${esc(item.executorTool || item.executor_tool || item.summary || "-")}</td></tr>`
191
- ).join("") || '<tr><td colspan="4">没有时间线记录</td></tr>';
192
- const commandHtml = commands.map((item) =>
193
- `<tr><td>${esc(item.phase || "-")}</td><td><code>${esc(item.command || "")}</code></td><td>${pill(Number(item.exit_code ?? item.exitCode) === 0 ? "OK" : "FAIL")}</td></tr>`
194
- ).join("") || '<tr><td colspan="3">没有命令证据</td></tr>';
195
- const candidate = record(release.candidate);
196
- const releaseHtml = recordOnly ? "" : `
197
- <article class="card"><h2>发布与候选</h2>
198
- <div class="fact"><span>候选证明</span>${pill(candidate.ok ? "OK" : candidate.code || "NOT_RUN")}</div>
199
- <div class="fact"><span>发布资格</span>${pill(release.eligible ? "OK" : release.decision || "BLOCKED")}</div>
200
- </article>`;
201
- const remoteCost = record(measurements.remoteCost);
202
- const remoteCostTotals = record(remoteCost.totals);
203
- const storage = record(measurements.artifactStorage);
204
- const efficiency = record(data.efficiency);
205
- const efficiencyTiming = record(efficiency.timing);
206
- const environmentActions = record(efficiency.environment);
207
- const failureClasses = record(efficiency.failureClasses);
208
- const projection = record(data.projection);
209
- const durability = record(data.archiveDurability);
210
- const durabilityStatus = durability.status || "ARCHIVED_LOCAL_ONLY";
211
- const scenarioCoverageStatus = scenarioCoverage.skipped
212
- ? "SKIPPED"
213
- : scenarioCoverage.ok
214
- ? "OK"
215
- : scenarioCoverage.code || "NOT_RUN";
216
- const scenarioCoverageHtml = `
217
- <article class="card"><h2>场景执行闭环</h2>
218
- <div class="fact"><span>结构化运行回执</span>${pill(scenarioCoverageStatus)}</div>
219
- <div class="fact"><span>已执行并通过</span><strong>${list(scenarioCoverage.passed).length}</strong></div>
220
- <div class="fact"><span>未执行 / 未通过</span><span>${unexecutedScenarios.length
221
- ? `<code>${esc(unexecutedScenarios.join(", "))}</code>`
222
- : "无"}</span></div>
223
- </article>`;
224
-
225
- const html = `<!doctype html>
226
- <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
227
- <title>Harness 最终报告 · ${esc(data.changeName || "未命名变更")}</title>
228
- <style>
229
- :root{color-scheme:light dark;--canvas:#f4f6f8;--surface:#fff;--surface-2:#f8fafc;--ink:#172033;--muted:#667085;--line:#dfe5ec;--accent:#2457d6;--good:#087443;--warn:#9a5b00;--bad:#b42318;--shadow:0 8px 26px rgba(22,34,58,.07)}
230
- @media(prefers-color-scheme:dark){:root{--canvas:#0b1119;--surface:#121a26;--surface-2:#172231;--ink:#eef3f9;--muted:#9aa8ba;--line:#2a384a;--accent:#86abff;--good:#58d59b;--warn:#efbd68;--bad:#ff8b83;--shadow:0 12px 30px rgba(0,0,0,.28)}}
231
- *{box-sizing:border-box}html,body{max-width:100%;overflow-x:hidden}body{margin:0;background:var(--canvas);color:var(--ink);font:14px/1.55 Inter,"Segoe UI","Microsoft YaHei",sans-serif}main{width:min(1180px,calc(100% - 32px));margin:24px auto 48px}.hero,.card,.metric,details,.verify-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}.hero{padding:24px 26px;border-top:4px solid var(--accent)}.kicker{color:var(--accent);font-size:12px;font-weight:800;letter-spacing:.1em}.hero h1{font-size:28px;line-height:1.25;margin:5px 0 7px;overflow-wrap:anywhere}.goal,.muted,small{color:var(--muted)}.outcome{display:flex;align-items:center;gap:10px;margin-top:14px}.record-only{display:inline-flex;margin-top:12px;padding:6px 10px;border-radius:8px;background:var(--surface-2);color:var(--muted)}.metrics{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:11px 0}.metric{min-width:0;padding:13px 14px}.metric strong{display:block;font-size:18px;margin-top:3px;overflow-wrap:anywhere}.card{padding:17px;margin-bottom:11px}.card h2,details summary{font-size:16px;font-weight:760}.card h2{margin:0 0 11px}.verification-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:9px}.verify-card{padding:13px;box-shadow:none;background:var(--surface-2)}.verify-card>div,.fact{display:flex;justify-content:space-between;gap:9px;align-items:center}.verify-name{font-weight:760}.verify-card p{margin:8px 0 0;color:var(--muted);font-size:12px}.two-column{display:grid;grid-template-columns:1.3fr .7fr;gap:11px}.risk-actions{display:grid;grid-template-columns:1fr 1fr;gap:10px}.risk-actions section{padding:12px;border-radius:10px;background:var(--surface-2)}.risk-actions h3{font-size:13px;margin:0 0 6px}.risk-actions ul{margin:0;padding-left:18px;color:var(--muted)}.fact{padding:8px 0;border-bottom:1px solid var(--line)}.fact:last-child{border:0}.pill{display:inline-flex;flex:none;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:800;white-space:nowrap}.pill.success{color:var(--good);background:color-mix(in srgb,var(--good) 13%,transparent)}.pill.warning{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.danger{color:var(--bad);background:color-mix(in srgb,var(--bad) 13%,transparent)}.pill.neutral{color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent)}details{margin:9px 0}summary{cursor:pointer;padding:13px 16px}details>div{padding:0 16px 16px;overflow:auto}.table-wrap{max-width:100%;overflow:auto}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:8px 9px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--muted);font-size:11px}code{font-family:"Cascadia Code",Consolas,monospace;color:var(--accent);overflow-wrap:anywhere}.positive{color:var(--good)}.negative{color:var(--bad)}dl{display:grid;grid-template-columns:155px minmax(0,1fr);gap:7px 12px}dt{color:var(--muted)}dd{margin:0;overflow-wrap:anywhere}
232
- @media(max-width:900px){.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.verification-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.two-column{grid-template-columns:1fr}}
233
- @media(max-width:600px){main{width:calc(100% - 18px);margin:9px auto 30px}.hero{padding:18px}.hero h1{font-size:23px}.metrics,.verification-grid,.risk-actions{grid-template-columns:1fr}.outcome{align-items:flex-start;flex-direction:column}.card{padding:14px}dl{grid-template-columns:1fr}.metric strong{font-size:17px}}
234
- </style></head>
235
- <body><main>
236
- <section class="hero">
237
- <div class="kicker">HARNESS · 管理结论</div>
238
- <h1>${esc(data.changeName || "未命名变更")}</h1>
239
- <p class="goal">${esc(data.businessGoal || "未记录业务目标")}</p>
240
- <div class="outcome">${pill(current.status)}<span>${esc(list(current.reasons).join(" · ") || "结论基于事件、验证账本与 Git 事实")}</span></div>
241
- ${recordOnly ? '<div class="record-only">归档意图:仅记录 · 未请求发布</div>' : ""}
242
- </section>
243
- <section class="metrics">
244
- <article class="metric"><small>产品提交链</small><strong><code title="${esc(identityChain)}">${esc(identityChain)}</code></strong></article>
245
- <article class="metric"><small>验证概览</small><strong>${passedGroups}/${groups.length} 组通过</strong><small>按后端 / Geo / 前端 / 浏览器 / API</small></article>
246
- <article class="metric"><small>风险与动作</small><strong>${risks.length} / ${actions.length}</strong><small>当前风险 / 人工动作</small></article>
247
- <article class="metric"><small>全流程耗时</small><strong>${esc(duration(wallClock))}</strong><small>活动 ${esc(duration(timing.stageActiveExecutionMs))}</small></article>
248
- <article class="metric"><small>代码范围</small><strong>${esc(record(data.diffStat).filesChanged ?? files.length)} 个文件</strong><small class="positive">+${esc(record(data.diffStat).insertions ?? 0)} · <span class="negative">-${esc(record(data.diffStat).deletions ?? 0)}</span></small></article>
249
- </section>
250
- <article class="card"><h2>验证概览</h2><div class="verification-grid">${groupHtml}</div></article>
251
- ${scenarioCoverageHtml}
252
- <section class="two-column">
253
- <article class="card"><h2>风险与动作</h2><div class="risk-actions"><section><h3>当前风险</h3><ul>${risksHtml}</ul></section><section><h3>人工动作</h3><ul>${actionsHtml}</ul></section></div></article>
254
- <div>
255
- ${releaseHtml}
256
- <article class="card"><h2>事实完整性</h2>
257
- <div class="fact"><span>归档完整性</span>${pill(record(data.archiveIntegrity).ok === false ? "FAIL" : record(data.archiveManifest).checksumStatus || "UNKNOWN")}</div>
258
- <div class="fact"><span>归档持久性</span><span>${pill(durabilityStatus === "ARCHIVED_DURABLE" ? "OK" : "WARN")} <code>${esc(durabilityStatus)}</code></span></div>
259
- <div class="fact"><span>时间守恒</span>${pill(numeric(timing.conservationDeltaMs) === 0 ? "OK" : "WARN")}</div>
260
- </article>
261
- </div>
262
- </section>
263
- <details><summary>技术证据 · 验证与时间守恒</summary><div>
264
- <div class="fact"><span>全流程墙钟 <code title="workflowWallClockMs">workflowWallClock</code></span><strong>${esc(duration(timing.workflowWallClockMs))}</strong></div>
265
- <div class="fact"><span>活动执行 <code title="stageActiveExecutionMs">stageActiveExecution</code></span><strong>${esc(duration(timing.stageActiveExecutionMs))}</strong></div>
266
- <div class="fact"><span>远端等待 <code title="externalWaitMs">externalWait</code></span><strong>${esc(duration(timing.externalWaitMs))}</strong></div>
267
- <div class="fact"><span>暂停 <code title="pausedMs">paused</code></span><strong>${esc(duration(timing.pausedMs))}</strong></div>
268
- <div class="fact"><span>未归因 <code title="agentOrToolUnattributedMs">agentOrToolUnattributed</code></span><strong>${esc(duration(timing.agentOrToolUnattributedMs))}</strong></div>
269
- <div class="fact"><span>远端 runner 成本</span><strong>${measurement(remoteCostTotals.runnerMinutes ?? remoteCost.runnerMinutes, " 分钟")}</strong></div>
270
- <div class="fact"><span>新增制品字节</span><strong>${measurement(storage.bytesAdded, " bytes")}</strong></div>
271
- <div class="fact"><span>验证尝试 / 资源等待</span><strong>${esc(efficiency.verificationAttempts ?? 0)} / ${esc(duration(efficiencyTiming.resourceWaitMs))}</strong></div>
272
- <div class="fact"><span>环境准备 / 复用 / 重置 / 清理</span><strong>${esc(environmentActions.prepare ?? 0)} / ${esc(environmentActions.reuse ?? 0)} / ${esc(environmentActions.reset ?? 0)} / ${esc(environmentActions.cleanup ?? 0)}</strong></div>
273
- <div class="fact"><span>启动器 / 环境 / 测试 / 外部失败</span><strong>${esc(failureClasses.launcher ?? 0)} / ${esc(failureClasses.environment ?? 0)} / ${esc(failureClasses.test ?? 0)} / ${esc(failureClasses.external ?? 0)}</strong></div>
274
- <div class="fact"><span>无新证据的重复命令</span><strong>${esc(efficiency.repeatedCommandsWithoutNewEvidence ?? 0)}</strong></div>
275
- <div class="fact"><span>守恒差值 <code>conservationDeltaMs</code></span><strong>${esc(timing.conservationDeltaMs ?? "N/A")}</strong></div>
276
- </div></details>
277
- <details><summary>阶段状态</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>结果</th></tr></thead><tbody>${stageHtml}</tbody></table></div></details>
278
- <details><summary>变更文件(${files.length})</summary><div class="table-wrap"><table><thead><tr><th>文件</th><th>新增</th><th>删除</th></tr></thead><tbody>${fileHtml}</tbody></table></div></details>
279
- <details><summary>执行时间线与工具交接(${timeline.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>尝试</th><th>状态</th><th>来源 / 摘要</th></tr></thead><tbody>${timelineHtml}</tbody></table></div></details>
280
- <details><summary>命令证据(${commands.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>命令</th><th>结果</th></tr></thead><tbody>${commandHtml}</tbody></table></div></details>
281
- <details><summary>技术元数据</summary><div><dl>
282
- <dt>产品提交</dt><dd><code>${esc(productCommit || "N/A")}</code></dd>
283
- <dt>检查点 → 产品 → 功能尖端 → 合并 → 发布尖端</dt><dd><code>${esc(identityChain)}</code></dd>
284
- <dt>产品树哈希</dt><dd><code>${esc(identity.productTreeHash || data.productTreeHash || "N/A")}</code></dd>
285
- <dt>环境哈希</dt><dd><code>${esc(identity.environmentHash || data.environmentHash || "N/A")}</code></dd>
286
- <dt>基线提交</dt><dd><code>${esc(identity.baseCommit || data.baseCommit || "N/A")}</code></dd>
287
- <dt>报告数据版本</dt><dd>${esc(data.schemaVersion || "N/A")}</dd>
288
- <dt>归档意图</dt><dd title="${esc(release.intent)}">${recordOnly ? "仅记录(未请求发布)" : esc(release.intent || "未声明")}</dd>
289
- <dt>归档持久性</dt><dd><code>${esc(durabilityStatus)}</code>${durability.risk ? ` · ${esc(durability.risk)}` : ""}</dd>
290
- <dt>保留策略</dt><dd>${esc(durability.retentionPolicy || "N/A")}</dd>
291
- <dt>场景回执状态</dt><dd><code>${esc(scenarioCoverage.code || scenarioCoverageStatus)}</code></dd>
292
- <dt>尝试记录</dt><dd>${attempts.length}</dd>
293
- <dt>历史质量</dt><dd>${numeric(timing.unclosedAttemptCount) === 0 ? "无未闭合尝试" : `${esc(timing.unclosedAttemptCount ?? "N/A")} 个未闭合尝试`}</dd>
294
- <dt>投影状态</dt><dd title="Projection / Fallback"><code>${esc(projection.code || projection.mode || "N/A")}</code></dd>
295
- </dl></div></details>
296
- </main></body></html>`;
297
-
298
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
299
- fs.writeFileSync(outPath, html, "utf8");
@@ -1,299 +0,0 @@
1
- #!/usr/bin/env node
2
- // Deterministic executive renderer for normalized Harness archive reports.
3
- import fs from "node:fs";
4
- import path from "node:path";
5
-
6
- const args = process.argv.slice(2);
7
- const arg = (name, fallback) => {
8
- const index = args.indexOf(name);
9
- return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
10
- };
11
- const summaryPath = arg("--summary", args[0]);
12
- const outPath = arg("--out", args[1] || "final-summary.html");
13
- if (!summaryPath) {
14
- console.error("Usage: node render-summary.mjs --summary summary-data.json --out final-summary.html");
15
- process.exit(2);
16
- }
17
-
18
- const data = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
19
- const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({
20
- "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"
21
- })[character]);
22
- const list = (value) => Array.isArray(value) ? value : [];
23
- const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
24
- const numeric = (value) => Number.isFinite(Number(value)) ? Number(value) : null;
25
- const normalized = record(data.normalizedReport);
26
- const outcomes = record(normalized.outcomes);
27
- const current = Object.keys(record(outcomes.current)).length
28
- ? record(outcomes.current)
29
- : {
30
- status: data.finalStatus || "UNKNOWN",
31
- reasons: list(data.finalStatusReasons),
32
- stages: record(data.stageStatus),
33
- knownRisks: list(data.knownRisks),
34
- findings: list(data.reviewFindings)
35
- };
36
- const history = record(outcomes.history);
37
- const release = Object.keys(record(outcomes.release)).length
38
- ? record(outcomes.release)
39
- : {
40
- decision: data.archiveIntent === "record-only" ? "NOT_REQUESTED" : record(data.releaseDecision).code,
41
- eligible: Boolean(record(data.releaseDecision).releaseEligible),
42
- candidate: record(data.candidateVerification),
43
- intent: data.archiveIntent || ""
44
- };
45
- const identity = Object.keys(record(normalized.identity)).length
46
- ? record(normalized.identity)
47
- : record(data.changeIdentity);
48
- const verification = Object.keys(record(normalized.verification)).length
49
- ? record(normalized.verification)
50
- : record(data.verification);
51
- const scenarioCoverage = record(data.scenarioCoverage);
52
- const unexecutedScenarios = list(scenarioCoverage.unexecuted);
53
- const timing = Object.keys(record(normalized.timing)).length
54
- ? record(normalized.timing)
55
- : record(data.timing);
56
- const measurements = record(normalized.measurements);
57
- const recordOnly = release.intent === "record-only" || data.archiveIntent === "record-only";
58
- const files = list(data.changedFiles);
59
- const commands = list(record(data.reportPipeline).commands);
60
- const timeline = list(history.timeline).length ? list(history.timeline) : list(data.timeline);
61
- const attempts = list(history.attempts).length
62
- ? list(history.attempts)
63
- : list(timing.attempts);
64
- const actions = list(data.manualActions);
65
- const risks = list(current.findings).length
66
- ? list(current.findings)
67
- : list(current.knownRisks).length
68
- ? list(current.knownRisks)
69
- : list(data.knownRisks);
70
-
71
- const statusTone = (raw) => {
72
- const status = String(raw || "UNKNOWN").toUpperCase();
73
- if (/FAIL|ERROR|BLOCKED/.test(status)) return "danger";
74
- if (/WARN|CONDITIONAL|PARTIAL|NOT_RUN|UNKNOWN|SKIP|EVIDENCE_MISSING/.test(status)) return "warning";
75
- if (/ADVISORY|REUSED|NOT_APPLICABLE/.test(status)) return "neutral";
76
- return "success";
77
- };
78
- const STATUS_LABELS = {
79
- OK: "通过", PASS: "通过", PASSED: "通过", CONDITIONAL_OK: "有条件通过",
80
- WARN: "警告", ADVISORY: "建议", FAIL: "失败", FAILED: "失败", ERROR: "错误",
81
- BLOCKED: "阻塞", NOT_RUN: "未运行", SKIPPED: "已跳过",
82
- NOT_APPLICABLE: "不适用", UNKNOWN: "未知", EVIDENCE_MISSING: "证据缺失"
83
- };
84
- const pill = (raw) => {
85
- const status = String(raw || "UNKNOWN").toUpperCase();
86
- return `<span class="pill ${statusTone(status)}" title="${esc(status)}">${esc(STATUS_LABELS[status] || status)}</span>`;
87
- };
88
- const describe = (value) => {
89
- if (typeof value === "string") return value;
90
- const item = record(value);
91
- return item.title || item.message || item.summary || item.action || item.remediation || item.note || JSON.stringify(item);
92
- };
93
- const shortHash = (value) => String(value || "N/A").slice(0, 10);
94
- const duration = (raw) => {
95
- const value = numeric(raw);
96
- if (value === null) return "N/A";
97
- if (value < 1000) return `${Math.round(value)}ms`;
98
- if (value < 60000) return `${Math.round(value / 100) / 10} 秒`;
99
- if (value < 3600000) return `${Math.round(value / 6000) / 10} 分钟`;
100
- return `${Math.floor(value / 3600000)} 小时 ${Math.round(value % 3600000 / 60000)} 分钟`;
101
- };
102
- const measurement = (value, unit = "") => {
103
- const item = record(value);
104
- if (item.state === "unknown" || item.state === "not_applicable") return "N/A";
105
- if (item.state === "zero") return `0${unit}`;
106
- if (item.state === "known") return `${esc(item.value)}${unit}`;
107
- if (value === null || value === undefined || value === "") return "N/A";
108
- return `${esc(value)}${unit}`;
109
- };
110
- const verificationStatus = (value) => {
111
- if (typeof value === "string") return value;
112
- const item = record(value);
113
- if (item.status) return item.status;
114
- if ((numeric(item.failures) || 0) + (numeric(item.errors) || 0) > 0) return "FAIL";
115
- if ((numeric(item.run) || numeric(item.total) || 0) > 0) return "OK";
116
- return "NOT_RUN";
117
- };
118
- const verificationNote = (key, raw) => {
119
- const value = record(raw);
120
- if (key === "unitTests") {
121
- const unique = numeric(value.uniqueTestCount);
122
- const uniqueLabel = unique === null ? "唯一用例未记录" : `唯一 ${unique} 项`;
123
- return `${value.passRate || `${value.run ?? 0} 次执行`} · ${uniqueLabel} · 重跑 ${value.rerunCount ?? 0}`;
124
- }
125
- if (key === "dbCompatibility") {
126
- const evidence = record(verification.dbCompatibilityEvidence);
127
- return evidence.status === "EVIDENCE_MISSING"
128
- ? "缺少可验证的类型化账本/收据"
129
- : evidence.reason || evidence.evidenceHash || "类型化兼容性证据";
130
- }
131
- if (key === "apiTests") return `${value.passRate || `${value.passed ?? 0}/${value.total ?? 0}`} · 阻塞 ${value.blocked ?? 0}`;
132
- if (key === "browserE2E") return `${value.passed ?? 0}/${value.total ?? 0} 通过 · ${value.failed ?? 0} 失败 · 重试 ${value.retries ?? 0}`;
133
- if (value.checks !== undefined) return `${value.checks} 项检查`;
134
- if (value.coverageDisplay) return value.coverageDisplay;
135
- return Object.keys(value).filter((name) => name !== "status").slice(0, 3)
136
- .map((name) => `${name}=${value[name]}`).join(" · ") || "无额外指标";
137
- };
138
- const groups = [
139
- ["后端", ["unitTests", "dbCompatibility"]],
140
- ["Geo", ["geo"]],
141
- ["前端", ["frontend"]],
142
- ["浏览器", ["browserE2E"]],
143
- ["API", ["apiTests"]]
144
- ].map(([label, keys]) => {
145
- const present = keys.filter((key) => verification[key] !== undefined);
146
- const values = present.map((key) => verification[key]);
147
- const statuses = values.map(verificationStatus);
148
- const groupStatus = statuses.some((status) => /FAIL|ERROR|BLOCKED/.test(String(status)))
149
- ? "FAIL"
150
- : statuses.some((status) => /EVIDENCE_MISSING/.test(String(status)))
151
- ? "EVIDENCE_MISSING"
152
- : statuses.some((status) => /WARN|CONDITIONAL|PARTIAL/.test(String(status)))
153
- ? "WARN"
154
- : statuses.length && statuses.every((status) => /NOT_APPLICABLE/.test(String(status)))
155
- ? "NOT_APPLICABLE"
156
- : statuses.length && statuses.every((status) => /OK|PASS|NOT_APPLICABLE/.test(String(status)))
157
- ? "OK"
158
- : "NOT_RUN";
159
- const note = present.map((key) => verificationNote(key, verification[key])).join(" · ") || "未配置该组验证";
160
- return { label, status: groupStatus, note, present };
161
- });
162
- const passedGroups = groups.filter((group) => group.status === "OK").length;
163
- const productCommit = identity.productCommit || data.productCommit || data.finalCommit;
164
- const identityChain = [
165
- ["检查点", identity.checkpointCommit || data.checkpointCommit],
166
- ["产品", productCommit],
167
- ["功能尖端", identity.featureTip || data.featureTip],
168
- ["合并", identity.mergeCommit || data.mergeCommit || identity.featureMergeHash],
169
- ["发布尖端", identity.releaseTip || data.releaseTip || identity.releaseTipHash]
170
- ].filter(([, value]) => value).map(([label, value]) => `${label} ${shortHash(value)}`).join(" → ") || "N/A";
171
- const wallClock = timing.workflowWallClockMs ?? record(data.durations).totalMs;
172
-
173
- const groupHtml = groups.map((group) => `
174
- <article class="verify-card" title="${esc(group.present.map((key) => {
175
- const value = record(verification[key]);
176
- return `${key} · status=${verificationStatus(verification[key])}${value.failed === undefined ? "" : ` · failed=${value.failed}`}`;
177
- }).join(" / "))}">
178
- <div><span class="verify-name">${esc(group.label)}</span>${pill(group.status)}</div>
179
- <p>${esc(group.note)}</p>
180
- </article>`).join("");
181
- const risksHtml = risks.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>当前没有未处置风险</li>";
182
- const actionsHtml = actions.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>无需人工后续动作</li>";
183
- const stageHtml = Object.entries(record(current.stages)).map(([name, status]) =>
184
- `<tr><td>${esc(name)}</td><td>${pill(status)}</td></tr>`
185
- ).join("") || '<tr><td colspan="2">没有阶段状态记录</td></tr>';
186
- const fileHtml = files.map((item) =>
187
- `<tr><td><code>${esc(item.path || item.file)}</code></td><td class="positive">+${esc(item.insertions ?? 0)}</td><td class="negative">-${esc(item.deletions ?? 0)}</td></tr>`
188
- ).join("") || '<tr><td colspan="3">没有变更文件证据</td></tr>';
189
- const timelineHtml = timeline.map((item) =>
190
- `<tr><td>${esc(item.phase || item.stage || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.status || item.result || item.type)}</td><td>${esc(item.executorTool || item.executor_tool || item.summary || "-")}</td></tr>`
191
- ).join("") || '<tr><td colspan="4">没有时间线记录</td></tr>';
192
- const commandHtml = commands.map((item) =>
193
- `<tr><td>${esc(item.phase || "-")}</td><td><code>${esc(item.command || "")}</code></td><td>${pill(Number(item.exit_code ?? item.exitCode) === 0 ? "OK" : "FAIL")}</td></tr>`
194
- ).join("") || '<tr><td colspan="3">没有命令证据</td></tr>';
195
- const candidate = record(release.candidate);
196
- const releaseHtml = recordOnly ? "" : `
197
- <article class="card"><h2>发布与候选</h2>
198
- <div class="fact"><span>候选证明</span>${pill(candidate.ok ? "OK" : candidate.code || "NOT_RUN")}</div>
199
- <div class="fact"><span>发布资格</span>${pill(release.eligible ? "OK" : release.decision || "BLOCKED")}</div>
200
- </article>`;
201
- const remoteCost = record(measurements.remoteCost);
202
- const remoteCostTotals = record(remoteCost.totals);
203
- const storage = record(measurements.artifactStorage);
204
- const efficiency = record(data.efficiency);
205
- const efficiencyTiming = record(efficiency.timing);
206
- const environmentActions = record(efficiency.environment);
207
- const failureClasses = record(efficiency.failureClasses);
208
- const projection = record(data.projection);
209
- const durability = record(data.archiveDurability);
210
- const durabilityStatus = durability.status || "ARCHIVED_LOCAL_ONLY";
211
- const scenarioCoverageStatus = scenarioCoverage.skipped
212
- ? "SKIPPED"
213
- : scenarioCoverage.ok
214
- ? "OK"
215
- : scenarioCoverage.code || "NOT_RUN";
216
- const scenarioCoverageHtml = `
217
- <article class="card"><h2>场景执行闭环</h2>
218
- <div class="fact"><span>结构化运行回执</span>${pill(scenarioCoverageStatus)}</div>
219
- <div class="fact"><span>已执行并通过</span><strong>${list(scenarioCoverage.passed).length}</strong></div>
220
- <div class="fact"><span>未执行 / 未通过</span><span>${unexecutedScenarios.length
221
- ? `<code>${esc(unexecutedScenarios.join(", "))}</code>`
222
- : "无"}</span></div>
223
- </article>`;
224
-
225
- const html = `<!doctype html>
226
- <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
227
- <title>Harness 最终报告 · ${esc(data.changeName || "未命名变更")}</title>
228
- <style>
229
- :root{color-scheme:light dark;--canvas:#f4f6f8;--surface:#fff;--surface-2:#f8fafc;--ink:#172033;--muted:#667085;--line:#dfe5ec;--accent:#2457d6;--good:#087443;--warn:#9a5b00;--bad:#b42318;--shadow:0 8px 26px rgba(22,34,58,.07)}
230
- @media(prefers-color-scheme:dark){:root{--canvas:#0b1119;--surface:#121a26;--surface-2:#172231;--ink:#eef3f9;--muted:#9aa8ba;--line:#2a384a;--accent:#86abff;--good:#58d59b;--warn:#efbd68;--bad:#ff8b83;--shadow:0 12px 30px rgba(0,0,0,.28)}}
231
- *{box-sizing:border-box}html,body{max-width:100%;overflow-x:hidden}body{margin:0;background:var(--canvas);color:var(--ink);font:14px/1.55 Inter,"Segoe UI","Microsoft YaHei",sans-serif}main{width:min(1180px,calc(100% - 32px));margin:24px auto 48px}.hero,.card,.metric,details,.verify-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}.hero{padding:24px 26px;border-top:4px solid var(--accent)}.kicker{color:var(--accent);font-size:12px;font-weight:800;letter-spacing:.1em}.hero h1{font-size:28px;line-height:1.25;margin:5px 0 7px;overflow-wrap:anywhere}.goal,.muted,small{color:var(--muted)}.outcome{display:flex;align-items:center;gap:10px;margin-top:14px}.record-only{display:inline-flex;margin-top:12px;padding:6px 10px;border-radius:8px;background:var(--surface-2);color:var(--muted)}.metrics{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:11px 0}.metric{min-width:0;padding:13px 14px}.metric strong{display:block;font-size:18px;margin-top:3px;overflow-wrap:anywhere}.card{padding:17px;margin-bottom:11px}.card h2,details summary{font-size:16px;font-weight:760}.card h2{margin:0 0 11px}.verification-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:9px}.verify-card{padding:13px;box-shadow:none;background:var(--surface-2)}.verify-card>div,.fact{display:flex;justify-content:space-between;gap:9px;align-items:center}.verify-name{font-weight:760}.verify-card p{margin:8px 0 0;color:var(--muted);font-size:12px}.two-column{display:grid;grid-template-columns:1.3fr .7fr;gap:11px}.risk-actions{display:grid;grid-template-columns:1fr 1fr;gap:10px}.risk-actions section{padding:12px;border-radius:10px;background:var(--surface-2)}.risk-actions h3{font-size:13px;margin:0 0 6px}.risk-actions ul{margin:0;padding-left:18px;color:var(--muted)}.fact{padding:8px 0;border-bottom:1px solid var(--line)}.fact:last-child{border:0}.pill{display:inline-flex;flex:none;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:800;white-space:nowrap}.pill.success{color:var(--good);background:color-mix(in srgb,var(--good) 13%,transparent)}.pill.warning{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.danger{color:var(--bad);background:color-mix(in srgb,var(--bad) 13%,transparent)}.pill.neutral{color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent)}details{margin:9px 0}summary{cursor:pointer;padding:13px 16px}details>div{padding:0 16px 16px;overflow:auto}.table-wrap{max-width:100%;overflow:auto}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:8px 9px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--muted);font-size:11px}code{font-family:"Cascadia Code",Consolas,monospace;color:var(--accent);overflow-wrap:anywhere}.positive{color:var(--good)}.negative{color:var(--bad)}dl{display:grid;grid-template-columns:155px minmax(0,1fr);gap:7px 12px}dt{color:var(--muted)}dd{margin:0;overflow-wrap:anywhere}
232
- @media(max-width:900px){.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.verification-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.two-column{grid-template-columns:1fr}}
233
- @media(max-width:600px){main{width:calc(100% - 18px);margin:9px auto 30px}.hero{padding:18px}.hero h1{font-size:23px}.metrics,.verification-grid,.risk-actions{grid-template-columns:1fr}.outcome{align-items:flex-start;flex-direction:column}.card{padding:14px}dl{grid-template-columns:1fr}.metric strong{font-size:17px}}
234
- </style></head>
235
- <body><main>
236
- <section class="hero">
237
- <div class="kicker">HARNESS · 管理结论</div>
238
- <h1>${esc(data.changeName || "未命名变更")}</h1>
239
- <p class="goal">${esc(data.businessGoal || "未记录业务目标")}</p>
240
- <div class="outcome">${pill(current.status)}<span>${esc(list(current.reasons).join(" · ") || "结论基于事件、验证账本与 Git 事实")}</span></div>
241
- ${recordOnly ? '<div class="record-only">归档意图:仅记录 · 未请求发布</div>' : ""}
242
- </section>
243
- <section class="metrics">
244
- <article class="metric"><small>产品提交链</small><strong><code title="${esc(identityChain)}">${esc(identityChain)}</code></strong></article>
245
- <article class="metric"><small>验证概览</small><strong>${passedGroups}/${groups.length} 组通过</strong><small>按后端 / Geo / 前端 / 浏览器 / API</small></article>
246
- <article class="metric"><small>风险与动作</small><strong>${risks.length} / ${actions.length}</strong><small>当前风险 / 人工动作</small></article>
247
- <article class="metric"><small>全流程耗时</small><strong>${esc(duration(wallClock))}</strong><small>活动 ${esc(duration(timing.stageActiveExecutionMs))}</small></article>
248
- <article class="metric"><small>代码范围</small><strong>${esc(record(data.diffStat).filesChanged ?? files.length)} 个文件</strong><small class="positive">+${esc(record(data.diffStat).insertions ?? 0)} · <span class="negative">-${esc(record(data.diffStat).deletions ?? 0)}</span></small></article>
249
- </section>
250
- <article class="card"><h2>验证概览</h2><div class="verification-grid">${groupHtml}</div></article>
251
- ${scenarioCoverageHtml}
252
- <section class="two-column">
253
- <article class="card"><h2>风险与动作</h2><div class="risk-actions"><section><h3>当前风险</h3><ul>${risksHtml}</ul></section><section><h3>人工动作</h3><ul>${actionsHtml}</ul></section></div></article>
254
- <div>
255
- ${releaseHtml}
256
- <article class="card"><h2>事实完整性</h2>
257
- <div class="fact"><span>归档完整性</span>${pill(record(data.archiveIntegrity).ok === false ? "FAIL" : record(data.archiveManifest).checksumStatus || "UNKNOWN")}</div>
258
- <div class="fact"><span>归档持久性</span><span>${pill(durabilityStatus === "ARCHIVED_DURABLE" ? "OK" : "WARN")} <code>${esc(durabilityStatus)}</code></span></div>
259
- <div class="fact"><span>时间守恒</span>${pill(numeric(timing.conservationDeltaMs) === 0 ? "OK" : "WARN")}</div>
260
- </article>
261
- </div>
262
- </section>
263
- <details><summary>技术证据 · 验证与时间守恒</summary><div>
264
- <div class="fact"><span>全流程墙钟 <code title="workflowWallClockMs">workflowWallClock</code></span><strong>${esc(duration(timing.workflowWallClockMs))}</strong></div>
265
- <div class="fact"><span>活动执行 <code title="stageActiveExecutionMs">stageActiveExecution</code></span><strong>${esc(duration(timing.stageActiveExecutionMs))}</strong></div>
266
- <div class="fact"><span>远端等待 <code title="externalWaitMs">externalWait</code></span><strong>${esc(duration(timing.externalWaitMs))}</strong></div>
267
- <div class="fact"><span>暂停 <code title="pausedMs">paused</code></span><strong>${esc(duration(timing.pausedMs))}</strong></div>
268
- <div class="fact"><span>未归因 <code title="agentOrToolUnattributedMs">agentOrToolUnattributed</code></span><strong>${esc(duration(timing.agentOrToolUnattributedMs))}</strong></div>
269
- <div class="fact"><span>远端 runner 成本</span><strong>${measurement(remoteCostTotals.runnerMinutes ?? remoteCost.runnerMinutes, " 分钟")}</strong></div>
270
- <div class="fact"><span>新增制品字节</span><strong>${measurement(storage.bytesAdded, " bytes")}</strong></div>
271
- <div class="fact"><span>验证尝试 / 资源等待</span><strong>${esc(efficiency.verificationAttempts ?? 0)} / ${esc(duration(efficiencyTiming.resourceWaitMs))}</strong></div>
272
- <div class="fact"><span>环境准备 / 复用 / 重置 / 清理</span><strong>${esc(environmentActions.prepare ?? 0)} / ${esc(environmentActions.reuse ?? 0)} / ${esc(environmentActions.reset ?? 0)} / ${esc(environmentActions.cleanup ?? 0)}</strong></div>
273
- <div class="fact"><span>启动器 / 环境 / 测试 / 外部失败</span><strong>${esc(failureClasses.launcher ?? 0)} / ${esc(failureClasses.environment ?? 0)} / ${esc(failureClasses.test ?? 0)} / ${esc(failureClasses.external ?? 0)}</strong></div>
274
- <div class="fact"><span>无新证据的重复命令</span><strong>${esc(efficiency.repeatedCommandsWithoutNewEvidence ?? 0)}</strong></div>
275
- <div class="fact"><span>守恒差值 <code>conservationDeltaMs</code></span><strong>${esc(timing.conservationDeltaMs ?? "N/A")}</strong></div>
276
- </div></details>
277
- <details><summary>阶段状态</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>结果</th></tr></thead><tbody>${stageHtml}</tbody></table></div></details>
278
- <details><summary>变更文件(${files.length})</summary><div class="table-wrap"><table><thead><tr><th>文件</th><th>新增</th><th>删除</th></tr></thead><tbody>${fileHtml}</tbody></table></div></details>
279
- <details><summary>执行时间线与工具交接(${timeline.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>尝试</th><th>状态</th><th>来源 / 摘要</th></tr></thead><tbody>${timelineHtml}</tbody></table></div></details>
280
- <details><summary>命令证据(${commands.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>命令</th><th>结果</th></tr></thead><tbody>${commandHtml}</tbody></table></div></details>
281
- <details><summary>技术元数据</summary><div><dl>
282
- <dt>产品提交</dt><dd><code>${esc(productCommit || "N/A")}</code></dd>
283
- <dt>检查点 → 产品 → 功能尖端 → 合并 → 发布尖端</dt><dd><code>${esc(identityChain)}</code></dd>
284
- <dt>产品树哈希</dt><dd><code>${esc(identity.productTreeHash || data.productTreeHash || "N/A")}</code></dd>
285
- <dt>环境哈希</dt><dd><code>${esc(identity.environmentHash || data.environmentHash || "N/A")}</code></dd>
286
- <dt>基线提交</dt><dd><code>${esc(identity.baseCommit || data.baseCommit || "N/A")}</code></dd>
287
- <dt>报告数据版本</dt><dd>${esc(data.schemaVersion || "N/A")}</dd>
288
- <dt>归档意图</dt><dd title="${esc(release.intent)}">${recordOnly ? "仅记录(未请求发布)" : esc(release.intent || "未声明")}</dd>
289
- <dt>归档持久性</dt><dd><code>${esc(durabilityStatus)}</code>${durability.risk ? ` · ${esc(durability.risk)}` : ""}</dd>
290
- <dt>保留策略</dt><dd>${esc(durability.retentionPolicy || "N/A")}</dd>
291
- <dt>场景回执状态</dt><dd><code>${esc(scenarioCoverage.code || scenarioCoverageStatus)}</code></dd>
292
- <dt>尝试记录</dt><dd>${attempts.length}</dd>
293
- <dt>历史质量</dt><dd>${numeric(timing.unclosedAttemptCount) === 0 ? "无未闭合尝试" : `${esc(timing.unclosedAttemptCount ?? "N/A")} 个未闭合尝试`}</dd>
294
- <dt>投影状态</dt><dd title="Projection / Fallback"><code>${esc(projection.code || projection.mode || "N/A")}</code></dd>
295
- </dl></div></details>
296
- </main></body></html>`;
297
-
298
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
299
- fs.writeFileSync(outPath, html, "utf8");