@hunter-harness/workflow-harness 0.2.41 → 0.2.43

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 (262) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/contracts/build-profile-v3.schema.json +56 -1
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-archive/reference.md +1 -0
  5. package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +8 -0
  6. package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +28 -0
  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 +42 -16
  9. package/harness/bundles/general/claude-code/harness-knowledge-ingest/reference.md +57 -6
  10. package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  11. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  14. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-run/reference.md +6 -1
  16. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  17. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +16 -10
  18. package/harness/bundles/general/claude-code/harness-sync/reference.md +34 -14
  19. package/harness/bundles/general/claude-code/harness-test/SKILL.md +8 -2
  20. package/harness/bundles/general/claude-code/protocols/execution-session-protocol.md +52 -0
  21. package/harness/bundles/general/claude-code/protocols/state-layout-protocol.md +24 -1
  22. package/harness/bundles/general/claude-code/protocols/verification-graph-protocol.md +6 -0
  23. package/harness/bundles/general/claude-code/scripts/harness_archive.py +4 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_efficiency.py +290 -0
  25. package/harness/bundles/general/claude-code/scripts/harness_environment.py +719 -35
  26. package/harness/bundles/general/claude-code/scripts/harness_fixback.py +742 -0
  27. package/harness/bundles/general/claude-code/scripts/harness_profile.py +34 -0
  28. package/harness/bundles/general/claude-code/scripts/harness_runtime.py +1165 -3
  29. package/harness/bundles/general/claude-code/scripts/harness_service.py +76 -0
  30. package/harness/bundles/general/claude-code/scripts/harness_test_runner.py +127 -39
  31. package/harness/bundles/general/claude-code/scripts/harness_verification.py +408 -8
  32. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  33. package/harness/bundles/general/codebuddy/contracts/build-profile-v3.schema.json +56 -1
  34. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  35. package/harness/bundles/general/codebuddy/harness-archive/reference.md +1 -0
  36. package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +8 -0
  37. package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +28 -0
  38. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  39. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +41 -15
  40. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/reference.md +57 -6
  41. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  42. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
  44. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  45. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  46. package/harness/bundles/general/codebuddy/harness-run/reference.md +6 -1
  47. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  48. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +16 -10
  49. package/harness/bundles/general/codebuddy/harness-sync/reference.md +34 -14
  50. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +8 -2
  51. package/harness/bundles/general/codebuddy/protocols/execution-session-protocol.md +52 -0
  52. package/harness/bundles/general/codebuddy/protocols/state-layout-protocol.md +24 -1
  53. package/harness/bundles/general/codebuddy/protocols/verification-graph-protocol.md +6 -0
  54. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +4 -0
  55. package/harness/bundles/general/codebuddy/scripts/harness_efficiency.py +290 -0
  56. package/harness/bundles/general/codebuddy/scripts/harness_environment.py +719 -35
  57. package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +742 -0
  58. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +34 -0
  59. package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +1165 -3
  60. package/harness/bundles/general/codebuddy/scripts/harness_service.py +76 -0
  61. package/harness/bundles/general/codebuddy/scripts/harness_test_runner.py +127 -39
  62. package/harness/bundles/general/codebuddy/scripts/harness_verification.py +408 -8
  63. package/harness/bundles/general/codex/.harness-build.json +1 -1
  64. package/harness/bundles/general/codex/contracts/build-profile-v3.schema.json +56 -1
  65. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-archive/reference.md +1 -0
  67. package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +8 -0
  68. package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +28 -0
  69. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  70. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +41 -15
  71. package/harness/bundles/general/codex/harness-knowledge-ingest/reference.md +57 -6
  72. package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  73. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  74. package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
  75. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  76. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  77. package/harness/bundles/general/codex/harness-run/reference.md +6 -1
  78. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  79. package/harness/bundles/general/codex/harness-sync/SKILL.md +16 -10
  80. package/harness/bundles/general/codex/harness-sync/reference.md +34 -14
  81. package/harness/bundles/general/codex/harness-test/SKILL.md +8 -2
  82. package/harness/bundles/general/codex/protocols/execution-session-protocol.md +52 -0
  83. package/harness/bundles/general/codex/protocols/state-layout-protocol.md +24 -1
  84. package/harness/bundles/general/codex/protocols/verification-graph-protocol.md +6 -0
  85. package/harness/bundles/general/codex/scripts/harness_archive.py +4 -0
  86. package/harness/bundles/general/codex/scripts/harness_efficiency.py +290 -0
  87. package/harness/bundles/general/codex/scripts/harness_environment.py +719 -35
  88. package/harness/bundles/general/codex/scripts/harness_fixback.py +742 -0
  89. package/harness/bundles/general/codex/scripts/harness_profile.py +34 -0
  90. package/harness/bundles/general/codex/scripts/harness_runtime.py +1165 -3
  91. package/harness/bundles/general/codex/scripts/harness_service.py +76 -0
  92. package/harness/bundles/general/codex/scripts/harness_test_runner.py +127 -39
  93. package/harness/bundles/general/codex/scripts/harness_verification.py +408 -8
  94. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  95. package/harness/bundles/general/cursor/contracts/build-profile-v3.schema.json +56 -1
  96. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  97. package/harness/bundles/general/cursor/harness-archive/reference.md +1 -0
  98. package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +8 -0
  99. package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +28 -0
  100. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  101. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +41 -15
  102. package/harness/bundles/general/cursor/harness-knowledge-ingest/reference.md +57 -6
  103. package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  104. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  105. package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
  106. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  107. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  108. package/harness/bundles/general/cursor/harness-run/reference.md +6 -1
  109. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  110. package/harness/bundles/general/cursor/harness-sync/SKILL.md +16 -10
  111. package/harness/bundles/general/cursor/harness-sync/reference.md +34 -14
  112. package/harness/bundles/general/cursor/harness-test/SKILL.md +8 -2
  113. package/harness/bundles/general/cursor/protocols/execution-session-protocol.md +52 -0
  114. package/harness/bundles/general/cursor/protocols/state-layout-protocol.md +24 -1
  115. package/harness/bundles/general/cursor/protocols/verification-graph-protocol.md +6 -0
  116. package/harness/bundles/general/cursor/scripts/harness_archive.py +4 -0
  117. package/harness/bundles/general/cursor/scripts/harness_efficiency.py +290 -0
  118. package/harness/bundles/general/cursor/scripts/harness_environment.py +719 -35
  119. package/harness/bundles/general/cursor/scripts/harness_fixback.py +742 -0
  120. package/harness/bundles/general/cursor/scripts/harness_profile.py +34 -0
  121. package/harness/bundles/general/cursor/scripts/harness_runtime.py +1165 -3
  122. package/harness/bundles/general/cursor/scripts/harness_service.py +76 -0
  123. package/harness/bundles/general/cursor/scripts/harness_test_runner.py +127 -39
  124. package/harness/bundles/general/cursor/scripts/harness_verification.py +408 -8
  125. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  126. package/harness/bundles/java/claude-code/contracts/build-profile-v3.schema.json +56 -1
  127. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  128. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  129. package/harness/bundles/java/claude-code/harness-archive/reference.md +1 -0
  130. package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +8 -0
  131. package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +28 -0
  132. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  133. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +42 -16
  134. package/harness/bundles/java/claude-code/harness-knowledge-ingest/reference.md +57 -6
  135. package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  136. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  137. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  138. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
  139. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  140. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  141. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  142. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +16 -10
  143. package/harness/bundles/java/claude-code/harness-sync/reference.md +34 -14
  144. package/harness/bundles/java/claude-code/harness-test/SKILL.md +8 -2
  145. package/harness/bundles/java/claude-code/protocols/execution-session-protocol.md +52 -0
  146. package/harness/bundles/java/claude-code/protocols/state-layout-protocol.md +24 -1
  147. package/harness/bundles/java/claude-code/protocols/verification-graph-protocol.md +6 -0
  148. package/harness/bundles/java/claude-code/scripts/harness_archive.py +4 -0
  149. package/harness/bundles/java/claude-code/scripts/harness_efficiency.py +290 -0
  150. package/harness/bundles/java/claude-code/scripts/harness_environment.py +719 -35
  151. package/harness/bundles/java/claude-code/scripts/harness_fixback.py +742 -0
  152. package/harness/bundles/java/claude-code/scripts/harness_profile.py +34 -0
  153. package/harness/bundles/java/claude-code/scripts/harness_runtime.py +1165 -3
  154. package/harness/bundles/java/claude-code/scripts/harness_service.py +76 -0
  155. package/harness/bundles/java/claude-code/scripts/harness_test_runner.py +127 -39
  156. package/harness/bundles/java/claude-code/scripts/harness_verification.py +408 -8
  157. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  158. package/harness/bundles/java/codebuddy/contracts/build-profile-v3.schema.json +56 -1
  159. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  160. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  161. package/harness/bundles/java/codebuddy/harness-archive/reference.md +1 -0
  162. package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +8 -0
  163. package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +28 -0
  164. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  165. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +41 -15
  166. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/reference.md +57 -6
  167. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  168. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  169. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  170. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
  171. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  172. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  173. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  174. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +16 -10
  175. package/harness/bundles/java/codebuddy/harness-sync/reference.md +34 -14
  176. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +8 -2
  177. package/harness/bundles/java/codebuddy/protocols/execution-session-protocol.md +52 -0
  178. package/harness/bundles/java/codebuddy/protocols/state-layout-protocol.md +24 -1
  179. package/harness/bundles/java/codebuddy/protocols/verification-graph-protocol.md +6 -0
  180. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +4 -0
  181. package/harness/bundles/java/codebuddy/scripts/harness_efficiency.py +290 -0
  182. package/harness/bundles/java/codebuddy/scripts/harness_environment.py +719 -35
  183. package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +742 -0
  184. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +34 -0
  185. package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +1165 -3
  186. package/harness/bundles/java/codebuddy/scripts/harness_service.py +76 -0
  187. package/harness/bundles/java/codebuddy/scripts/harness_test_runner.py +127 -39
  188. package/harness/bundles/java/codebuddy/scripts/harness_verification.py +408 -8
  189. package/harness/bundles/java/codex/.harness-build.json +1 -1
  190. package/harness/bundles/java/codex/contracts/build-profile-v3.schema.json +56 -1
  191. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  192. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  193. package/harness/bundles/java/codex/harness-archive/reference.md +1 -0
  194. package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +8 -0
  195. package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +28 -0
  196. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  197. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +41 -15
  198. package/harness/bundles/java/codex/harness-knowledge-ingest/reference.md +57 -6
  199. package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  200. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  201. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  202. package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
  203. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  204. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  205. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  206. package/harness/bundles/java/codex/harness-sync/SKILL.md +16 -10
  207. package/harness/bundles/java/codex/harness-sync/reference.md +34 -14
  208. package/harness/bundles/java/codex/harness-test/SKILL.md +8 -2
  209. package/harness/bundles/java/codex/protocols/execution-session-protocol.md +52 -0
  210. package/harness/bundles/java/codex/protocols/state-layout-protocol.md +24 -1
  211. package/harness/bundles/java/codex/protocols/verification-graph-protocol.md +6 -0
  212. package/harness/bundles/java/codex/scripts/harness_archive.py +4 -0
  213. package/harness/bundles/java/codex/scripts/harness_efficiency.py +290 -0
  214. package/harness/bundles/java/codex/scripts/harness_environment.py +719 -35
  215. package/harness/bundles/java/codex/scripts/harness_fixback.py +742 -0
  216. package/harness/bundles/java/codex/scripts/harness_profile.py +34 -0
  217. package/harness/bundles/java/codex/scripts/harness_runtime.py +1165 -3
  218. package/harness/bundles/java/codex/scripts/harness_service.py +76 -0
  219. package/harness/bundles/java/codex/scripts/harness_test_runner.py +127 -39
  220. package/harness/bundles/java/codex/scripts/harness_verification.py +408 -8
  221. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  222. package/harness/bundles/java/cursor/contracts/build-profile-v3.schema.json +56 -1
  223. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  224. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  225. package/harness/bundles/java/cursor/harness-archive/reference.md +1 -0
  226. package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +8 -0
  227. package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +28 -0
  228. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  229. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +41 -15
  230. package/harness/bundles/java/cursor/harness-knowledge-ingest/reference.md +57 -6
  231. package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +1097 -174
  232. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  233. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  234. package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
  235. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  236. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  237. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  238. package/harness/bundles/java/cursor/harness-sync/SKILL.md +16 -10
  239. package/harness/bundles/java/cursor/harness-sync/reference.md +34 -14
  240. package/harness/bundles/java/cursor/harness-test/SKILL.md +8 -2
  241. package/harness/bundles/java/cursor/protocols/execution-session-protocol.md +52 -0
  242. package/harness/bundles/java/cursor/protocols/state-layout-protocol.md +24 -1
  243. package/harness/bundles/java/cursor/protocols/verification-graph-protocol.md +6 -0
  244. package/harness/bundles/java/cursor/scripts/harness_archive.py +4 -0
  245. package/harness/bundles/java/cursor/scripts/harness_efficiency.py +290 -0
  246. package/harness/bundles/java/cursor/scripts/harness_environment.py +719 -35
  247. package/harness/bundles/java/cursor/scripts/harness_fixback.py +742 -0
  248. package/harness/bundles/java/cursor/scripts/harness_profile.py +34 -0
  249. package/harness/bundles/java/cursor/scripts/harness_runtime.py +1165 -3
  250. package/harness/bundles/java/cursor/scripts/harness_service.py +76 -0
  251. package/harness/bundles/java/cursor/scripts/harness_test_runner.py +127 -39
  252. package/harness/bundles/java/cursor/scripts/harness_verification.py +408 -8
  253. package/harness/manifests/general/claude-code.json +46 -33
  254. package/harness/manifests/general/codebuddy.json +46 -33
  255. package/harness/manifests/general/codex.json +46 -33
  256. package/harness/manifests/general/cursor.json +46 -33
  257. package/harness/manifests/java/claude-code.json +47 -34
  258. package/harness/manifests/java/codebuddy.json +47 -34
  259. package/harness/manifests/java/codex.json +47 -34
  260. package/harness/manifests/java/cursor.json +47 -34
  261. package/hunter-workflow-family.json +12 -10
  262. package/package.json +1 -1
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env python3
2
+ """Summarize execution efficiency without assigning blame."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import datetime as dt
8
+ import json
9
+ import sys
10
+ from collections import Counter
11
+ from pathlib import Path
12
+ from statistics import median
13
+ from typing import Any, Iterable
14
+
15
+ SCRIPTS_DIR = Path(__file__).resolve().parent
16
+ if str(SCRIPTS_DIR) not in sys.path:
17
+ sys.path.insert(0, str(SCRIPTS_DIR))
18
+
19
+ from harness_paths import resolve_state_dir_for_contract
20
+
21
+
22
+ SCHEMA_VERSION = 1
23
+ FAILURE_CLASSES = ("launcher", "environment", "test", "external", "unknown")
24
+
25
+
26
+ def _integer(value: Any) -> int:
27
+ try:
28
+ return max(0, int(value or 0))
29
+ except (TypeError, ValueError):
30
+ return 0
31
+
32
+
33
+ def _failure_class(session: dict[str, Any]) -> str | None:
34
+ status = str(session.get("status") or "").upper()
35
+ reason = str(session.get("reasonCode") or "").upper()
36
+ if status == "OK":
37
+ return None
38
+ if reason.startswith(("LAUNCHER_", "WORKER_", "LAUNCH_SPEC_")):
39
+ return "launcher"
40
+ if any(
41
+ token in reason
42
+ for token in ("ENVIRONMENT", "DOCKER", "PREFLIGHT", "RESOURCE_WAIT")
43
+ ):
44
+ return "environment"
45
+ if reason in {"EXTERNAL_WINDOW_ABORT", "CLIENT_DISCONNECTED"}:
46
+ return "external"
47
+ if status == "FAIL" or reason == "CHILD_EXIT_NONZERO":
48
+ return "test"
49
+ return "unknown"
50
+
51
+
52
+ def _counts(values: Iterable[str]) -> dict[str, int]:
53
+ return dict(sorted(Counter(values).items()))
54
+
55
+
56
+ def _wall_clock_ms(run_sessions: list[dict[str, Any]]) -> int:
57
+ intervals: list[tuple[dt.datetime, dt.datetime]] = []
58
+ fallback = 0
59
+ for session in run_sessions:
60
+ start_raw = session.get("createdAt") or session.get("startedAt")
61
+ end_raw = session.get("endedAt")
62
+ try:
63
+ start = dt.datetime.fromisoformat(
64
+ str(start_raw).replace("Z", "+00:00")
65
+ )
66
+ end = dt.datetime.fromisoformat(str(end_raw).replace("Z", "+00:00"))
67
+ if start.tzinfo is None:
68
+ start = start.astimezone()
69
+ if end.tzinfo is None:
70
+ end = end.astimezone()
71
+ if end < start:
72
+ raise ValueError("negative interval")
73
+ intervals.append((start.astimezone(dt.timezone.utc), end.astimezone(dt.timezone.utc)))
74
+ except (TypeError, ValueError):
75
+ fallback += _integer(session.get("wallClockMs"))
76
+ merged_ms = 0
77
+ current_start: dt.datetime | None = None
78
+ current_end: dt.datetime | None = None
79
+ for start, end in sorted(intervals):
80
+ if current_start is None or current_end is None:
81
+ current_start, current_end = start, end
82
+ continue
83
+ if start <= current_end:
84
+ current_end = max(current_end, end)
85
+ else:
86
+ merged_ms += int((current_end - current_start).total_seconds() * 1000)
87
+ current_start, current_end = start, end
88
+ if current_start is not None and current_end is not None:
89
+ merged_ms += int((current_end - current_start).total_seconds() * 1000)
90
+ return merged_ms + fallback
91
+
92
+
93
+ def _timing_by_stage(
94
+ run_sessions: list[dict[str, Any]],
95
+ ) -> dict[str, dict[str, int]]:
96
+ grouped: dict[str, list[dict[str, Any]]] = {}
97
+ for session in run_sessions:
98
+ stage_timings = session.get("stageTimings")
99
+ if isinstance(stage_timings, list) and stage_timings:
100
+ for item in stage_timings:
101
+ if isinstance(item, dict):
102
+ stage = str(item.get("stage") or "unknown")
103
+ grouped.setdefault(stage, []).append(item)
104
+ continue
105
+ stage = str(
106
+ session.get("stage")
107
+ or session.get("verification")
108
+ or "unknown"
109
+ )
110
+ grouped.setdefault(stage, []).append(session)
111
+ return {
112
+ stage: {
113
+ "wallClockMs": _wall_clock_ms(items),
114
+ "activeTimeMs": sum(_integer(item.get("activeTimeMs")) for item in items),
115
+ "resourceWaitMs": sum(
116
+ _integer(item.get("resourceWaitMs")) for item in items
117
+ ),
118
+ "attempts": len(items),
119
+ }
120
+ for stage, items in sorted(grouped.items())
121
+ }
122
+
123
+
124
+ def build_efficiency_summary(
125
+ *,
126
+ run_sessions: list[dict[str, Any]],
127
+ environment_receipts: list[dict[str, Any]],
128
+ invalidations: list[dict[str, Any]],
129
+ ) -> dict[str, Any]:
130
+ failure_counts = {key: 0 for key in FAILURE_CLASSES}
131
+ for session in run_sessions:
132
+ classification = _failure_class(session)
133
+ if classification is not None:
134
+ failure_counts[classification] += 1
135
+ environment_actions: Counter[str] = Counter()
136
+ for item in environment_receipts:
137
+ action = str(item.get("action") or "").lower()
138
+ if action not in {"prepare", "reuse", "reset", "cleanup"}:
139
+ continue
140
+ if action in {"reset", "cleanup"}:
141
+ evidence = item.get("operationEvidence")
142
+ if not isinstance(evidence, dict) or evidence.get("status") != "OK":
143
+ continue
144
+ environment_actions[action] += 1
145
+ evidence_counts = Counter(
146
+ (
147
+ str(item.get("commandHash")),
148
+ str(item.get("productIdentity")),
149
+ str(item.get("resultDigest")),
150
+ )
151
+ for item in run_sessions
152
+ if str(item.get("commandHash") or "").strip()
153
+ and str(item.get("productIdentity") or "").strip()
154
+ and str(item.get("resultDigest") or "").strip()
155
+ and item.get("testProcessStarted") is True
156
+ )
157
+ product_identities = {
158
+ str(item.get("productIdentity"))
159
+ for item in run_sessions
160
+ if str(item.get("productIdentity") or "").strip()
161
+ }
162
+ return {
163
+ "schemaVersion": SCHEMA_VERSION,
164
+ "timing": {
165
+ "wallClockMs": _wall_clock_ms(run_sessions),
166
+ "activeTimeMs": sum(
167
+ _integer(item.get("activeTimeMs")) for item in run_sessions
168
+ ),
169
+ "resourceWaitMs": sum(
170
+ _integer(item.get("resourceWaitMs")) for item in run_sessions
171
+ ),
172
+ },
173
+ "timingByStage": _timing_by_stage(run_sessions),
174
+ "executionAttempts": len(run_sessions),
175
+ "verificationAttempts": sum(
176
+ 1 for item in run_sessions if item.get("testProcessStarted") is True
177
+ ),
178
+ "launcherAttempts": sum(
179
+ 1 for item in run_sessions if item.get("testProcessStarted") is not True
180
+ ),
181
+ "statusCounts": _counts(
182
+ str(item.get("status") or "UNKNOWN").upper()
183
+ for item in run_sessions
184
+ ),
185
+ "failureClasses": failure_counts,
186
+ "invalidationReasons": _counts(
187
+ str(item.get("reasonCode") or "UNKNOWN")
188
+ for item in invalidations
189
+ ),
190
+ "environment": {
191
+ key: environment_actions.get(key, 0)
192
+ for key in ("prepare", "reuse", "reset", "cleanup")
193
+ },
194
+ "productIdentityCount": len(product_identities),
195
+ "repeatedCommandsWithoutNewEvidence": sum(
196
+ max(count - 1, 0) for count in evidence_counts.values()
197
+ ),
198
+ "manualWrapperCount": sum(
199
+ 1
200
+ for item in run_sessions
201
+ if item.get("managedByHarness") is False
202
+ ),
203
+ "notes": [
204
+ "Metrics separate launcher, environment, test, and external failures.",
205
+ "This summary reports facts and does not assign responsibility.",
206
+ ],
207
+ }
208
+
209
+
210
+ def compact_progress_view(
211
+ session: dict[str, Any],
212
+ *,
213
+ historical_durations: list[int | float],
214
+ ) -> dict[str, Any]:
215
+ completed = _integer(session.get("completedItems"))
216
+ planned = _integer(session.get("plannedItems"))
217
+ durations = sorted(
218
+ _integer(value) for value in historical_durations if _integer(value) > 0
219
+ )
220
+ eta: str | dict[str, int]
221
+ if len(durations) < 3:
222
+ eta = "INSUFFICIENT_HISTORY"
223
+ else:
224
+ eta = {
225
+ "lowSeconds": durations[0],
226
+ "typicalSeconds": int(median(durations)),
227
+ "highSeconds": durations[-1],
228
+ }
229
+ return {
230
+ "verification": session.get("verification"),
231
+ "stage": session.get("stage"),
232
+ "progress": f"{completed}/{planned}" if planned else str(completed),
233
+ "lastHeartbeatAt": session.get("lastHeartbeatAt"),
234
+ "expectedDurationSeconds": session.get("expectedDurationSeconds"),
235
+ "eta": eta,
236
+ "resourceWait": list(session.get("resourceLocks") or []),
237
+ "status": session.get("status", "RUNNING"),
238
+ }
239
+
240
+
241
+ def _read_objects(paths: list[Path]) -> list[dict[str, Any]]:
242
+ values: list[dict[str, Any]] = []
243
+ for path in paths:
244
+ try:
245
+ value = json.loads(path.read_text(encoding="utf-8-sig"))
246
+ except (OSError, json.JSONDecodeError):
247
+ continue
248
+ if isinstance(value, dict):
249
+ values.append(value)
250
+ elif isinstance(value, list):
251
+ values.extend(item for item in value if isinstance(item, dict))
252
+ return values
253
+
254
+
255
+ def collect_efficiency_summary(change_dir: Path) -> dict[str, Any]:
256
+ state_root = Path(resolve_state_dir_for_contract(change_dir)).resolve()
257
+ run_sessions = _read_objects(
258
+ sorted((state_root / "runtime" / "run-sessions").glob("*/session.json"))
259
+ )
260
+ environment_receipts = _read_objects(
261
+ sorted((state_root / "runtime" / "environment-receipts").glob("*.json"))
262
+ )
263
+ invalidations = _read_objects(
264
+ sorted((state_root / "runtime" / "invalidations").glob("*.json"))
265
+ )
266
+ return build_efficiency_summary(
267
+ run_sessions=run_sessions,
268
+ environment_receipts=environment_receipts,
269
+ invalidations=invalidations,
270
+ )
271
+
272
+
273
+ def main(argv: list[str] | None = None) -> int:
274
+ parser = argparse.ArgumentParser(prog="harness_efficiency.py")
275
+ parser.add_argument("summary", nargs="?")
276
+ parser.add_argument("--change-dir", required=True)
277
+ parser.add_argument("--out")
278
+ args = parser.parse_args(argv)
279
+ result = collect_efficiency_summary(Path(args.change_dir))
280
+ text = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
281
+ if args.out:
282
+ path = Path(args.out)
283
+ path.parent.mkdir(parents=True, exist_ok=True)
284
+ path.write_text(text, encoding="utf-8", newline="\n")
285
+ print(text, end="")
286
+ return 0
287
+
288
+
289
+ if __name__ == "__main__":
290
+ raise SystemExit(main())