@hunter-harness/workflow-harness 0.2.43 → 0.2.45

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 (274) 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 +100 -0
  3. package/harness/bundles/general/claude-code/contracts/fixtures/managed-execution.json +341 -0
  4. package/harness/bundles/general/claude-code/contracts/process-identity.schema.json +132 -0
  5. package/harness/bundles/general/claude-code/contracts/run-session.schema.json +142 -0
  6. package/harness/bundles/general/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
  7. package/harness/bundles/general/claude-code/contracts/service-session.schema.json +147 -0
  8. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  10. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  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-submit/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/harness-submit/checklist.md +12 -0
  17. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  18. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  19. package/harness/bundles/general/claude-code/scripts/harness_archive.py +324 -28
  20. package/harness/bundles/general/claude-code/scripts/harness_change.py +137 -2
  21. package/harness/bundles/general/claude-code/scripts/harness_efficiency.py +153 -0
  22. package/harness/bundles/general/claude-code/scripts/harness_environment.py +55 -25
  23. package/harness/bundles/general/claude-code/scripts/harness_execution_contracts.py +596 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_integration.py +289 -52
  25. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +5 -0
  26. package/harness/bundles/general/claude-code/scripts/harness_orchestration.py +344 -0
  27. package/harness/bundles/general/claude-code/scripts/harness_process.py +1065 -0
  28. package/harness/bundles/general/claude-code/scripts/harness_profile.py +244 -4
  29. package/harness/bundles/general/claude-code/scripts/harness_runtime.py +646 -132
  30. package/harness/bundles/general/claude-code/scripts/harness_service.py +1469 -194
  31. package/harness/bundles/general/claude-code/scripts/harness_test_runner.py +107 -117
  32. package/harness/bundles/general/claude-code/scripts/harness_verification.py +79 -0
  33. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  34. package/harness/bundles/general/codebuddy/contracts/build-profile-v3.schema.json +100 -0
  35. package/harness/bundles/general/codebuddy/contracts/fixtures/managed-execution.json +341 -0
  36. package/harness/bundles/general/codebuddy/contracts/process-identity.schema.json +132 -0
  37. package/harness/bundles/general/codebuddy/contracts/run-session.schema.json +142 -0
  38. package/harness/bundles/general/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
  39. package/harness/bundles/general/codebuddy/contracts/service-session.schema.json +147 -0
  40. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  44. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
  45. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  46. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  47. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  48. package/harness/bundles/general/codebuddy/harness-submit/checklist.md +12 -0
  49. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  50. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  51. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +324 -28
  52. package/harness/bundles/general/codebuddy/scripts/harness_change.py +137 -2
  53. package/harness/bundles/general/codebuddy/scripts/harness_efficiency.py +153 -0
  54. package/harness/bundles/general/codebuddy/scripts/harness_environment.py +55 -25
  55. package/harness/bundles/general/codebuddy/scripts/harness_execution_contracts.py +596 -0
  56. package/harness/bundles/general/codebuddy/scripts/harness_integration.py +289 -52
  57. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +5 -0
  58. package/harness/bundles/general/codebuddy/scripts/harness_orchestration.py +344 -0
  59. package/harness/bundles/general/codebuddy/scripts/harness_process.py +1065 -0
  60. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +244 -4
  61. package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +646 -132
  62. package/harness/bundles/general/codebuddy/scripts/harness_service.py +1469 -194
  63. package/harness/bundles/general/codebuddy/scripts/harness_test_runner.py +107 -117
  64. package/harness/bundles/general/codebuddy/scripts/harness_verification.py +79 -0
  65. package/harness/bundles/general/codex/.harness-build.json +1 -1
  66. package/harness/bundles/general/codex/contracts/build-profile-v3.schema.json +100 -0
  67. package/harness/bundles/general/codex/contracts/fixtures/managed-execution.json +341 -0
  68. package/harness/bundles/general/codex/contracts/process-identity.schema.json +132 -0
  69. package/harness/bundles/general/codex/contracts/run-session.schema.json +142 -0
  70. package/harness/bundles/general/codex/contracts/service-retirement-receipt.schema.json +85 -0
  71. package/harness/bundles/general/codex/contracts/service-session.schema.json +147 -0
  72. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  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 +1 -1
  77. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  78. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  79. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  80. package/harness/bundles/general/codex/harness-submit/checklist.md +12 -0
  81. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  82. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  83. package/harness/bundles/general/codex/scripts/harness_archive.py +324 -28
  84. package/harness/bundles/general/codex/scripts/harness_change.py +137 -2
  85. package/harness/bundles/general/codex/scripts/harness_efficiency.py +153 -0
  86. package/harness/bundles/general/codex/scripts/harness_environment.py +55 -25
  87. package/harness/bundles/general/codex/scripts/harness_execution_contracts.py +596 -0
  88. package/harness/bundles/general/codex/scripts/harness_integration.py +289 -52
  89. package/harness/bundles/general/codex/scripts/harness_ledger.py +5 -0
  90. package/harness/bundles/general/codex/scripts/harness_orchestration.py +344 -0
  91. package/harness/bundles/general/codex/scripts/harness_process.py +1065 -0
  92. package/harness/bundles/general/codex/scripts/harness_profile.py +244 -4
  93. package/harness/bundles/general/codex/scripts/harness_runtime.py +646 -132
  94. package/harness/bundles/general/codex/scripts/harness_service.py +1469 -194
  95. package/harness/bundles/general/codex/scripts/harness_test_runner.py +107 -117
  96. package/harness/bundles/general/codex/scripts/harness_verification.py +79 -0
  97. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  98. package/harness/bundles/general/cursor/contracts/build-profile-v3.schema.json +100 -0
  99. package/harness/bundles/general/cursor/contracts/fixtures/managed-execution.json +341 -0
  100. package/harness/bundles/general/cursor/contracts/process-identity.schema.json +132 -0
  101. package/harness/bundles/general/cursor/contracts/run-session.schema.json +142 -0
  102. package/harness/bundles/general/cursor/contracts/service-retirement-receipt.schema.json +85 -0
  103. package/harness/bundles/general/cursor/contracts/service-session.schema.json +147 -0
  104. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  105. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  106. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  107. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  108. package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
  109. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  110. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  111. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  112. package/harness/bundles/general/cursor/harness-submit/checklist.md +12 -0
  113. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  114. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  115. package/harness/bundles/general/cursor/scripts/harness_archive.py +324 -28
  116. package/harness/bundles/general/cursor/scripts/harness_change.py +137 -2
  117. package/harness/bundles/general/cursor/scripts/harness_efficiency.py +153 -0
  118. package/harness/bundles/general/cursor/scripts/harness_environment.py +55 -25
  119. package/harness/bundles/general/cursor/scripts/harness_execution_contracts.py +596 -0
  120. package/harness/bundles/general/cursor/scripts/harness_integration.py +289 -52
  121. package/harness/bundles/general/cursor/scripts/harness_ledger.py +5 -0
  122. package/harness/bundles/general/cursor/scripts/harness_orchestration.py +344 -0
  123. package/harness/bundles/general/cursor/scripts/harness_process.py +1065 -0
  124. package/harness/bundles/general/cursor/scripts/harness_profile.py +244 -4
  125. package/harness/bundles/general/cursor/scripts/harness_runtime.py +646 -132
  126. package/harness/bundles/general/cursor/scripts/harness_service.py +1469 -194
  127. package/harness/bundles/general/cursor/scripts/harness_test_runner.py +107 -117
  128. package/harness/bundles/general/cursor/scripts/harness_verification.py +79 -0
  129. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  130. package/harness/bundles/java/claude-code/contracts/build-profile-v3.schema.json +100 -0
  131. package/harness/bundles/java/claude-code/contracts/fixtures/managed-execution.json +341 -0
  132. package/harness/bundles/java/claude-code/contracts/process-identity.schema.json +132 -0
  133. package/harness/bundles/java/claude-code/contracts/run-session.schema.json +142 -0
  134. package/harness/bundles/java/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
  135. package/harness/bundles/java/claude-code/contracts/service-session.schema.json +147 -0
  136. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  137. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  138. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  139. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  140. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  141. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  142. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
  143. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  144. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  145. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  146. package/harness/bundles/java/claude-code/harness-submit/checklist.md +12 -0
  147. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  148. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  149. package/harness/bundles/java/claude-code/scripts/harness_archive.py +324 -28
  150. package/harness/bundles/java/claude-code/scripts/harness_change.py +137 -2
  151. package/harness/bundles/java/claude-code/scripts/harness_efficiency.py +153 -0
  152. package/harness/bundles/java/claude-code/scripts/harness_environment.py +55 -25
  153. package/harness/bundles/java/claude-code/scripts/harness_execution_contracts.py +596 -0
  154. package/harness/bundles/java/claude-code/scripts/harness_integration.py +289 -52
  155. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +5 -0
  156. package/harness/bundles/java/claude-code/scripts/harness_orchestration.py +344 -0
  157. package/harness/bundles/java/claude-code/scripts/harness_process.py +1065 -0
  158. package/harness/bundles/java/claude-code/scripts/harness_profile.py +244 -4
  159. package/harness/bundles/java/claude-code/scripts/harness_runtime.py +646 -132
  160. package/harness/bundles/java/claude-code/scripts/harness_service.py +1469 -194
  161. package/harness/bundles/java/claude-code/scripts/harness_test_runner.py +107 -117
  162. package/harness/bundles/java/claude-code/scripts/harness_verification.py +79 -0
  163. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  164. package/harness/bundles/java/codebuddy/contracts/build-profile-v3.schema.json +100 -0
  165. package/harness/bundles/java/codebuddy/contracts/fixtures/managed-execution.json +341 -0
  166. package/harness/bundles/java/codebuddy/contracts/process-identity.schema.json +132 -0
  167. package/harness/bundles/java/codebuddy/contracts/run-session.schema.json +142 -0
  168. package/harness/bundles/java/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
  169. package/harness/bundles/java/codebuddy/contracts/service-session.schema.json +147 -0
  170. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  171. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  172. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  173. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  174. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  175. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  176. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
  177. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  178. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  179. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  180. package/harness/bundles/java/codebuddy/harness-submit/checklist.md +12 -0
  181. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  182. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  183. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +324 -28
  184. package/harness/bundles/java/codebuddy/scripts/harness_change.py +137 -2
  185. package/harness/bundles/java/codebuddy/scripts/harness_efficiency.py +153 -0
  186. package/harness/bundles/java/codebuddy/scripts/harness_environment.py +55 -25
  187. package/harness/bundles/java/codebuddy/scripts/harness_execution_contracts.py +596 -0
  188. package/harness/bundles/java/codebuddy/scripts/harness_integration.py +289 -52
  189. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +5 -0
  190. package/harness/bundles/java/codebuddy/scripts/harness_orchestration.py +344 -0
  191. package/harness/bundles/java/codebuddy/scripts/harness_process.py +1065 -0
  192. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +244 -4
  193. package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +646 -132
  194. package/harness/bundles/java/codebuddy/scripts/harness_service.py +1469 -194
  195. package/harness/bundles/java/codebuddy/scripts/harness_test_runner.py +107 -117
  196. package/harness/bundles/java/codebuddy/scripts/harness_verification.py +79 -0
  197. package/harness/bundles/java/codex/.harness-build.json +1 -1
  198. package/harness/bundles/java/codex/contracts/build-profile-v3.schema.json +100 -0
  199. package/harness/bundles/java/codex/contracts/fixtures/managed-execution.json +341 -0
  200. package/harness/bundles/java/codex/contracts/process-identity.schema.json +132 -0
  201. package/harness/bundles/java/codex/contracts/run-session.schema.json +142 -0
  202. package/harness/bundles/java/codex/contracts/service-retirement-receipt.schema.json +85 -0
  203. package/harness/bundles/java/codex/contracts/service-session.schema.json +147 -0
  204. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  205. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  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 +1 -1
  210. package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
  211. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  212. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  213. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  214. package/harness/bundles/java/codex/harness-submit/checklist.md +12 -0
  215. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  216. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  217. package/harness/bundles/java/codex/scripts/harness_archive.py +324 -28
  218. package/harness/bundles/java/codex/scripts/harness_change.py +137 -2
  219. package/harness/bundles/java/codex/scripts/harness_efficiency.py +153 -0
  220. package/harness/bundles/java/codex/scripts/harness_environment.py +55 -25
  221. package/harness/bundles/java/codex/scripts/harness_execution_contracts.py +596 -0
  222. package/harness/bundles/java/codex/scripts/harness_integration.py +289 -52
  223. package/harness/bundles/java/codex/scripts/harness_ledger.py +5 -0
  224. package/harness/bundles/java/codex/scripts/harness_orchestration.py +344 -0
  225. package/harness/bundles/java/codex/scripts/harness_process.py +1065 -0
  226. package/harness/bundles/java/codex/scripts/harness_profile.py +244 -4
  227. package/harness/bundles/java/codex/scripts/harness_runtime.py +646 -132
  228. package/harness/bundles/java/codex/scripts/harness_service.py +1469 -194
  229. package/harness/bundles/java/codex/scripts/harness_test_runner.py +107 -117
  230. package/harness/bundles/java/codex/scripts/harness_verification.py +79 -0
  231. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  232. package/harness/bundles/java/cursor/contracts/build-profile-v3.schema.json +100 -0
  233. package/harness/bundles/java/cursor/contracts/fixtures/managed-execution.json +341 -0
  234. package/harness/bundles/java/cursor/contracts/process-identity.schema.json +132 -0
  235. package/harness/bundles/java/cursor/contracts/run-session.schema.json +142 -0
  236. package/harness/bundles/java/cursor/contracts/service-retirement-receipt.schema.json +85 -0
  237. package/harness/bundles/java/cursor/contracts/service-session.schema.json +147 -0
  238. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  239. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  240. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  241. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  242. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  243. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  244. package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
  245. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  246. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  247. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  248. package/harness/bundles/java/cursor/harness-submit/checklist.md +12 -0
  249. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  250. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  251. package/harness/bundles/java/cursor/scripts/harness_archive.py +324 -28
  252. package/harness/bundles/java/cursor/scripts/harness_change.py +137 -2
  253. package/harness/bundles/java/cursor/scripts/harness_efficiency.py +153 -0
  254. package/harness/bundles/java/cursor/scripts/harness_environment.py +55 -25
  255. package/harness/bundles/java/cursor/scripts/harness_execution_contracts.py +596 -0
  256. package/harness/bundles/java/cursor/scripts/harness_integration.py +289 -52
  257. package/harness/bundles/java/cursor/scripts/harness_ledger.py +5 -0
  258. package/harness/bundles/java/cursor/scripts/harness_orchestration.py +344 -0
  259. package/harness/bundles/java/cursor/scripts/harness_process.py +1065 -0
  260. package/harness/bundles/java/cursor/scripts/harness_profile.py +244 -4
  261. package/harness/bundles/java/cursor/scripts/harness_runtime.py +646 -132
  262. package/harness/bundles/java/cursor/scripts/harness_service.py +1469 -194
  263. package/harness/bundles/java/cursor/scripts/harness_test_runner.py +107 -117
  264. package/harness/bundles/java/cursor/scripts/harness_verification.py +79 -0
  265. package/harness/manifests/general/claude-code.json +58 -26
  266. package/harness/manifests/general/codebuddy.json +58 -26
  267. package/harness/manifests/general/codex.json +58 -26
  268. package/harness/manifests/general/cursor.json +58 -26
  269. package/harness/manifests/java/claude-code.json +60 -28
  270. package/harness/manifests/java/codebuddy.json +60 -28
  271. package/harness/manifests/java/codex.json +60 -28
  272. package/harness/manifests/java/cursor.json +60 -28
  273. package/hunter-workflow-family.json +4 -4
  274. package/package.json +1 -1
