@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
@@ -6,10 +6,30 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import fnmatch
8
8
  import json
9
+ import time
9
10
  from pathlib import Path
10
11
  from typing import Any
11
12
 
12
13
 
14
+ _COVERAGE_RANK = {"incremental": 0, "module": 1, "module-am": 2, "full": 3}
15
+
16
+
17
+ def _normalize_targets(raw_targets: Any) -> list[dict[str, Any]]:
18
+ if isinstance(raw_targets, dict):
19
+ return [
20
+ {**dict(target), "id": str(target.get("id") or target_id)}
21
+ for target_id, target in raw_targets.items()
22
+ if isinstance(target_id, str) and isinstance(target, dict)
23
+ ]
24
+ if isinstance(raw_targets, list):
25
+ return [
26
+ dict(target)
27
+ for target in raw_targets
28
+ if isinstance(target, dict) and str(target.get("id") or "").strip()
29
+ ]
30
+ return []
31
+
32
+
13
33
  def select_verifications(payload: dict[str, Any]) -> dict[str, Any]:
14
34
  changed = sorted(
15
35
  {
@@ -18,9 +38,7 @@ def select_verifications(payload: dict[str, Any]) -> dict[str, Any]:
18
38
  if str(path).strip()
19
39
  }
20
40
  )
21
- targets = [
22
- target for target in payload.get("targets", []) if isinstance(target, dict)
23
- ]
41
+ targets = _normalize_targets(payload.get("targets", []))
24
42
  selected_ids: set[str] = set()
25
43
  reasons: dict[str, list[str]] = {}
26
44
  for target in targets:
@@ -90,16 +108,398 @@ def select_verifications(payload: dict[str, Any]) -> dict[str, Any]:
90
108
  }
91
109
 
92
110
 
