@hunter-harness/workflow-harness 0.4.0 → 0.4.2

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 (386) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  3. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-execute/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
  10. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  11. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  14. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/scripts/harness_archive.py +29 -6
  16. package/harness/bundles/general/claude-code/scripts/harness_deploy.py +1 -1
  17. package/harness/bundles/general/claude-code/scripts/harness_knowledge_candidates.py +398 -0
  18. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  19. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  20. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  21. package/harness/bundles/general/codebuddy/harness-execute/SKILL.md +1 -1
  22. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  23. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  24. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
  25. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
  26. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
  27. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  28. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  32. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +29 -6
  33. package/harness/bundles/general/codebuddy/scripts/harness_deploy.py +1 -1
  34. package/harness/bundles/general/codebuddy/scripts/harness_knowledge_candidates.py +398 -0
  35. package/harness/bundles/general/codex/.harness-build.json +1 -1
  36. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  37. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  38. package/harness/bundles/general/codex/harness-execute/SKILL.md +1 -1
  39. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  40. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  41. package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
  42. package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
  43. package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
  44. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  45. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  46. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  47. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  48. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  49. package/harness/bundles/general/codex/scripts/harness_archive.py +29 -6
  50. package/harness/bundles/general/codex/scripts/harness_deploy.py +1 -1
  51. package/harness/bundles/general/codex/scripts/harness_knowledge_candidates.py +398 -0
  52. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  53. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  54. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  55. package/harness/bundles/general/cursor/harness-execute/SKILL.md +1 -1
  56. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  57. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  58. package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
  59. package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
  60. package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
  61. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  62. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  63. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  64. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  65. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  66. package/harness/bundles/general/cursor/scripts/harness_archive.py +29 -6
  67. package/harness/bundles/general/cursor/scripts/harness_deploy.py +1 -1
  68. package/harness/bundles/general/cursor/scripts/harness_knowledge_candidates.py +398 -0
  69. package/harness/bundles/general/pi/.harness-build.json +6 -0
  70. package/harness/bundles/general/pi/CONTEXT.md +98 -0
  71. package/harness/bundles/general/pi/README.md +478 -0
  72. package/harness/bundles/general/pi/contracts/build-profile-v3.schema.json +302 -0
  73. package/harness/bundles/general/pi/contracts/ci-metrics.schema.json +36 -0
  74. package/harness/bundles/general/pi/contracts/doctor-capability.schema.json +43 -0
  75. package/harness/bundles/general/pi/contracts/external-platform-observation.schema.json +62 -0
  76. package/harness/bundles/general/pi/contracts/fixtures/managed-execution.json +341 -0
  77. package/harness/bundles/general/pi/contracts/process-identity.schema.json +132 -0
  78. package/harness/bundles/general/pi/contracts/run-session.schema.json +142 -0
  79. package/harness/bundles/general/pi/contracts/service-retirement-receipt.schema.json +85 -0
  80. package/harness/bundles/general/pi/contracts/service-session.schema.json +147 -0
  81. package/harness/bundles/general/pi/contracts/workflow-policy.json +574 -0
  82. package/harness/bundles/general/pi/harness-archive/SKILL.md +322 -0
  83. package/harness/bundles/general/pi/harness-archive/checklist.md +71 -0
  84. package/harness/bundles/general/pi/harness-archive/reference.md +150 -0
  85. package/harness/bundles/general/pi/harness-archive/scripts/gen-manifest.ps1 +31 -0
  86. package/harness/bundles/general/pi/harness-archive/templates/summary-data-template.json +218 -0
  87. package/harness/bundles/general/pi/harness-codebase-map/SKILL.md +116 -0
  88. package/harness/bundles/general/pi/harness-codebase-map/checklist.md +126 -0
  89. package/harness/bundles/general/pi/harness-codebase-map/reference.md +364 -0
  90. package/harness/bundles/general/pi/harness-codebase-map/templates/ARCHITECTURE.md +57 -0
  91. package/harness/bundles/general/pi/harness-codebase-map/templates/CONCERNS.md +49 -0
  92. package/harness/bundles/general/pi/harness-codebase-map/templates/CONVENTIONS.md +57 -0
  93. package/harness/bundles/general/pi/harness-codebase-map/templates/INTEGRATIONS.md +52 -0
  94. package/harness/bundles/general/pi/harness-codebase-map/templates/STACK.md +55 -0
  95. package/harness/bundles/general/pi/harness-codebase-map/templates/STRUCTURE.md +69 -0
  96. package/harness/bundles/general/pi/harness-codebase-map/templates/TESTING.md +64 -0
  97. package/harness/bundles/general/pi/harness-codebase-map/templates/map-manifest.schema.json +73 -0
  98. package/harness/bundles/general/pi/harness-codebase-map/templates/map-summary.md +64 -0
  99. package/harness/bundles/general/pi/harness-execute/SKILL.md +170 -0
  100. package/harness/bundles/general/pi/harness-knowledge-ingest/SKILL.md +103 -0
  101. package/harness/bundles/general/pi/harness-knowledge-query/SKILL.md +97 -0
  102. package/harness/bundles/general/pi/harness-plan/SKILL.md +157 -0
  103. package/harness/bundles/general/pi/harness-plan/checklist.md +243 -0
  104. package/harness/bundles/general/pi/harness-plan/protocols.md +133 -0
  105. package/harness/bundles/general/pi/harness-plan/reference.md +468 -0
  106. package/harness/bundles/general/pi/harness-pull/SKILL.md +55 -0
  107. package/harness/bundles/general/pi/harness-push/SKILL.md +83 -0
  108. package/harness/bundles/general/pi/harness-review/SKILL.md +166 -0
  109. package/harness/bundles/general/pi/harness-review/checklist.md +124 -0
  110. package/harness/bundles/general/pi/harness-review/protocols.md +69 -0
  111. package/harness/bundles/general/pi/harness-review/reference.md +114 -0
  112. package/harness/bundles/general/pi/harness-run/SKILL.md +188 -0
  113. package/harness/bundles/general/pi/harness-run/checklist.md +279 -0
  114. package/harness/bundles/general/pi/harness-run/protocols.md +141 -0
  115. package/harness/bundles/general/pi/harness-run/reference.md +886 -0
  116. package/harness/bundles/general/pi/harness-submit/SKILL.md +182 -0
  117. package/harness/bundles/general/pi/harness-submit/checklist.md +452 -0
  118. package/harness/bundles/general/pi/harness-submit/reference.md +152 -0
  119. package/harness/bundles/general/pi/harness-sync/SKILL.md +72 -0
  120. package/harness/bundles/general/pi/harness-sync/reference.md +176 -0
  121. package/harness/bundles/general/pi/harness-test/SKILL.md +250 -0
  122. package/harness/bundles/general/pi/harness-test/checklist.md +359 -0
  123. package/harness/bundles/general/pi/harness-test/pitfalls-java.md +14 -0
  124. package/harness/bundles/general/pi/harness-test/pitfalls.md +307 -0
  125. package/harness/bundles/general/pi/harness-test/reference.md +880 -0
  126. package/harness/bundles/general/pi/harness-test/scripts/runtime-helpers.mjs +216 -0
  127. package/harness/bundles/general/pi/protocols/archive-report-protocol.md +196 -0
  128. package/harness/bundles/general/pi/protocols/ci-layering-protocol.md +22 -0
  129. package/harness/bundles/general/pi/protocols/evidence-based-reporting-protocol.md +134 -0
  130. package/harness/bundles/general/pi/protocols/execution-session-protocol.md +52 -0
  131. package/harness/bundles/general/pi/protocols/external-platform-protocol.md +27 -0
  132. package/harness/bundles/general/pi/protocols/ledger-protocol.md +365 -0
  133. package/harness/bundles/general/pi/protocols/memory-closeout-protocol.md +21 -0
  134. package/harness/bundles/general/pi/protocols/model-routing-protocol.md +22 -0
  135. package/harness/bundles/general/pi/protocols/powershell-protocol.md +238 -0
  136. package/harness/bundles/general/pi/protocols/registry-governance-protocol.md +31 -0
  137. package/harness/bundles/general/pi/protocols/report-pipeline-protocol.md +172 -0
  138. package/harness/bundles/general/pi/protocols/sensitive-info-protocol.md +110 -0
  139. package/harness/bundles/general/pi/protocols/state-layout-protocol.md +209 -0
  140. package/harness/bundles/general/pi/protocols/submit-protocol.md +78 -0
  141. package/harness/bundles/general/pi/protocols/verification-graph-protocol.md +32 -0
  142. package/harness/bundles/general/pi/scripts/harness_adoption_metrics.py +391 -0
  143. package/harness/bundles/general/pi/scripts/harness_apidoc.py +53 -0
  144. package/harness/bundles/general/pi/scripts/harness_archive.py +11345 -0
  145. package/harness/bundles/general/pi/scripts/harness_change.py +1626 -0
  146. package/harness/bundles/general/pi/scripts/harness_check_gate.py +113 -0
  147. package/harness/bundles/general/pi/scripts/harness_context.py +1774 -0
  148. package/harness/bundles/general/pi/scripts/harness_deploy.py +968 -0
  149. package/harness/bundles/general/pi/scripts/harness_efficiency.py +443 -0
  150. package/harness/bundles/general/pi/scripts/harness_environment.py +1690 -0
  151. package/harness/bundles/general/pi/scripts/harness_events.py +2234 -0
  152. package/harness/bundles/general/pi/scripts/harness_events_sync.py +1425 -0
  153. package/harness/bundles/general/pi/scripts/harness_execution_contracts.py +596 -0
  154. package/harness/bundles/general/pi/scripts/harness_fixback.py +1975 -0
  155. package/harness/bundles/general/pi/scripts/harness_gate.py +3816 -0
  156. package/harness/bundles/general/pi/scripts/harness_headless.py +37 -0
  157. package/harness/bundles/general/pi/scripts/harness_integration.py +2564 -0
  158. package/harness/bundles/general/pi/scripts/harness_knowledge_candidates.py +796 -0
  159. package/harness/bundles/general/pi/scripts/harness_ledger.py +3556 -0
  160. package/harness/bundles/general/pi/scripts/harness_migration_head.py +220 -0
  161. package/harness/bundles/general/pi/scripts/harness_orchestration.py +344 -0
  162. package/harness/bundles/general/pi/scripts/harness_paths.py +676 -0
  163. package/harness/bundles/general/pi/scripts/harness_phase.py +1726 -0
  164. package/harness/bundles/general/pi/scripts/harness_plan_aggregate.py +249 -0
  165. package/harness/bundles/general/pi/scripts/harness_plan_finalize.py +1016 -0
  166. package/harness/bundles/general/pi/scripts/harness_preflight.py +650 -0
  167. package/harness/bundles/general/pi/scripts/harness_process.py +1065 -0
  168. package/harness/bundles/general/pi/scripts/harness_profile.py +1817 -0
  169. package/harness/bundles/general/pi/scripts/harness_report_model.py +442 -0
  170. package/harness/bundles/general/pi/scripts/harness_retry.py +175 -0
  171. package/harness/bundles/general/pi/scripts/harness_review.py +631 -0
  172. package/harness/bundles/general/pi/scripts/harness_runtime.py +2243 -0
  173. package/harness/bundles/general/pi/scripts/harness_service.py +3355 -0
  174. package/harness/bundles/general/pi/scripts/harness_state.py +365 -0
  175. package/harness/bundles/general/pi/scripts/harness_sync.py +407 -0
  176. package/harness/bundles/general/pi/scripts/harness_test_cleanup.py +175 -0
  177. package/harness/bundles/general/pi/scripts/harness_test_guard.py +1899 -0
  178. package/harness/bundles/general/pi/scripts/harness_test_runner.py +1586 -0
  179. package/harness/bundles/general/pi/scripts/harness_verification.py +593 -0
  180. package/harness/bundles/general/pi/scripts/harness_workflow_policy.py +266 -0
  181. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  182. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  183. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  184. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  185. package/harness/bundles/java/claude-code/harness-execute/SKILL.md +1 -1
  186. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  187. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  188. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  189. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
  190. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
  191. package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
  192. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  193. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  194. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  195. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  196. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  197. package/harness/bundles/java/claude-code/scripts/harness_archive.py +29 -6
  198. package/harness/bundles/java/claude-code/scripts/harness_deploy.py +1 -1
  199. package/harness/bundles/java/claude-code/scripts/harness_knowledge_candidates.py +398 -0
  200. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  201. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  202. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  203. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  204. package/harness/bundles/java/codebuddy/harness-execute/SKILL.md +1 -1
  205. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  206. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  207. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  208. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
  209. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
  210. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
  211. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  212. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  213. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  214. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  215. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  216. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +29 -6
  217. package/harness/bundles/java/codebuddy/scripts/harness_deploy.py +1 -1
  218. package/harness/bundles/java/codebuddy/scripts/harness_knowledge_candidates.py +398 -0
  219. package/harness/bundles/java/codex/.harness-build.json +1 -1
  220. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  221. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  222. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  223. package/harness/bundles/java/codex/harness-execute/SKILL.md +1 -1
  224. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  225. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  226. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  227. package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
  228. package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
  229. package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
  230. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  231. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  232. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  233. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  234. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  235. package/harness/bundles/java/codex/scripts/harness_archive.py +29 -6
  236. package/harness/bundles/java/codex/scripts/harness_deploy.py +1 -1
  237. package/harness/bundles/java/codex/scripts/harness_knowledge_candidates.py +398 -0
  238. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  239. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  240. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  241. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  242. package/harness/bundles/java/cursor/harness-execute/SKILL.md +1 -1
  243. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  244. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  245. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  246. package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
  247. package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
  248. package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
  249. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  250. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  251. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  252. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  253. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  254. package/harness/bundles/java/cursor/scripts/harness_archive.py +29 -6
  255. package/harness/bundles/java/cursor/scripts/harness_deploy.py +1 -1
  256. package/harness/bundles/java/cursor/scripts/harness_knowledge_candidates.py +398 -0
  257. package/harness/bundles/java/pi/.harness-build.json +6 -0
  258. package/harness/bundles/java/pi/CONTEXT.md +98 -0
  259. package/harness/bundles/java/pi/README.md +478 -0
  260. package/harness/bundles/java/pi/contracts/build-profile-v3.schema.json +302 -0
  261. package/harness/bundles/java/pi/contracts/ci-metrics.schema.json +36 -0
  262. package/harness/bundles/java/pi/contracts/doctor-capability.schema.json +43 -0
  263. package/harness/bundles/java/pi/contracts/external-platform-observation.schema.json +62 -0
  264. package/harness/bundles/java/pi/contracts/fixtures/managed-execution.json +341 -0
  265. package/harness/bundles/java/pi/contracts/process-identity.schema.json +132 -0
  266. package/harness/bundles/java/pi/contracts/run-session.schema.json +142 -0
  267. package/harness/bundles/java/pi/contracts/service-retirement-receipt.schema.json +85 -0
  268. package/harness/bundles/java/pi/contracts/service-session.schema.json +147 -0
  269. package/harness/bundles/java/pi/contracts/workflow-policy.json +574 -0
  270. package/harness/bundles/java/pi/harness-apidoc/SKILL.md +97 -0
  271. package/harness/bundles/java/pi/harness-apidoc/checklist.md +142 -0
  272. package/harness/bundles/java/pi/harness-apidoc/reference.md +233 -0
  273. package/harness/bundles/java/pi/harness-archive/SKILL.md +322 -0
  274. package/harness/bundles/java/pi/harness-archive/checklist.md +71 -0
  275. package/harness/bundles/java/pi/harness-archive/reference.md +150 -0
  276. package/harness/bundles/java/pi/harness-archive/scripts/gen-manifest.ps1 +31 -0
  277. package/harness/bundles/java/pi/harness-archive/templates/summary-data-template.json +218 -0
  278. package/harness/bundles/java/pi/harness-codebase-map/SKILL.md +116 -0
  279. package/harness/bundles/java/pi/harness-codebase-map/checklist.md +126 -0
  280. package/harness/bundles/java/pi/harness-codebase-map/reference.md +364 -0
  281. package/harness/bundles/java/pi/harness-codebase-map/templates/ARCHITECTURE.md +57 -0
  282. package/harness/bundles/java/pi/harness-codebase-map/templates/CONCERNS.md +49 -0
  283. package/harness/bundles/java/pi/harness-codebase-map/templates/CONVENTIONS.md +57 -0
  284. package/harness/bundles/java/pi/harness-codebase-map/templates/INTEGRATIONS.md +52 -0
  285. package/harness/bundles/java/pi/harness-codebase-map/templates/STACK.md +55 -0
  286. package/harness/bundles/java/pi/harness-codebase-map/templates/STRUCTURE.md +69 -0
  287. package/harness/bundles/java/pi/harness-codebase-map/templates/TESTING.md +64 -0
  288. package/harness/bundles/java/pi/harness-codebase-map/templates/map-manifest.schema.json +73 -0
  289. package/harness/bundles/java/pi/harness-codebase-map/templates/map-summary.md +64 -0
  290. package/harness/bundles/java/pi/harness-execute/SKILL.md +170 -0
  291. package/harness/bundles/java/pi/harness-knowledge-ingest/SKILL.md +103 -0
  292. package/harness/bundles/java/pi/harness-knowledge-query/SKILL.md +97 -0
  293. package/harness/bundles/java/pi/harness-package/SKILL.md +94 -0
  294. package/harness/bundles/java/pi/harness-package/checklist.md +322 -0
  295. package/harness/bundles/java/pi/harness-package/reference.md +210 -0
  296. package/harness/bundles/java/pi/harness-plan/SKILL.md +157 -0
  297. package/harness/bundles/java/pi/harness-plan/checklist.md +243 -0
  298. package/harness/bundles/java/pi/harness-plan/protocols.md +133 -0
  299. package/harness/bundles/java/pi/harness-plan/reference.md +468 -0
  300. package/harness/bundles/java/pi/harness-pull/SKILL.md +55 -0
  301. package/harness/bundles/java/pi/harness-push/SKILL.md +83 -0
  302. package/harness/bundles/java/pi/harness-review/SKILL.md +166 -0
  303. package/harness/bundles/java/pi/harness-review/checklist.md +124 -0
  304. package/harness/bundles/java/pi/harness-review/protocols.md +69 -0
  305. package/harness/bundles/java/pi/harness-review/reference.md +114 -0
  306. package/harness/bundles/java/pi/harness-run/SKILL.md +206 -0
  307. package/harness/bundles/java/pi/harness-run/checklist.md +252 -0
  308. package/harness/bundles/java/pi/harness-run/protocols.md +141 -0
  309. package/harness/bundles/java/pi/harness-run/reference.md +820 -0
  310. package/harness/bundles/java/pi/harness-submit/SKILL.md +189 -0
  311. package/harness/bundles/java/pi/harness-submit/checklist.md +452 -0
  312. package/harness/bundles/java/pi/harness-submit/reference.md +152 -0
  313. package/harness/bundles/java/pi/harness-sync/SKILL.md +72 -0
  314. package/harness/bundles/java/pi/harness-sync/reference.md +176 -0
  315. package/harness/bundles/java/pi/harness-test/SKILL.md +264 -0
  316. package/harness/bundles/java/pi/harness-test/checklist.md +320 -0
  317. package/harness/bundles/java/pi/harness-test/pitfalls-java.md +16 -0
  318. package/harness/bundles/java/pi/harness-test/pitfalls.md +307 -0
  319. package/harness/bundles/java/pi/harness-test/reference.md +792 -0
  320. package/harness/bundles/java/pi/harness-test/scripts/runtime-helpers.mjs +216 -0
  321. package/harness/bundles/java/pi/protocols/archive-report-protocol.md +196 -0
  322. package/harness/bundles/java/pi/protocols/ci-layering-protocol.md +22 -0
  323. package/harness/bundles/java/pi/protocols/evidence-based-reporting-protocol.md +134 -0
  324. package/harness/bundles/java/pi/protocols/execution-session-protocol.md +52 -0
  325. package/harness/bundles/java/pi/protocols/external-platform-protocol.md +27 -0
  326. package/harness/bundles/java/pi/protocols/ledger-protocol.md +365 -0
  327. package/harness/bundles/java/pi/protocols/memory-closeout-protocol.md +21 -0
  328. package/harness/bundles/java/pi/protocols/model-routing-protocol.md +22 -0
  329. package/harness/bundles/java/pi/protocols/powershell-protocol.md +238 -0
  330. package/harness/bundles/java/pi/protocols/registry-governance-protocol.md +31 -0
  331. package/harness/bundles/java/pi/protocols/report-pipeline-protocol.md +172 -0
  332. package/harness/bundles/java/pi/protocols/sensitive-info-protocol.md +110 -0
  333. package/harness/bundles/java/pi/protocols/state-layout-protocol.md +209 -0
  334. package/harness/bundles/java/pi/protocols/submit-protocol.md +78 -0
  335. package/harness/bundles/java/pi/protocols/verification-graph-protocol.md +32 -0
  336. package/harness/bundles/java/pi/scripts/harness_adoption_metrics.py +391 -0
  337. package/harness/bundles/java/pi/scripts/harness_apidoc.py +53 -0
  338. package/harness/bundles/java/pi/scripts/harness_archive.py +11345 -0
  339. package/harness/bundles/java/pi/scripts/harness_change.py +1626 -0
  340. package/harness/bundles/java/pi/scripts/harness_check_gate.py +113 -0
  341. package/harness/bundles/java/pi/scripts/harness_context.py +1774 -0
  342. package/harness/bundles/java/pi/scripts/harness_deploy.py +968 -0
  343. package/harness/bundles/java/pi/scripts/harness_efficiency.py +443 -0
  344. package/harness/bundles/java/pi/scripts/harness_environment.py +1690 -0
  345. package/harness/bundles/java/pi/scripts/harness_events.py +2234 -0
  346. package/harness/bundles/java/pi/scripts/harness_events_sync.py +1425 -0
  347. package/harness/bundles/java/pi/scripts/harness_execution_contracts.py +596 -0
  348. package/harness/bundles/java/pi/scripts/harness_fixback.py +1975 -0
  349. package/harness/bundles/java/pi/scripts/harness_gate.py +3816 -0
  350. package/harness/bundles/java/pi/scripts/harness_headless.py +37 -0
  351. package/harness/bundles/java/pi/scripts/harness_integration.py +2564 -0
  352. package/harness/bundles/java/pi/scripts/harness_knowledge_candidates.py +796 -0
  353. package/harness/bundles/java/pi/scripts/harness_ledger.py +3556 -0
  354. package/harness/bundles/java/pi/scripts/harness_migration_head.py +220 -0
  355. package/harness/bundles/java/pi/scripts/harness_orchestration.py +344 -0
  356. package/harness/bundles/java/pi/scripts/harness_paths.py +676 -0
  357. package/harness/bundles/java/pi/scripts/harness_phase.py +1726 -0
  358. package/harness/bundles/java/pi/scripts/harness_plan_aggregate.py +249 -0
  359. package/harness/bundles/java/pi/scripts/harness_plan_finalize.py +1016 -0
  360. package/harness/bundles/java/pi/scripts/harness_preflight.py +650 -0
  361. package/harness/bundles/java/pi/scripts/harness_process.py +1065 -0
  362. package/harness/bundles/java/pi/scripts/harness_profile.py +1817 -0
  363. package/harness/bundles/java/pi/scripts/harness_report_model.py +442 -0
  364. package/harness/bundles/java/pi/scripts/harness_retry.py +175 -0
  365. package/harness/bundles/java/pi/scripts/harness_review.py +631 -0
  366. package/harness/bundles/java/pi/scripts/harness_runtime.py +2243 -0
  367. package/harness/bundles/java/pi/scripts/harness_service.py +3355 -0
  368. package/harness/bundles/java/pi/scripts/harness_state.py +365 -0
  369. package/harness/bundles/java/pi/scripts/harness_sync.py +407 -0
  370. package/harness/bundles/java/pi/scripts/harness_test_cleanup.py +175 -0
  371. package/harness/bundles/java/pi/scripts/harness_test_guard.py +1899 -0
  372. package/harness/bundles/java/pi/scripts/harness_test_runner.py +1586 -0
  373. package/harness/bundles/java/pi/scripts/harness_verification.py +593 -0
  374. package/harness/bundles/java/pi/scripts/harness_workflow_policy.py +266 -0
  375. package/harness/manifests/general/claude-code.json +17 -17
  376. package/harness/manifests/general/codebuddy.json +17 -17
  377. package/harness/manifests/general/codex.json +17 -17
  378. package/harness/manifests/general/cursor.json +17 -17
  379. package/harness/manifests/general/pi.json +475 -0
  380. package/harness/manifests/java/claude-code.json +19 -19
  381. package/harness/manifests/java/codebuddy.json +19 -19
  382. package/harness/manifests/java/codex.json +19 -19
  383. package/harness/manifests/java/cursor.json +19 -19
  384. package/harness/manifests/java/pi.json +499 -0
  385. package/hunter-workflow-family.json +2 -2
  386. package/package.json +1 -1