@@ -29,6 +29,7 @@ if str(SCRIPT_DIR) not in sys.path:
29
29
  sys.path.insert(0, str(SCRIPT_DIR))
30
30
 
31
31
  import harness_environment as henv
32
+ import harness_process as hprocess
32
33
 
33
34
  DEFAULT_MAX_WORKERS = 2
34
35
  DEFAULT_TIMEOUT_SECONDS = 300.0
@@ -139,35 +140,30 @@ def build_unittest_plan(
139
140
 
140
141
 
141
142
  def _pid_is_running(pid: int) -> bool:
142
- if pid <= 0:
143
- return False
144
- if pid == os.getpid():
145
- return True
146
- if os.name == "nt":
147
- process_query_limited_information = 0x1000
148
- kernel32 = ctypes.windll.kernel32
149
- kernel32.OpenProcess.restype = ctypes.c_void_p
150
- handle = kernel32.OpenProcess(
151
- process_query_limited_information,
152
- False,
153
- pid,
154
- )
155
- if not handle:
156
- return False
157
- try:
158
- exit_code = ctypes.c_uint32()
159
- if not kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)):
160
- return False
161
- return int(exit_code.value) == 259
162
- finally:
163
- kernel32.CloseHandle(ctypes.c_void_p(handle))
143
+ return hprocess.is_pid_alive(pid)
144
+
145
+
146
+ def _verify_recorded_owner(owner: Mapping[str, object]) -> bool | None:
147
+ """Verify a legacy lock owner through the canonical process provider."""
148
+
164
149
  try:
165
- os.kill(pid, 0)
166
- except ProcessLookupError:
167
- return False
168
- except PermissionError:
150
+ pid = int(owner.get("pid") or 0)
151
+ except (TypeError, ValueError):
152
+ return None
153
+ observed = hprocess.observe_process_identity(pid)
154
+ decision = hprocess.verify_process_identity(
155
+ {
156
+ "pid": pid,
157
+ "createdAt": owner.get("startedAt"),
158
+ "executable": owner.get("executable"),
159
+ },
160
+ observed,
161
+ )
162
+ if decision.get("ok") is True:
169
163
  return True
170
- return True
164
+ if decision.get("reasonCode") == "IDENTITY_UNVERIFIABLE":
165
+ return None
166
+ return False
171
167
 
172
168
 
173
169
  def classify_test_lock(path: Path) -> dict[str, object]:
@@ -237,13 +233,7 @@ def classify_test_lock(path: Path) -> dict[str, object]:
237
233
  "token": payload.get("token"),
238
234
  "owner": owner,
239
235
  }
240
- verified = henv.hservice.verify_process_identity(
241
- {
242
- "pid": owner_pid,
243
- "startedAt": owner["startedAt"],
244
- "processIdentity": {"executable": owner["executable"]},
245
- }
246
- )
236
+ verified = _verify_recorded_owner(owner)
247
237
  classification = (
248
238
  "OWNER_ACTIVE"
249
239
  if verified is True
@@ -348,9 +338,9 @@ class TestRunLock:
348
338
  )