111
+ def _resource_locks(target: dict[str, Any]) -> list[str]:
112
+ locks = sorted(
113
+ {
114
+ str(item).strip()
115
+ for item in target.get("resourceLocks", [])
116
+ if str(item).strip()
117
+ }
118
+ )
119
+ estimated = float(
120
+ target.get("estimatedDurationSeconds")
121
+ or target.get("expectedDurationSeconds")
122
+ or 0
123
+ )
124
+ if estimated >= 60 and not locks:
125
+ locks.append("harness:unclassified-heavy")
126
+ return locks
127
+
128
+
129
+ def _target_decision(
130
+ target: dict[str, Any],
131
+ *,
132
+ product_identity: str,
133
+ frozen_identity: str,
134
+ available_capabilities: set[str],
135
+ verification_ledger: list[dict[str, Any]],
136
+ ) -> tuple[str, list[str]]:
137
+ blockers: list[str] = []
138
+ if not product_identity:
139
+ return "BLOCKED", ["PRODUCT_IDENTITY_REQUIRED"]
140
+ required_capabilities = {
141
+ str(item).strip()
142
+ for item in target.get("requiredCapabilities", [])
143
+ if str(item).strip()
144
+ }
145
+ blockers.extend(
146
+ "CAPABILITY_MISSING:" + item
147
+ for item in sorted(required_capabilities - available_capabilities)
148
+ )
149
+ required_coverage = str(target.get("requiredCoverage") or "").strip()
150
+ coverage_level = str(
151
+ target.get("coverageLevel") or target.get("scope") or ""
152
+ ).strip()
153
+ if required_coverage:
154
+ if required_coverage not in _COVERAGE_RANK:
155
+ blockers.append("REQUIRED_COVERAGE_INVALID")
156
+ elif coverage_level not in _COVERAGE_RANK:
157
+ blockers.append("COVERAGE_DECLARATION_REQUIRED")
158
+ elif _COVERAGE_RANK[coverage_level] < _COVERAGE_RANK[required_coverage]:
159
+ blockers.append(
160
+ f"COVERAGE_INSUFFICIENT:{coverage_level}<{required_coverage}"
161
+ )
162
+ applicability = target.get("applicability")
163
+ if isinstance(applicability, dict) and applicability.get("applicable") is False:
164
+ return "SKIP", [
165
+ str(applicability.get("reasonCode") or "NOT_APPLICABLE")
166
+ ]
167
+ if (
168
+ target.get("requiresFrozenIdentity") is True
169
+ and not frozen_identity
170
+ ):
171
+ blockers.append("FROZEN_IDENTITY_REQUIRED")
172
+ if (
173
+ target.get("requiresFrozenIdentity") is True
174
+ and frozen_identity
175
+ and product_identity != frozen_identity
176
+ ):
177
+ blockers.append("FROZEN_IDENTITY_DRIFT")
178
+ if blockers:
179
+ return "BLOCKED", blockers
180
+ reuse = target.get("reuse")
181
+ if isinstance(reuse, dict) and reuse.get("eligible") is True:
182
+ if target.get("reusePolicy") == "never":
183
+ return "EXECUTE", ["REUSE_POLICY_NEVER"]
184
+ evidence_id = str(reuse.get("evidenceId") or "").strip()
185
+ evidence_product = str(reuse.get("productIdentity") or "").strip()
186
+ target_id = str(target.get("id") or "")
187
+ ledger_match = next(
188
+ (
189
+ item
190
+ for item in verification_ledger
191
+ if str(item.get("evidenceId") or "") == evidence_id
192
+ and str(item.get("targetId") or item.get("verification") or "")
193
+ == target_id
194
+ and str(item.get("productIdentity") or "") == product_identity
195
+ and item.get("status") == "OK"
196
+ ),
197
+ None,
198
+ )
199
+ if (
200
+ str(target.get("reusePolicy") or "") != "ledger-exact"
201
+ or not evidence_id
202
+ or evidence_product != product_identity
203
+ or ledger_match is None
204
+ ):
205
+ return "BLOCKED", ["REUSE_EVIDENCE_INVALID"]
206
+ return "REUSE", [str(reuse.get("reasonCode") or "REUSE_ELIGIBLE")]
207
+ return "EXECUTE", ["SELECTED_FOR_EXECUTION"]
208
+
209
+
210
+ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
211
+ """Build an explainable, lock-aware verification execution plan.
212
+
213
+ Dependencies define the earliest wave. Nodes in the same wave may run in
214
+ parallel only when their declared resource locks are disjoint. Long tasks
215
+ without a lock declaration receive one shared conservative lock.
216
+ """
217
+ targets = _normalize_targets(payload.get("targets", []))
218
+ if not targets:
219
+ return {
220
+ "ok": False,
221
+ "code": "VERIFICATION_TARGETS_REQUIRED",
222
+ "productIdentity": str(payload.get("productIdentity") or ""),
223
+ "frozenIdentity": str(payload.get("frozenIdentity") or ""),
224
+ "plan": [],
225
+ "waves": [],
226
+ }
227
+ targets.sort(key=lambda item: str(item["id"]))
228
+ by_id = {str(item["id"]): item for item in targets}
229
+ product_identity = str(payload.get("productIdentity") or "")
230
+ frozen_identity = str(payload.get("frozenIdentity") or "")
231
+ available_capabilities = {
232
+ str(item).strip()
233
+ for item in payload.get("availableCapabilities", [])
234
+ if str(item).strip()
235
+ }
236
+ verification_ledger = [
237
+ dict(item)
238
+ for item in payload.get("verificationLedger", [])
239
+ if isinstance(item, dict)
240
+ ]
241
+ decisions: dict[str, tuple[str, list[str]]] = {
242
+ target_id: _target_decision(
243
+ target,
244
+ product_identity=product_identity,
245
+ frozen_identity=frozen_identity,
246
+ available_capabilities=available_capabilities,
247
+ verification_ledger=verification_ledger,
248
+ )
249
+ for target_id, target in by_id.items()
250
+ }
251
+ waves: list[list[str]] = []
252
+ placed: dict[str, int | None] = {}
253
+ pending = set(by_id)
254
+
255
+ while pending:
256
+ progressed = False
257
+ for target_id in sorted(pending):
258
+ target = by_id[target_id]
259
+ dependencies = [str(item) for item in target.get("dependsOn", [])]
260
+ unknown = sorted(set(dependencies) - set(by_id))
261
+ if unknown:
262
+ decisions[target_id] = (
263
+ "BLOCKED",
264
+ ["DEPENDENCY_UNKNOWN:" + item for item in unknown],
265
+ )
266
+ placed[target_id] = None
267
+ pending.remove(target_id)
268
+ progressed = True
269
+ break
270
+ if any(item not in placed for item in dependencies):
271
+ continue
272
+ failed_dependencies = [
273
+ item
274
+ for item in dependencies
275
+ if decisions[item][0] in {"SKIP", "BLOCKED"}
276
+ ]
277
+ if failed_dependencies:
278
+ decisions[target_id] = (
279
+ "BLOCKED",
280
+ [
281
+ "DEPENDENCY_BLOCKED:" + item
282
+ for item in sorted(failed_dependencies)
283
+ ],
284
+ )
285
+ placed[target_id] = None
286
+ pending.remove(target_id)
287
+ progressed = True
288
+ break
289
+ decision, _ = decisions[target_id]
290
+ if decision in {"SKIP", "BLOCKED"}:
291
+ placed[target_id] = None
292
+ pending.remove(target_id)
293
+ progressed = True
294
+ break
295
+ dependency_waves = [
296
+ placed[item]
297
+ for item in dependencies
298
+ if placed.get(item) is not None
299
+ ]
300
+ earliest = (
301
+ max(int(item) for item in dependency_waves) + 1
302
+ if dependency_waves
303
+ else 0
304
+ )
305
+ if decision == "REUSE":
306
+ placed[target_id] = max(0, earliest - 1)
307
+ pending.remove(target_id)
308
+ progressed = True
309
+ break
310
+ locks = set(_resource_locks(target))
311
+ wave = earliest
312
+ while True:
313
+ while wave >= len(waves):
314
+ waves.append([])
315
+ occupied = {
316
+ lock
317
+ for item_id in waves[wave]
318
+ for lock in _resource_locks(by_id[item_id])
319
+ }
320
+ if not locks & occupied:
321
+ waves[wave].append(target_id)
322
+ placed[target_id] = wave
323
+ break
324
+ wave += 1
325
+ pending.remove(target_id)
326
+ progressed = True
327
+ break
328
+ if not progressed:
329
+ for target_id in sorted(pending):
330
+ decisions[target_id] = ("BLOCKED", ["DEPENDENCY_CYCLE"])
331
+ placed[target_id] = None
332
+ pending.clear()
333
+
334
+ plan: list[dict[str, Any]] = []
335
+ for target_id, target in by_id.items():
336
+ decision, reasons = decisions[target_id]
337
+ dependencies = [str(item) for item in target.get("dependsOn", [])]
338
+ reason_codes = [
339
+ *reasons,
340
+ *["depends-on:" + item for item in dependencies],
341
+ ]
342
+ plan.append(
343
+ {
344
+ **target,
345
+ "id": target_id,
346
+ "decision": decision,
347
+ "reasonCodes": reason_codes,
348
+ "resourceLocks": _resource_locks(target),
349
+ "wave": placed[target_id],
350
+ }
351
+ )
352
+ return {
353
+ "ok": all(item["decision"] != "BLOCKED" for item in plan),
354
+ "code": (
355
+ "VERIFICATION_PLAN_READY"
356
+ if all(item["decision"] != "BLOCKED" for item in plan)
357
+ else "VERIFICATION_PLAN_BLOCKED"
358
+ ),
359
+ "productIdentity": product_identity,
360
+ "frozenIdentity": frozen_identity,
361
+ "plan": plan,
362
+ "waves": [
363
+ {"wave": index, "targets": target_ids}
364
+ for index, target_ids in enumerate(waves)
365
+ if target_ids
366
+ ],
367
+ }
368
+
369
+
370
+ def execute_verifications(payload: dict[str, Any]) -> dict[str, Any]:
371
+ """Execute a scheduled graph wave-by-wave through managed run sessions."""
372
+ import harness_runtime
373
+
374
+ scheduled = schedule_verifications(payload)
375
+ if not scheduled["ok"]:
376
+ return {**scheduled, "execution": []}
377
+ state_root = Path(str(payload.get("stateRoot") or "")).expanduser().resolve()
378
+ working_directory = Path(
379
+ str(payload.get("workingDirectory") or "")
380
+ ).expanduser().resolve()
381
+ if not str(payload.get("stateRoot") or "").strip():
382
+ return {
383
+ **scheduled,
384
+ "ok": False,
385
+ "code": "VERIFICATION_STATE_ROOT_REQUIRED",
386
+ "execution": [],
387
+ }
388
+ if not working_directory.is_dir():
389
+ return {
390
+ **scheduled,
391
+ "ok": False,
392
+ "code": "VERIFICATION_WORKDIR_INVALID",
393
+ "execution": [],
394
+ }
395
+ by_id = {str(item["id"]): item for item in scheduled["plan"]}
396
+ outcomes: dict[str, dict[str, Any]] = {
397
+ target_id: {
398
+ "targetId": target_id,
399
+ "status": "OK",
400
+ "reasonCode": "VERIFICATION_REUSED",
401
+ }
402
+ for target_id, target in by_id.items()
403
+ if target.get("decision") == "REUSE"
404
+ }
405
+ execution: list[dict[str, Any]] = list(outcomes.values())
406
+ for wave in scheduled["waves"]:
407
+ running: list[tuple[str, str]] = []
408
+ for target_id in wave["targets"]:
409
+ target = by_id[target_id]
410
+ failed_dependencies = [
411
+ dependency
412
+ for dependency in target.get("dependsOn", [])
413
+ if outcomes.get(str(dependency), {}).get("status") != "OK"
414
+ ]
415
+ if failed_dependencies:
416
+ outcome = {
417
+ "targetId": target_id,
418
+ "status": "BLOCKED",
419
+ "reasonCode": "DEPENDENCY_EXECUTION_FAILED",
420
+ "blockedBy": failed_dependencies,
421
+ }
422
+ outcomes[target_id] = outcome
423
+ execution.append(outcome)
424
+ continue
425
+ argv = target.get("argvTemplate")
426
+ if (
427
+ not isinstance(argv, list)
428
+ or not argv
429
+ or not all(isinstance(item, str) and item for item in argv)
430
+ or any("{" in item or "}" in item for item in argv)
431
+ ):
432
+ outcome = {
433
+ "targetId": target_id,
434
+ "status": "BLOCKED",
435
+ "reasonCode": "VERIFICATION_ARGV_NOT_CONCRETE",
436
+ }
437
+ outcomes[target_id] = outcome
438
+ execution.append(outcome)
439
+ continue
440
+ receipt = harness_runtime.start_run_session(
441
+ state_root=state_root,
442
+ verification=target_id,
443
+ argv=argv,
444
+ working_directory=working_directory,
445
+ timeout_seconds=target.get("timeoutSeconds"),
446
+ expected_duration_seconds=target.get(
447
+ "estimatedDurationSeconds"
448
+ ),
449
+ product_identity=scheduled["productIdentity"],
450
+ resource_locks=list(target["resourceLocks"]),
451
+ )
452
+ if receipt.get("status") in harness_runtime.RUN_TERMINAL_STATUSES:
453
+ outcome = {"targetId": target_id, **receipt}
454
+ outcomes[target_id] = outcome
455
+ execution.append(outcome)
456
+ else:
457
+ running.append((target_id, str(receipt["sessionId"])))
458
+ while running:
459
+ remaining: list[tuple[str, str]] = []
460
+ for target_id, session_id in running:
461
+ receipt = harness_runtime.run_session_status(state_root, session_id)
462
+ if receipt.get("status") in harness_runtime.RUN_TERMINAL_STATUSES:
463
+ outcome = {"targetId": target_id, **receipt}
464
+ outcomes[target_id] = outcome
465
+ execution.append(outcome)
466
+ else:
467
+ remaining.append((target_id, session_id))
468
+ running = remaining
469
+ if running:
470
+ time.sleep(0.05)
471
+ execution_ok = all(item.get("status") == "OK" for item in execution)
472
+ return {
473
+ **scheduled,
474
+ "ok": execution_ok,
475
+ "code": (
476
+ "VERIFICATION_EXECUTION_OK"
477
+ if execution_ok
478
+ else "VERIFICATION_EXECUTION_FAILED"
479
+ ),
480
+ "execution": execution,
481
+ }
482
+
483
+
93
484
  def main(argv: list[str] | None = None) -> int:
94
485
  parser = argparse.ArgumentParser(prog="harness_verification.py")
95
- parser.add_argument("select", nargs="?")
486
+ parser.add_argument(
487
+ "command",
488
+ nargs="?",
489
+ choices=("select", "plan", "execute"),
490
+ default="select",
491
+ )
96
492
  parser.add_argument("--input", required=True, type=Path)
97
493
  parser.add_argument("--out", type=Path)
98
494
  parser.add_argument("--json", action="store_true")
99
495
  args = parser.parse_args(argv)
100
- result = select_verifications(
101
- json.loads(args.input.read_text(encoding="utf-8-sig"))
102
- )
496
+ payload = json.loads(args.input.read_text(encoding="utf-8-sig"))
497
+ if args.command == "execute":
498
+ result = execute_verifications(payload)
499
+ elif args.command == "plan":
500
+ result = schedule_verifications(payload)
501
+ else:
502
+ result = select_verifications(payload)
103
503
  if args.out:
104
504
  args.out.parent.mkdir(parents=True, exist_ok=True)
105
505
  args.out.write_text(
@@ -107,7 +507,7 @@ def main(argv: list[str] | None = None) -> int:
107
507
  encoding="utf-8",
108
508
  )
109
509
  print(json.dumps(result, ensure_ascii=False, indent=2))
110
- return 0
510
+ return 0 if result.get("ok") else 1
111
511
 
112
512
 
113
513
  if __name__ == "__main__":