@@ -0,0 +1,442 @@
1
+ #!/usr/bin/env python3
2
+ """Normalized report facts shared by archive collection and rendering."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import copy
7
+ import datetime as dt
8
+ from typing import Any, Iterable, Mapping
9
+
10
+ REPORT_MODEL_SCHEMA_VERSION = 1
11
+ UNKNOWN_VALUES = frozenset({"", "unknown", "not_available", "n/a", "null"})
12
+ TERMINAL_STATUSES = frozenset(
13
+ {
14
+ "OK",
15
+ "PASS",
16
+ "PASSED",
17
+ "WARN",
18
+ "FAIL",
19
+ "FAILED",
20
+ "ERROR",
21
+ "BLOCKED",
22
+ "BLOCKED_BY_ENV",
23
+ "BLOCKED_BY_DBA",
24
+ "NOT_RUN",
25
+ "SKIPPED",
26
+ "USER_SKIPPED",
27
+ "NOT_APPLICABLE",
28
+ "CANCELLED",
29
+ "TIMED_OUT",
30
+ "TERMINATED",
31
+ }
32
+ )
33
+ IDENTITY_FIELDS = (
34
+ "repositoryId",
35
+ "changeName",
36
+ "baseCommit",
37
+ "checkpointCommit",
38
+ "productCommit",
39
+ "featureTip",
40
+ "mergeCommit",
41
+ "releaseTip",
42
+ "featureMergeHash",
43
+ "releaseTipHash",
44
+ "productTreeHash",
45
+ "environmentHash",
46
+ "candidateId",
47
+ )
48
+ CONTENT_HASH_FIELDS = frozenset(
49
+ {"productTreeHash", "commandSetHash", "environmentHash", "toolchainHash", "lockHash", "dbSchemaHash"}
50
+ )
51
+ TARGET_IDENTITY_FIELDS = (
52
+ "target",
53
+ "repositoryId",
54
+ "changeName",
55
+ "productTreeHash",
56
+ "commandSetHash",
57
+ "environmentHash",
58
+ "toolchainHash",
59
+ "lockHash",
60
+ "dbSchemaHash",
61
+ "profile",
62
+ "candidateId",
63
+ )
64
+ CURRENT_DISPOSITIONS = frozenset(
65
+ {"OPEN", "ACCEPTED_RISK", "DEFERRED", "UNKNOWN"}
66
+ )
67
+
68
+
69
+ def measurement(
70
+ value: Any = None,
71
+ *,
72
+ applicable: bool | None = True,
73
+ reason: str | None = None,
74
+ ) -> dict[str, Any]:
75
+ if isinstance(value, Mapping) and value.get("state") in {
76
+ "unknown",
77
+ "not_applicable",
78
+ "zero",
79
+ "known",
80
+ }:
81
+ return copy.deepcopy(dict(value))
82
+ if applicable is False:
83
+ result = {"state": "not_applicable", "value": None}
84
+ elif value is None or (
85
+ isinstance(value, str) and value.strip().lower() in UNKNOWN_VALUES
86
+ ):
87
+ result = {"state": "unknown", "value": None}
88
+ elif isinstance(value, (int, float)) and not isinstance(value, bool) and value == 0:
89
+ result = {"state": "zero", "value": value}
90
+ else:
91
+ result = {"state": "known", "value": copy.deepcopy(value)}
92
+ if reason:
93
+ result["reason"] = reason
94
+ return result
95
+
96
+
97
+ def canonical_identity(summary: Mapping[str, Any]) -> dict[str, str]:
98
+ nested = summary.get("changeIdentity")
99
+ nested = nested if isinstance(nested, Mapping) else {}
100
+ fallbacks: dict[str, tuple[str, ...]] = {
101
+ "repositoryId": ("repositoryId",),
102
+ "changeName": ("changeName",),
103
+ "baseCommit": ("baseCommit",),
104
+ "checkpointCommit": ("checkpointCommit",),
105
+ "productCommit": ("productCommit", "finalCommit"),
106
+ "featureTip": ("featureTip", "featureTipHash", "productCommit", "finalCommit"),
107
+ "mergeCommit": ("mergeCommit", "featureMergeHash"),
108
+ "releaseTip": ("releaseTip", "releaseTipHash", "archiveCommit", "finalCommit"),
109
+ "featureMergeHash": ("featureMergeHash", "mergeCommit", "featureTip", "productCommit", "finalCommit"),
110
+ "releaseTipHash": ("releaseTipHash", "releaseTip", "archiveCommit", "finalCommit"),
111
+ "productTreeHash": ("productTreeHash",),
112
+ "environmentHash": ("environmentHash",),
113
+ "candidateId": ("candidateId",),
114
+ }
115
+ identity: dict[str, str] = {}
116
+ for field in IDENTITY_FIELDS:
117
+ # Prefer changeIdentity, then nested aliases, then top-level mirrors.
118
+ candidates = (nested.get(field),) + tuple(
119
+ nested.get(key) for key in fallbacks[field]
120
+ ) + tuple(summary.get(key) for key in fallbacks[field])
121
+ value = next(
122
+ (
123
+ str(candidate).strip()
124
+ for candidate in candidates
125
+ if candidate is not None and str(candidate).strip()
126
+ ),
127
+ "",
128
+ )
129
+ if field in CONTENT_HASH_FIELDS and value and value != "not_available":
130
+ value = "sha256:" + value.removeprefix("sha256:")
131
+ identity[field] = value
132
+ return identity
133
+
134
+
135
+ def apply_identity_mirrors(
136
+ summary: Mapping[str, Any],
137
+ identity: Mapping[str, Any] | None = None,
138
+ ) -> dict[str, Any]:
139
+ result = copy.deepcopy(dict(summary))
140
+ canonical = dict(identity or canonical_identity(summary))
141
+ result["changeIdentity"] = copy.deepcopy(canonical)
142
+ for field in (
143
+ "checkpointCommit",
144
+ "productCommit",
145
+ "featureTip",
146
+ "mergeCommit",
147
+ "releaseTip",
148
+ "featureMergeHash",
149
+ "releaseTipHash",
150
+ "productTreeHash",
151
+ "environmentHash",
152
+ ):
153
+ if canonical.get(field):
154
+ result[field] = canonical[field]
155
+ git_facts = result.get("gitFacts")
156
+ git_facts = copy.deepcopy(git_facts) if isinstance(git_facts, Mapping) else {}
157
+ for field in (
158
+ "baseCommit",
159
+ "checkpointCommit",
160
+ "productCommit",
161
+ "featureTip",
162
+ "mergeCommit",
163
+ "releaseTip",
164
+ "featureMergeHash",
165
+ "releaseTipHash",
166
+ "productTreeHash",
167
+ "environmentHash",
168
+ ):
169
+ if canonical.get(field):
170
+ git_facts[field] = canonical[field]
171
+ result["gitFacts"] = git_facts
172
+ return result
173
+
174
+
175
+ def _identity_value(field: str, value: Any) -> str:
176
+ text = str(value or "").strip()
177
+ if field in CONTENT_HASH_FIELDS and text and text != "not_available":
178
+ return text.removeprefix("sha256:")
179
+ return text
180
+
181
+
182
+ def _record_time(record: Mapping[str, Any]) -> dt.datetime:
183
+ for field in ("finishedAt", "completedAt", "endedAt", "timestamp", "recordedAt"):
184
+ value = str(record.get(field) or "").strip()
185
+ if not value:
186
+ continue
187
+ try:
188
+ return dt.datetime.fromisoformat(value.replace("Z", "+00:00"))
189
+ except ValueError:
190
+ continue
191
+ return dt.datetime.min.replace(tzinfo=dt.timezone.utc)
192
+
193
+
194
+ def select_latest_terminal_verification(
195
+ records: Iterable[Mapping[str, Any]],
196
+ target_identity: Mapping[str, Any],
197
+ ) -> dict[str, Any] | None:
198
+ matches: list[Mapping[str, Any]] = []
199
+ for record in records:
200
+ if str(record.get("status") or "").strip().upper() not in TERMINAL_STATUSES:
201
+ continue
202
+ matches_identity = True
203
+ for field in TARGET_IDENTITY_FIELDS:
204
+ expected = _identity_value(field, target_identity.get(field))
205
+ if not expected:
206
+ continue
207
+ actual = _identity_value(field, record.get(field))
208
+ if actual != expected:
209
+ matches_identity = False
210
+ break
211
+ if matches_identity:
212
+ matches.append(record)
213
+ if not matches:
214
+ return None
215
+ return copy.deepcopy(dict(max(matches, key=_record_time)))
216
+
217
+
218
+ def current_findings(items: Iterable[Mapping[str, Any]]) -> list[dict[str, Any]]:
219
+ current: list[dict[str, Any]] = []
220
+ for item in items:
221
+ disposition = (
222
+ str(item.get("disposition") or "UNKNOWN")
223
+ .strip()
224
+ .upper()
225
+ .replace("-", "_")
226
+ )
227
+ if disposition not in CURRENT_DISPOSITIONS:
228
+ continue
229
+ entry = copy.deepcopy(dict(item))
230
+ entry["disposition"] = disposition
231
+ current.append(entry)
232
+ return current
233
+
234
+
235
+ def latest_terminal_validations(
236
+ ledger: Mapping[str, Any] | None,
237
+ ) -> dict[str, dict[str, Any]]:
238
+ """Project validation history onto the current immutable product target."""
239
+ if not isinstance(ledger, Mapping):
240
+ return {}
241
+ raw_validations = ledger.get("validations")
242
+ raw_validations = (
243
+ raw_validations if isinstance(raw_validations, Mapping) else {}
244
+ )
245
+ global_history: list[Mapping[str, Any]] = []
246
+ dynamic_targets = ledger.get("verificationTargets")
247
+ if isinstance(dynamic_targets, Mapping):
248
+ for key, item in dynamic_targets.items():
249
+ if isinstance(item, Mapping):
250
+ global_history.append(
251
+ {
252
+ **dict(item),
253
+ "target": (
254
+ item.get("target")
255
+ or item.get("verification")
256
+ or str(key)
257
+ ),
258
+ }
259
+ )
260
+ elif isinstance(dynamic_targets, list):
261
+ global_history.extend(
262
+ item for item in dynamic_targets if isinstance(item, Mapping)
263
+ )
264
+ for key in ("verificationHistory", "validationHistory", "validationsHistory"):
265
+ items = ledger.get(key)
266
+ if isinstance(items, list):
267
+ global_history.extend(
268
+ item for item in items if isinstance(item, Mapping)
269
+ )
270
+ projected: dict[str, dict[str, Any]] = {}
271
+ targets = {
272
+ str(key) for key in raw_validations
273
+ } | {
274
+ str(item.get("target") or item.get("verification") or "")
275
+ for item in global_history
276
+ if item.get("target") or item.get("verification")
277
+ }
278
+ base_target = {
279
+ "repositoryId": ledger.get("repositoryId"),
280
+ "changeName": ledger.get("changeName"),
281
+ "productCommit": (
282
+ ledger.get("productCommit")
283
+ or ledger.get("headCommit")
284
+ or ledger.get("currentHead")
285
+ ),
286
+ "productTreeHash": ledger.get("productTreeHash"),
287
+ "environmentHash": ledger.get("environmentHash"),
288
+ "profile": ledger.get("profile"),
289
+ "candidateId": ledger.get("candidateId"),
290
+ }
291
+ for target in sorted(item for item in targets if item):
292
+ direct = raw_validations.get(target)
293
+ candidates: list[Mapping[str, Any]] = [
294
+ {
295
+ **dict(item),
296
+ "target": item.get("target") or item.get("verification") or target,
297
+ }
298
+ for item in global_history
299
+ if str(item.get("target") or item.get("verification") or "") == target
300
+ ]
301
+ if isinstance(direct, Mapping):
302
+ for key in ("history", "attempts"):
303
+ items = direct.get(key)
304
+ if isinstance(items, list):
305
+ candidates.extend(
306
+ {**dict(item), "target": target}
307
+ for item in items
308
+ if isinstance(item, Mapping)
309
+ )
310
+ candidates.append({**dict(direct), "target": target})
311
+ selected = select_latest_terminal_verification(
312
+ candidates,
313
+ {"target": target, **base_target},
314
+ )
315
+ if selected is not None:
316
+ selected.pop("target", None)
317
+ projected[target] = selected
318
+ elif isinstance(direct, Mapping):
319
+ projected[target] = copy.deepcopy(dict(direct))
320
+ return projected
321
+
322
+
323
+ def _measure_record(value: Any) -> Any:
324
+ if not isinstance(value, Mapping):
325
+ return measurement(value)
326
+ return {
327
+ str(key): _measure_record(item)
328
+ for key, item in value.items()
329
+ }
330
+
331
+
332
+ def _mark_uncollected_metrics(
333
+ measured: dict[str, Any],
334
+ *paths: tuple[str, ...],
335
+ ) -> None:
336
+ for path in paths:
337
+ current: Any = measured
338
+ for key in path[:-1]:
339
+ if not isinstance(current, dict):
340
+ current = None
341
+ break
342
+ current = current.get(key)
343
+ if isinstance(current, dict) and path:
344
+ current[path[-1]] = measurement(
345
+ None,
346
+ reason="metric was not collected",
347
+ )
348
+
349
+
350
+ def normalize_report(summary: Mapping[str, Any]) -> dict[str, Any]:
351
+ canonical = canonical_identity(summary)
352
+ review_items = summary.get("reviewFindings")
353
+ review_items = review_items if isinstance(review_items, list) else []
354
+ risks = current_findings(review_items)
355
+ remote_cost = summary.get("remoteCost")
356
+ remote_cost = remote_cost if isinstance(remote_cost, Mapping) else {}
357
+ artifact_storage = summary.get("artifactStorage")
358
+ artifact_storage = (
359
+ artifact_storage if isinstance(artifact_storage, Mapping) else {}
360
+ )
361
+ candidate = summary.get("candidateVerification")
362
+ candidate = copy.deepcopy(candidate) if isinstance(candidate, Mapping) else {}
363
+ current = {
364
+ "status": str(summary.get("finalStatus") or "UNKNOWN"),
365
+ "reasons": copy.deepcopy(list(summary.get("finalStatusReasons") or [])),
366
+ "stages": copy.deepcopy(dict(summary.get("stageStatus") or {})),
367
+ "findings": risks,
368
+ "knownRisks": (
369
+ copy.deepcopy(risks)
370
+ if review_items
371
+ else copy.deepcopy(list(summary.get("knownRisks") or []))
372
+ ),
373
+ }
374
+ history = {
375
+ "timeline": copy.deepcopy(list(summary.get("timeline") or [])),
376
+ "attempts": copy.deepcopy(
377
+ list((summary.get("timing") or {}).get("attempts") or [])
378
+ if isinstance(summary.get("timing"), Mapping)
379
+ else []
380
+ ),
381
+ }
382
+ release_raw = summary.get("releaseDecision")
383
+ release_raw = release_raw if isinstance(release_raw, Mapping) else {}
384
+ release_intent = str(summary.get("archiveIntent") or "")
385
+ release_decision = (
386
+ "NOT_REQUESTED"
387
+ if release_intent == "record-only"
388
+ else str(
389
+ release_raw.get("code")
390
+ or summary.get("releaseDecision")
391
+ or "NOT_DECIDED"
392
+ )
393
+ )
394
+ release = {
395
+ "decision": release_decision,
396
+ "eligible": (
397
+ False
398
+ if release_intent == "record-only"
399
+ else bool(
400
+ release_raw.get("releaseEligible")
401
+ if "releaseEligible" in release_raw
402
+ else summary.get("releaseEligible")
403
+ )
404
+ ),
405
+ "candidate": candidate,
406
+ "intent": release_intent,
407
+ }
408
+ remote_measurements = _measure_record(remote_cost)
409
+ if remote_cost.get("available") is False:
410
+ _mark_uncollected_metrics(
411
+ remote_measurements,
412
+ ("totals", "runCount"),
413
+ ("totals", "runnerMinutes"),
414
+ ("totals", "queueWaitMs"),
415
+ ("totals", "artifactBytes"),
416
+ ("totals", "duplicateRunCount"),
417
+ )
418
+ storage_measurements = _measure_record(artifact_storage)
419
+ if artifact_storage.get("available") is not True:
420
+ _mark_uncollected_metrics(
421
+ storage_measurements,
422
+ ("artifactCount",),
423
+ ("bytesAdded",),
424
+ ("bytesReused",),
425
+ ("bytesPruned",),
426
+ )
427
+ return {
428
+ "schemaVersion": REPORT_MODEL_SCHEMA_VERSION,
429
+ "changeName": str(summary.get("changeName") or ""),
430
+ "identity": canonical,
431
+ "outcomes": {
432
+ "current": current,
433
+ "history": history,
434
+ "release": release,
435
+ },
436
+ "verification": copy.deepcopy(dict(summary.get("verification") or {})),
437
+ "timing": copy.deepcopy(dict(summary.get("timing") or {})),
438
+ "measurements": {
439
+ "remoteCost": remote_measurements,
440
+ "artifactStorage": storage_measurements,
441
+ },
442
+ }
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env python3
2
+ """Deterministic retry classifier for remote and deployment side effects."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ DECISIONS = {
13
+ "RETRY_ALLOWED_INPUT_CHANGED",
14
+ "WAIT_EXISTING_RUN",
15
+ "BLOCKED_EXTERNAL_PREREQUISITE",
16
+ "NO_NEW_INFORMATION",
17
+ "REOPEN_CANDIDATE",
18
+ }
19
+
20
+
21
+ def _same_identity(left: Any, right: Any) -> bool:
22
+ return (
23
+ isinstance(left, dict)
24
+ and isinstance(right, dict)
25
+ and all(
26
+ str(left.get(field) or "") == str(right.get(field) or "")
27
+ for field in (
28
+ "productCommit",
29
+ "productTreeHash",
30
+ "environmentHash",
31
+ )
32
+ )
33
+ )
34
+
35
+
36
+ def classify_retry(
37
+ history: list[dict[str, Any]],
38
+ *,
39
+ candidate_identity: dict[str, Any],
40
+ failure_signature: str,
41
+ input_hash: str,
42
+ external_prerequisite_hash: str | None = None,
43
+ projection_status: str = "verified",
44
+ ) -> dict[str, Any]:
45
+ """Return a side-effect-safe retry decision from durable facts."""
46
+ matching = [
47
+ item
48
+ for item in history
49
+ if isinstance(item, dict)
50
+ and _same_identity(item.get("candidateIdentity"), candidate_identity)
51
+ ]
52
+ active = next(
53
+ (
54
+ item
55
+ for item in reversed(matching)
56
+ if str(item.get("status") or "").upper()
57
+ in {"QUEUED", "RUNNING", "IN_PROGRESS", "PENDING"}
58
+ ),
59
+ None,
60
+ )
61
+ if active is not None:
62
+ return {
63
+ "ok": True,
64
+ "allowed": False,
65
+ "decision": "WAIT_EXISTING_RUN",
66
+ "message": "an equivalent candidate run is still active",
67
+ "existingRun": active,
68
+ }
69
+ if projection_status.strip().lower() == "degraded":
70
+ return {
71
+ "ok": True,
72
+ "allowed": False,
73
+ "decision": "BLOCKED_EXTERNAL_PREREQUISITE",
74
+ "message": (
75
+ "adapter/config projection is degraded; protocol identity is unknown"
76
+ ),
77
+ }
78
+ latest = matching[-1] if matching else None
79
+ if latest is None:
80
+ return {
81
+ "ok": True,
82
+ "allowed": True,
83
+ "decision": "REOPEN_CANDIDATE",
84
+ "message": "no prior equivalent candidate attempt exists",
85
+ }
86
+ prior_input = str(latest.get("inputHash") or "")
87
+ if prior_input != input_hash:
88
+ return {
89
+ "ok": True,
90
+ "allowed": True,
91
+ "decision": "RETRY_ALLOWED_INPUT_CHANGED",
92
+ "message": "candidate inputs changed since the prior attempt",
93
+ "priorInputHash": prior_input,
94
+ "inputHash": input_hash,
95
+ }
96
+ prior_external = str(latest.get("externalPrerequisiteHash") or "")
97
+ current_external = str(external_prerequisite_hash or "")
98
+ same_failure = (
99
+ str(latest.get("failureSignature") or "") == failure_signature
100
+ )
101
+ if same_failure and prior_external == current_external:
102
+ return {
103
+ "ok": True,
104
+ "allowed": False,
105
+ "decision": "NO_NEW_INFORMATION",
106
+ "message": (
107
+ "same candidate, failure signature, inputs, and external "
108
+ "prerequisites; retry would repeat the same side effect"
109
+ ),
110
+ "priorAttempt": latest,
111
+ }
112
+ if same_failure and prior_external != current_external:
113
+ return {
114
+ "ok": True,
115
+ "allowed": True,
116
+ "decision": "REOPEN_CANDIDATE",
117
+ "message": "external prerequisite identity changed",
118
+ }
119
+ if re_external_failure(failure_signature) and prior_external == current_external:
120
+ return {
121
+ "ok": True,
122
+ "allowed": False,
123
+ "decision": "BLOCKED_EXTERNAL_PREREQUISITE",
124
+ "message": "external prerequisite has not changed",
125
+ }
126
+ return {
127
+ "ok": True,
128
+ "allowed": True,
129
+ "decision": "REOPEN_CANDIDATE",
130
+ "message": "failure signature changed; a new attempt may add information",
131
+ }
132
+
133
+
134
+ def re_external_failure(signature: str) -> bool:
135
+ lowered = signature.lower()
136
+ return any(
137
+ marker in lowered
138
+ for marker in (
139
+ "permission",
140
+ "credential",
141
+ "secret",
142
+ "registry",
143
+ "allowlist",
144
+ "quota",
145
+ )
146
+ )
147
+
148
+
149
+ def main(argv: list[str] | None = None) -> int:
150
+ parser = argparse.ArgumentParser(prog="harness_retry.py")
151
+ parser.add_argument("--history", required=True)
152
+ parser.add_argument("--candidate", required=True)
153
+ parser.add_argument("--failure-signature", required=True)
154
+ parser.add_argument("--input-hash", required=True)
155
+ parser.add_argument("--external-prerequisite-hash")
156
+ parser.add_argument("--projection-status", default="verified")
157
+ args = parser.parse_args(argv)
158
+ history = json.loads(Path(args.history).read_text(encoding="utf-8-sig"))
159
+ candidate = json.loads(Path(args.candidate).read_text(encoding="utf-8-sig"))
160
+ if not isinstance(history, list) or not isinstance(candidate, dict):
161
+ raise ValueError("history must be an array and candidate an object")
162
+ result = classify_retry(
163
+ history,
164
+ candidate_identity=candidate,
165
+ failure_signature=args.failure_signature,
166
+ input_hash=args.input_hash,
167
+ external_prerequisite_hash=args.external_prerequisite_hash,
168
+ projection_status=args.projection_status,
169
+ )
170
+ print(json.dumps(result, ensure_ascii=False, indent=2))
171
+ return 0 if result["allowed"] else 2
172
+
173
+
174
+ if __name__ == "__main__":
175
+ raise SystemExit(main())