349
339
  else:
350
340
  now = time.time()
351
- created = henv.hservice.get_process_create_time(os.getpid())
341
+ created = hprocess.get_process_create_time(os.getpid())
352
342
  executable = (
353
- henv.hservice.get_process_executable(os.getpid())
343
+ hprocess.get_process_executable(os.getpid())
354
344
  or str(Path(sys.executable).resolve())
355
345
  )
356
346
  payload = {
@@ -794,35 +784,12 @@ def _terminate_process_tree(
794
784
  *,
795
785
  windows_job: _WindowsKillOnCloseJob | None,
796
786
  ) -> None:
787
+ """Legacy seam retained for callers; ownership must be provider-backed."""
788
+
797
789
  if windows_job is not None and windows_job.handle is not None:
798
790
  windows_job.terminate_and_wait()
799
- elif os.name == "nt":
800
- subprocess.run(
801
- ["taskkill", "/PID", str(process.pid), "/T", "/F"],
802
- stdout=subprocess.DEVNULL,
803
- stderr=subprocess.DEVNULL,
804
- check=False,
805
- timeout=10,
806
- creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
807
- )
808
791
  else:
809
- try:
810
- os.killpg(process.pid, signal.SIGTERM)
811
- except ProcessLookupError:
812
- pass
813
- try:
814
- process.wait(timeout=2)
815
- except subprocess.TimeoutExpired:
816
- try:
817
- os.killpg(process.pid, signal.SIGKILL)
818
- except ProcessLookupError:
819
- pass
820
- if process.poll() is None:
821
- process.kill()
822
- try:
823
- process.wait(timeout=5)
824
- except subprocess.TimeoutExpired:
825
- pass
792
+ raise RuntimeError("PROCESS_OWNERSHIP_REQUIRED")
826
793
 
827
794
 
828
795
  def _cleanup_descendants_after_success(
@@ -832,12 +799,7 @@ def _cleanup_descendants_after_success(
832
799
  ) -> bool:
833
800
  if windows_job is not None and windows_job.handle is not None:
834
801
  return windows_job.terminate_and_wait()
835
- if os.name != "nt":
836
- try:
837
- os.killpg(process.pid, signal.SIGTERM)
838
- except ProcessLookupError:
839
- pass
840
- return os.name != "nt"
802
+ return False
841
803
 
842
804
 
843
805
  def run_managed_command(
@@ -859,74 +821,84 @@ def run_managed_command(
859
821
  child_env.setdefault("PYTHONUNBUFFERED", "1")
860
822
  lower_process_priority()
861
823
 
862
- popen_kwargs: dict[str, object] = {
863
- "cwd": str(Path(cwd).resolve()),
864
- "env": child_env,
865
- "shell": False,
866
- }
867
824
  output_stream = tempfile.TemporaryFile(mode="w+b") if capture_output else None
868
- if output_stream is not None:
869
- popen_kwargs["stdout"] = output_stream
870
- popen_kwargs["stderr"] = subprocess.STDOUT
871
- if os.name == "nt":
872
- popen_kwargs["creationflags"] = getattr(
873
- subprocess,
874
- "CREATE_NEW_PROCESS_GROUP",
875
- 0,
876
- )
877
- else:
878
- popen_kwargs["start_new_session"] = True
879
-
880
825
  started = time.monotonic()
826
+ spawned = None
827
+ process: subprocess.Popen[object] | None = None
828
+ cleanup_complete = False
881
829
  try:
882
- process = subprocess.Popen(list(argv), **popen_kwargs)
883
- windows_job = (
884
- _WindowsKillOnCloseJob(allow_breakaway=allow_detached_processes)
885
- if os.name == "nt"
886
- else None
830
+ spawned = hprocess.spawn_structured_argv(
831
+ list(argv),
832
+ cwd=Path(cwd).resolve(),
833
+ environment=child_env,
834
+ owner_token=f"test-runner:{uuid.uuid4().hex}",
835
+ stdout=output_stream,
836
+ stderr=(subprocess.STDOUT if output_stream is not None else None),
887
837
  )
888
- isolated = bool(windows_job and windows_job.assign(process))
889
- known_windows_descendants: set[int] = set()
890
- windows_snapshot_ok = True
891
- if os.name != "nt":
892
- isolated = True
838
+ process = spawned.process
839
+ isolated = bool(
840
+ isinstance(spawned.ownershipProof, dict)
841
+ and spawned.ownershipProof.get("membersComplete") is True
842
+ )
843
+
844
+ def provider_cleanup() -> bool:
845
+ hprocess.capture_owned_members(spawned)
846
+ result = hprocess.terminate_owned_tree(
847
+ spawned.attestation,
848
+ spawned.ownershipProof,
849
+ {"graceSeconds": 3.0},
850
+ )
851
+ return bool(result.get("cleanupComplete"))
852
+
853
+ def wait_owned_members_gone() -> bool:
854
+ proof = spawned.ownershipProof
855
+ if not isinstance(proof, dict):
856
+ return False
857
+ members = proof.get("members")
858
+ if not isinstance(members, list):
859
+ return False
860
+ pids = {
861
+ int(item["pid"])
862
+ for item in members
863
+ if isinstance(item, dict) and isinstance(item.get("pid"), int)
864
+ }
865
+ deadline = time.monotonic() + 5.0
866
+ while pids and time.monotonic() < deadline:
867
+ pids = {pid for pid in pids if hprocess.is_pid_alive(pid)}
868
+ if pids:
869
+ time.sleep(0.05)
870
+ return not pids
871
+
893
872
  timed_out = False
894
873
  try:
895
874
  deadline = time.monotonic() + max(0.01, float(timeout_seconds))
896
875
  while True:
897
- if os.name == "nt":
898
- windows_snapshot_ok = (
899
- _update_windows_descendants(
900
- process.pid,
901
- known_windows_descendants,
902
- )
903
- and windows_snapshot_ok
904
- )
905
876
  polled = process.poll()
906
877
  if polled is not None:
907
878
  returncode = polled
908
879
  break
909
880
  if time.monotonic() >= deadline:
910
881
  timed_out = True
911
- _terminate_process_tree(process, windows_job=windows_job)
882
+ cleanup_complete = provider_cleanup()
912
883
  returncode = 124
913
884
  break
914
885
  time.sleep(0.05)
915
886
  except KeyboardInterrupt:
916
- _terminate_process_tree(process, windows_job=windows_job)
917
- if os.name == "nt":
918
- _terminate_tracked_windows_descendants(known_windows_descendants)
887
+ cleanup_complete = provider_cleanup()
919
888
  raise
920
- descendants_drained = True
921
889
  if not timed_out:
922
- descendants_drained = _cleanup_descendants_after_success(
923
- process,
924
- windows_job=windows_job,
925
- )
926
- if os.name == "nt":
927
- _update_windows_descendants(process.pid, known_windows_descendants)
928
- _terminate_tracked_windows_descendants(known_windows_descendants)
929
- isolated = isolated and windows_snapshot_ok and descendants_drained
890
+ cleanup_complete = provider_cleanup()
891
+ # A named Windows Job remains an owned capability after the leader
892
+ # exits. Closing that provider handle drains any attested children;
893
+ # PID-only or POSIX group fallbacks stay fail-closed.
894
+ if not cleanup_complete and os.name == "nt":
895
+ spawned.close()
896
+ cleanup_complete = wait_owned_members_gone()
897
+ if process.poll() is None:
898
+ try:
899
+ process.wait(timeout=5)
900
+ except subprocess.TimeoutExpired:
901
+ cleanup_complete = False
930
902
  duration = time.monotonic() - started
931
903
  output_tail = ""
932
904
  if output_stream is not None:
@@ -939,10 +911,28 @@ def run_managed_command(
939
911
  returncode=returncode,
940
912
  timed_out=timed_out,
941
913
  duration_seconds=duration,
942
- process_tree_isolated=isolated,
914
+ process_tree_isolated=isolated and cleanup_complete,
943
915
  output_tail=output_tail,
944
916
  )
945
917
  finally:
918
+ if (
919
+ not cleanup_complete
920
+ and process is not None
921
+ and process.poll() is None
922
+ and spawned is not None
923
+ ):
924
+ try:
925
+ hprocess.capture_owned_members(spawned)
926
+ cleanup = hprocess.terminate_owned_tree(
927
+ spawned.attestation,
928
+ spawned.ownershipProof,
929
+ {"graceSeconds": 3.0},
930
+ )
931
+ cleanup_complete = bool(cleanup.get("cleanupComplete"))
932
+ except Exception:
933
+ cleanup_complete = False
934
+ if spawned is not None:
935
+ spawned.close()
946
936
  if output_stream is not None:
947
937
  output_stream.close()
948
938
 
@@ -7,12 +7,63 @@ import argparse
7
7
  import fnmatch
8
8
  import json
9
9
  import time
10
+ from collections import Counter
10
11
  from pathlib import Path
11
12
  from typing import Any
12
13
 
13
14
 
14
15
  _COVERAGE_RANK = {"incremental": 0, "module": 1, "module-am": 2, "full": 3}
15
16
 
17
+ _REASON_EXPLANATIONS = {
18
+ "PRODUCT_IDENTITY_REQUIRED": "A product identity is required before a verification can run.",
19
+ "CAPABILITY_MISSING": "A required execution capability is not available.",
20
+ "REQUIRED_COVERAGE_INVALID": "The required coverage declaration is invalid.",
21
+ "COVERAGE_DECLARATION_REQUIRED": "The target does not declare the coverage level needed for this gate.",
22
+ "COVERAGE_INSUFFICIENT": "The target coverage is below the required gate level.",
23
+ "NOT_APPLICABLE": "The target is explicitly not applicable to this change.",
24
+ "FROZEN_IDENTITY_REQUIRED": "The target requires a frozen product identity.",
25
+ "FROZEN_IDENTITY_DRIFT": "The current product identity differs from the frozen identity.",
26
+ "REUSE_POLICY_NEVER": "The target policy explicitly requires a fresh execution.",
27
+ "REUSE_EVIDENCE_INVALID": "Ledger evidence does not match the target and product identity exactly.",
28
+ "REUSE_ELIGIBLE": "Identity-matched ledger evidence permits reuse.",
29
+ "SELECTED_FOR_EXECUTION": "The target was selected for execution and passed its gate checks.",
30
+ "COMMAND_NOT_DECLARED": "The target command key is absent from the authoritative command declaration map.",
31
+ "COMMAND_DECLARATION_INVALID": "The authoritative command declaration is empty or malformed.",
32
+ "DEPENDENCY_UNKNOWN": "The target depends on a node that is not in the verification graph.",
33
+ "DEPENDENCY_BLOCKED": "A dependency was skipped or blocked, so this target cannot run.",
34
+ "DEPENDENCY_CYCLE": "The verification graph contains a dependency cycle.",
35
+ "VERIFICATION_REUSED": "The target reused an identity-matched verification receipt.",
36
+ "VERIFICATION_ARGV_NOT_CONCRETE": "The target command arguments are not concrete and executable.",
37
+ }
38
+
39
+
40
+ def _reason_explanation(reason_codes: list[str], decision: str) -> str:
41
+ explanations: list[str] = []
42
+ for raw in reason_codes:
43
+ code = str(raw).split(":", 1)[0]
44
+ explanation = _REASON_EXPLANATIONS.get(code)
45
+ if explanation is None:
46
+ if raw.startswith("depends-on:"):
47
+ explanation = f"The target waits for dependency {raw.split(':', 1)[1]} to complete."
48
+ elif raw.startswith("consumer-of:"):
49
+ explanation = f"The target is included because it consumes {raw.split(':', 1)[1]}."
50
+ elif raw.startswith("input-match:"):
51
+ explanation = f"A changed input matched {raw.split(':', 1)[1]}."
52
+ elif raw.startswith("CAPABILITY_MISSING:"):
53
+ explanation = f"Capability {raw.split(':', 1)[1]} is unavailable."
54
+ else:
55
+ explanation = f"Verification decision reason: {raw}."
56
+ if explanation not in explanations:
57
+ explanations.append(explanation)
58
+ if explanations:
59
+ return " ".join(explanations)
60
+ return {
61
+ "EXECUTE": "The target passed all declared gate checks and is ready to execute.",
62
+ "REUSE": "The target has valid identity-matched reusable evidence.",
63
+ "SKIP": "The target is not applicable to this change.",
64
+ "BLOCKED": "The target cannot execute until its blocking conditions are resolved.",
65
+ }.get(decision, "The target has no executable decision.")
66
+
16
67
 
17
68
  def _normalize_targets(raw_targets: Any) -> list[dict[str, Any]]:
18
69
  if isinstance(raw_targets, dict):
@@ -133,10 +184,26 @@ def _target_decision(
133
184
  frozen_identity: str,
134
185
  available_capabilities: set[str],
135
186
  verification_ledger: list[dict[str, Any]],
187
+ command_declarations: dict[str, Any] | None = None,
136
188
  ) -> tuple[str, list[str]]:
137
189
  blockers: list[str] = []
138
190
  if not product_identity:
139
191
  return "BLOCKED", ["PRODUCT_IDENTITY_REQUIRED"]
192
+ if command_declarations is not None:
193
+ command_key = str(target.get("commandKey") or "").strip()
194
+ declaration = command_declarations.get(command_key) if command_key else None
195
+ if declaration is None:
196
+ blockers.append("COMMAND_NOT_DECLARED")
197
+ elif isinstance(declaration, dict):
198
+ argv = declaration.get("argv") or declaration.get("argvTemplate")
199
+ if not isinstance(argv, list) or not argv or not all(
200
+ isinstance(item, str) and item for item in argv
201
+ ):
202
+ blockers.append("COMMAND_DECLARATION_INVALID")
203
+ elif not isinstance(declaration, list) or not declaration or not all(
204
+ isinstance(item, str) and item for item in declaration
205
+ ):
206
+ blockers.append("COMMAND_DECLARATION_INVALID")
140
207
  required_capabilities = {
141
208
  str(item).strip()
142
209
  for item in target.get("requiredCapabilities", [])
@@ -238,6 +305,12 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
238
305
  for item in payload.get("verificationLedger", [])
239
306
  if isinstance(item, dict)
240
307
  ]
308
+ raw_commands = payload.get("commands")
309
+ command_declarations = (
310
+ {str(key): value for key, value in raw_commands.items()}
311
+ if isinstance(raw_commands, dict)
312
+ else None
313
+ )
241
314
  decisions: dict[str, tuple[str, list[str]]] = {
242
315
  target_id: _target_decision(
243
316
  target,
@@ -245,6 +318,7 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
245
318
  frozen_identity=frozen_identity,
246
319
  available_capabilities=available_capabilities,
247
320
  verification_ledger=verification_ledger,
321
+ command_declarations=command_declarations,
248
322
  )
249
323
  for target_id, target in by_id.items()
250
324
  }
@@ -345,10 +419,14 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
345
419
  "id": target_id,
346
420
  "decision": decision,
347
421
  "reasonCodes": reason_codes,
422
+ "explanation": _reason_explanation(reason_codes, decision),
348
423
  "resourceLocks": _resource_locks(target),
349
424
  "wave": placed[target_id],
350
425
  }
351
426
  )
427
+ decision_counts = dict(
428
+ sorted(Counter(item["decision"] for item in plan).items())
429
+ )
352
430
  return {
353
431
  "ok": all(item["decision"] != "BLOCKED" for item in plan),
354
432
  "code": (
@@ -359,6 +437,7 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
359
437
  "productIdentity": product_identity,
360
438
  "frozenIdentity": frozen_identity,
361
439
  "plan": plan,
440
+ "decisionCounts": decision_counts,
362
441
  "waves": [
363
442
  {"wave": index, "targets": target_ids}
364
443
  for index, target_ids in enumerate(waves)