@hunter-harness/workflow-harness 0.2.1 → 0.2.3

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/harness-archive/SKILL.md +1 -1
  3. package/harness/bundles/general/claude-code/harness-archive/reference.md +2 -8
  4. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +2 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  7. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +2 -1
  9. package/harness/bundles/general/claude-code/harness-review/SKILL.md +5 -1
  10. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  11. package/harness/bundles/general/claude-code/harness-run/checklist.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
  13. package/harness/bundles/general/claude-code/harness-submit/checklist.md +30 -6
  14. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-test/SKILL.md +2 -2
  16. package/harness/bundles/general/claude-code/harness-test/checklist.md +1 -1
  17. package/harness/bundles/general/claude-code/harness-test/pitfalls.md +16 -0
  18. package/harness/bundles/general/claude-code/harness-test/scripts/runtime-helpers.mjs +216 -0
  19. package/harness/bundles/general/claude-code/protocols/archive-report-protocol.md +1 -1
  20. package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +55 -2
  21. package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +1 -8
  22. package/harness/bundles/general/claude-code/protocols/sensitive-info-protocol.md +13 -0
  23. package/harness/bundles/general/claude-code/protocols/state-layout-protocol.md +24 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_archive.py +31 -14
  25. package/harness/bundles/general/claude-code/scripts/harness_check_gate.py +111 -0
  26. package/harness/bundles/general/claude-code/scripts/harness_deploy.py +179 -4
  27. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +269 -3
  28. package/harness/bundles/general/claude-code/scripts/harness_preflight.py +72 -382
  29. package/harness/bundles/general/claude-code/scripts/harness_profile.py +742 -0
  30. package/harness/bundles/general/claude-code/scripts/harness_service.py +99 -2
  31. package/harness/bundles/general/claude-code/scripts/harness_state.py +179 -0
  32. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  33. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  34. package/harness/bundles/general/codebuddy/harness-archive/reference.md +2 -8
  35. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +2 -1
  36. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  38. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  39. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +2 -1
  40. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +5 -1
  41. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  42. package/harness/bundles/general/codebuddy/harness-run/checklist.md +1 -1
  43. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
  44. package/harness/bundles/general/codebuddy/harness-submit/checklist.md +30 -6
  45. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  46. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +2 -2
  47. package/harness/bundles/general/codebuddy/harness-test/checklist.md +1 -1
  48. package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +16 -0
  49. package/harness/bundles/general/codebuddy/harness-test/scripts/runtime-helpers.mjs +216 -0
  50. package/harness/bundles/general/codebuddy/protocols/archive-report-protocol.md +1 -1
  51. package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +55 -2
  52. package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +1 -8
  53. package/harness/bundles/general/codebuddy/protocols/sensitive-info-protocol.md +13 -0
  54. package/harness/bundles/general/codebuddy/protocols/state-layout-protocol.md +24 -0
  55. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +31 -14
  56. package/harness/bundles/general/codebuddy/scripts/harness_check_gate.py +111 -0
  57. package/harness/bundles/general/codebuddy/scripts/harness_deploy.py +179 -4
  58. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +269 -3
  59. package/harness/bundles/general/codebuddy/scripts/harness_preflight.py +72 -382
  60. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +742 -0
  61. package/harness/bundles/general/codebuddy/scripts/harness_service.py +99 -2
  62. package/harness/bundles/general/codebuddy/scripts/harness_state.py +179 -0
  63. package/harness/bundles/general/codex/.harness-build.json +1 -1
  64. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-archive/reference.md +2 -8
  66. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +2 -1
  67. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  68. package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  69. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  70. package/harness/bundles/general/codex/harness-plan/SKILL.md +2 -1
  71. package/harness/bundles/general/codex/harness-review/SKILL.md +5 -1
  72. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  73. package/harness/bundles/general/codex/harness-run/checklist.md +1 -1
  74. package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
  75. package/harness/bundles/general/codex/harness-submit/checklist.md +30 -6
  76. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  77. package/harness/bundles/general/codex/harness-test/SKILL.md +2 -2
  78. package/harness/bundles/general/codex/harness-test/checklist.md +1 -1
  79. package/harness/bundles/general/codex/harness-test/pitfalls.md +16 -0
  80. package/harness/bundles/general/codex/harness-test/scripts/runtime-helpers.mjs +216 -0
  81. package/harness/bundles/general/codex/protocols/archive-report-protocol.md +1 -1
  82. package/harness/bundles/general/codex/protocols/ledger-protocol.md +55 -2
  83. package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +1 -8
  84. package/harness/bundles/general/codex/protocols/sensitive-info-protocol.md +13 -0
  85. package/harness/bundles/general/codex/protocols/state-layout-protocol.md +24 -0
  86. package/harness/bundles/general/codex/scripts/harness_archive.py +31 -14
  87. package/harness/bundles/general/codex/scripts/harness_check_gate.py +111 -0
  88. package/harness/bundles/general/codex/scripts/harness_deploy.py +179 -4
  89. package/harness/bundles/general/codex/scripts/harness_ledger.py +269 -3
  90. package/harness/bundles/general/codex/scripts/harness_preflight.py +72 -382
  91. package/harness/bundles/general/codex/scripts/harness_profile.py +742 -0
  92. package/harness/bundles/general/codex/scripts/harness_service.py +99 -2
  93. package/harness/bundles/general/codex/scripts/harness_state.py +179 -0
  94. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  95. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  96. package/harness/bundles/general/cursor/harness-archive/reference.md +2 -8
  97. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +2 -1
  98. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  99. package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  100. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  101. package/harness/bundles/general/cursor/harness-plan/SKILL.md +2 -1
  102. package/harness/bundles/general/cursor/harness-review/SKILL.md +5 -1
  103. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  104. package/harness/bundles/general/cursor/harness-run/checklist.md +1 -1
  105. package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
  106. package/harness/bundles/general/cursor/harness-submit/checklist.md +30 -6
  107. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  108. package/harness/bundles/general/cursor/harness-test/SKILL.md +2 -2
  109. package/harness/bundles/general/cursor/harness-test/checklist.md +1 -1
  110. package/harness/bundles/general/cursor/harness-test/pitfalls.md +16 -0
  111. package/harness/bundles/general/cursor/harness-test/scripts/runtime-helpers.mjs +216 -0
  112. package/harness/bundles/general/cursor/protocols/archive-report-protocol.md +1 -1
  113. package/harness/bundles/general/cursor/protocols/ledger-protocol.md +55 -2
  114. package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +1 -8
  115. package/harness/bundles/general/cursor/protocols/sensitive-info-protocol.md +13 -0
  116. package/harness/bundles/general/cursor/protocols/state-layout-protocol.md +24 -0
  117. package/harness/bundles/general/cursor/scripts/harness_archive.py +31 -14
  118. package/harness/bundles/general/cursor/scripts/harness_check_gate.py +111 -0
  119. package/harness/bundles/general/cursor/scripts/harness_deploy.py +179 -4
  120. package/harness/bundles/general/cursor/scripts/harness_ledger.py +269 -3
  121. package/harness/bundles/general/cursor/scripts/harness_preflight.py +72 -382
  122. package/harness/bundles/general/cursor/scripts/harness_profile.py +742 -0
  123. package/harness/bundles/general/cursor/scripts/harness_service.py +99 -2
  124. package/harness/bundles/general/cursor/scripts/harness_state.py +179 -0
  125. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  126. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  127. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  128. package/harness/bundles/java/claude-code/harness-archive/reference.md +2 -8
  129. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +2 -1
  130. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  131. package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  132. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  133. package/harness/bundles/java/claude-code/harness-package/SKILL.md +3 -3
  134. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +2 -1
  135. package/harness/bundles/java/claude-code/harness-review/SKILL.md +5 -1
  136. package/harness/bundles/java/claude-code/harness-run/SKILL.md +4 -4
  137. package/harness/bundles/java/claude-code/harness-run/checklist.md +3 -1
  138. package/harness/bundles/java/claude-code/harness-run/reference.md +6 -0
  139. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +3 -3
  140. package/harness/bundles/java/claude-code/harness-submit/checklist.md +30 -6
  141. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  142. package/harness/bundles/java/claude-code/harness-test/SKILL.md +5 -5
  143. package/harness/bundles/java/claude-code/harness-test/checklist.md +3 -1
  144. package/harness/bundles/java/claude-code/harness-test/pitfalls.md +16 -0
  145. package/harness/bundles/java/claude-code/harness-test/reference.md +2 -0
  146. package/harness/bundles/java/claude-code/harness-test/scripts/runtime-helpers.mjs +216 -0
  147. package/harness/bundles/java/claude-code/protocols/archive-report-protocol.md +1 -1
  148. package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +55 -2
  149. package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +1 -8
  150. package/harness/bundles/java/claude-code/protocols/sensitive-info-protocol.md +13 -0
  151. package/harness/bundles/java/claude-code/protocols/state-layout-protocol.md +24 -0
  152. package/harness/bundles/java/claude-code/scripts/harness_archive.py +31 -14
  153. package/harness/bundles/java/claude-code/scripts/harness_check_gate.py +111 -0
  154. package/harness/bundles/java/claude-code/scripts/harness_deploy.py +179 -4
  155. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +269 -3
  156. package/harness/bundles/java/claude-code/scripts/harness_preflight.py +72 -382
  157. package/harness/bundles/java/claude-code/scripts/harness_profile.py +742 -0
  158. package/harness/bundles/java/claude-code/scripts/harness_service.py +99 -2
  159. package/harness/bundles/java/claude-code/scripts/harness_state.py +179 -0
  160. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  161. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  162. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  163. package/harness/bundles/java/codebuddy/harness-archive/reference.md +2 -8
  164. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +2 -1
  165. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  166. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  167. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  168. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +3 -3
  169. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +2 -1
  170. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +5 -1
  171. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +4 -4
  172. package/harness/bundles/java/codebuddy/harness-run/checklist.md +3 -1
  173. package/harness/bundles/java/codebuddy/harness-run/reference.md +6 -0
  174. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +3 -3
  175. package/harness/bundles/java/codebuddy/harness-submit/checklist.md +30 -6
  176. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  177. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +5 -5
  178. package/harness/bundles/java/codebuddy/harness-test/checklist.md +3 -1
  179. package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +16 -0
  180. package/harness/bundles/java/codebuddy/harness-test/reference.md +2 -0
  181. package/harness/bundles/java/codebuddy/harness-test/scripts/runtime-helpers.mjs +216 -0
  182. package/harness/bundles/java/codebuddy/protocols/archive-report-protocol.md +1 -1
  183. package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +55 -2
  184. package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +1 -8
  185. package/harness/bundles/java/codebuddy/protocols/sensitive-info-protocol.md +13 -0
  186. package/harness/bundles/java/codebuddy/protocols/state-layout-protocol.md +24 -0
  187. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +31 -14
  188. package/harness/bundles/java/codebuddy/scripts/harness_check_gate.py +111 -0
  189. package/harness/bundles/java/codebuddy/scripts/harness_deploy.py +179 -4
  190. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +269 -3
  191. package/harness/bundles/java/codebuddy/scripts/harness_preflight.py +72 -382
  192. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +742 -0
  193. package/harness/bundles/java/codebuddy/scripts/harness_service.py +99 -2
  194. package/harness/bundles/java/codebuddy/scripts/harness_state.py +179 -0
  195. package/harness/bundles/java/codex/.harness-build.json +1 -1
  196. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  197. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  198. package/harness/bundles/java/codex/harness-archive/reference.md +2 -8
  199. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +2 -1
  200. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  201. package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  202. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  203. package/harness/bundles/java/codex/harness-package/SKILL.md +3 -3
  204. package/harness/bundles/java/codex/harness-plan/SKILL.md +2 -1
  205. package/harness/bundles/java/codex/harness-review/SKILL.md +5 -1
  206. package/harness/bundles/java/codex/harness-run/SKILL.md +4 -4
  207. package/harness/bundles/java/codex/harness-run/checklist.md +3 -1
  208. package/harness/bundles/java/codex/harness-run/reference.md +6 -0
  209. package/harness/bundles/java/codex/harness-submit/SKILL.md +3 -3
  210. package/harness/bundles/java/codex/harness-submit/checklist.md +30 -6
  211. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  212. package/harness/bundles/java/codex/harness-test/SKILL.md +5 -5
  213. package/harness/bundles/java/codex/harness-test/checklist.md +3 -1
  214. package/harness/bundles/java/codex/harness-test/pitfalls.md +16 -0
  215. package/harness/bundles/java/codex/harness-test/reference.md +2 -0
  216. package/harness/bundles/java/codex/harness-test/scripts/runtime-helpers.mjs +216 -0
  217. package/harness/bundles/java/codex/protocols/archive-report-protocol.md +1 -1
  218. package/harness/bundles/java/codex/protocols/ledger-protocol.md +55 -2
  219. package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +1 -8
  220. package/harness/bundles/java/codex/protocols/sensitive-info-protocol.md +13 -0
  221. package/harness/bundles/java/codex/protocols/state-layout-protocol.md +24 -0
  222. package/harness/bundles/java/codex/scripts/harness_archive.py +31 -14
  223. package/harness/bundles/java/codex/scripts/harness_check_gate.py +111 -0
  224. package/harness/bundles/java/codex/scripts/harness_deploy.py +179 -4
  225. package/harness/bundles/java/codex/scripts/harness_ledger.py +269 -3
  226. package/harness/bundles/java/codex/scripts/harness_preflight.py +72 -382
  227. package/harness/bundles/java/codex/scripts/harness_profile.py +742 -0
  228. package/harness/bundles/java/codex/scripts/harness_service.py +99 -2
  229. package/harness/bundles/java/codex/scripts/harness_state.py +179 -0
  230. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  231. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  232. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  233. package/harness/bundles/java/cursor/harness-archive/reference.md +2 -8
  234. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +2 -1
  235. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  236. package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
  237. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  238. package/harness/bundles/java/cursor/harness-package/SKILL.md +3 -3
  239. package/harness/bundles/java/cursor/harness-plan/SKILL.md +2 -1
  240. package/harness/bundles/java/cursor/harness-review/SKILL.md +5 -1
  241. package/harness/bundles/java/cursor/harness-run/SKILL.md +4 -4
  242. package/harness/bundles/java/cursor/harness-run/checklist.md +3 -1
  243. package/harness/bundles/java/cursor/harness-run/reference.md +6 -0
  244. package/harness/bundles/java/cursor/harness-submit/SKILL.md +3 -3
  245. package/harness/bundles/java/cursor/harness-submit/checklist.md +30 -6
  246. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  247. package/harness/bundles/java/cursor/harness-test/SKILL.md +5 -5
  248. package/harness/bundles/java/cursor/harness-test/checklist.md +3 -1
  249. package/harness/bundles/java/cursor/harness-test/pitfalls.md +16 -0
  250. package/harness/bundles/java/cursor/harness-test/reference.md +2 -0
  251. package/harness/bundles/java/cursor/harness-test/scripts/runtime-helpers.mjs +216 -0
  252. package/harness/bundles/java/cursor/protocols/archive-report-protocol.md +1 -1
  253. package/harness/bundles/java/cursor/protocols/ledger-protocol.md +55 -2
  254. package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +1 -8
  255. package/harness/bundles/java/cursor/protocols/sensitive-info-protocol.md +13 -0
  256. package/harness/bundles/java/cursor/protocols/state-layout-protocol.md +24 -0
  257. package/harness/bundles/java/cursor/scripts/harness_archive.py +31 -14
  258. package/harness/bundles/java/cursor/scripts/harness_check_gate.py +111 -0
  259. package/harness/bundles/java/cursor/scripts/harness_deploy.py +179 -4
  260. package/harness/bundles/java/cursor/scripts/harness_ledger.py +269 -3
  261. package/harness/bundles/java/cursor/scripts/harness_preflight.py +72 -382
  262. package/harness/bundles/java/cursor/scripts/harness_profile.py +742 -0
  263. package/harness/bundles/java/cursor/scripts/harness_service.py +99 -2
  264. package/harness/bundles/java/cursor/scripts/harness_state.py +179 -0
  265. package/harness/manifests/general/claude-code.json +44 -28
  266. package/harness/manifests/general/codebuddy.json +44 -28
  267. package/harness/manifests/general/codex.json +44 -28
  268. package/harness/manifests/general/cursor.json +44 -28
  269. package/harness/manifests/java/claude-code.json +48 -32
  270. package/harness/manifests/java/codebuddy.json +48 -32
  271. package/harness/manifests/java/codex.json +48 -32
  272. package/harness/manifests/java/cursor.json +48 -32
  273. package/hunter-workflow-family.json +2 -2
  274. package/package.json +1 -1
@@ -102,11 +102,15 @@ def read_json(path: Path) -> Any:
102
102
 
103
103
  def write_json(path: Path, data: Any) -> None:
104
104
  path.parent.mkdir(parents=True, exist_ok=True)
105
- path.write_text(
106
- json.dumps(data, ensure_ascii=False, indent=2) + "\n",
107
- encoding="utf-8",
108
- newline="\n",
109
- )
105
+ text = json.dumps(data, ensure_ascii=False, indent=2) + "\n"
106
+ # 强制 LF,UTF-8 无 BOM;原子写 temp+os.replace(与 runtime-helpers.mjs 一致)。
107
+ tmp = path.with_name(f".{path.name}.{os.getpid()}.tmp")
108
+ try:
109
+ tmp.write_text(text, encoding="utf-8", newline="\n")
110
+ os.replace(tmp, path)
111
+ except BaseException:
112
+ tmp.unlink(missing_ok=True)
113
+ raise
110
114
 
111
115
 
112
116
  def sha256_file(path: Path) -> str:
@@ -490,16 +494,29 @@ def check_status(change_dir: Path) -> dict[str, Any]:
490
494
  if head_hash.startswith(expected_hash) or expected_hash.startswith(head_hash[:7]):
491
495
  checks["final_hash_match"] = True
492
496
  else:
493
- checks["final_hash_match"] = False
494
- blockers.append(
495
- {
496
- "code": "final-hash-mismatch",
497
- "message": (
498
- f"HEAD={head_hash} != expected={expected_hash} "
499
- f"(source={hash_source})"
500
- ),
501
- }
497
+ # main may have advanced since this change merged (a later change
498
+ # merged on top); the change's final hash is still pushed as long as
499
+ # it is an ancestor of HEAD. Strict == would block archiving whenever
500
+ # the repo moved on, which is the normal case in a multi-change
501
+ # workflow.
502
+ anc_code, _, _ = git_run(
503
+ project, "merge-base", "--is-ancestor", expected_hash, head_hash
502
504
  )
505
+ if anc_code == 0:
506
+ checks["final_hash_match"] = True
507
+ checks["final_hash_ancestor"] = True
508
+ else:
509
+ checks["final_hash_match"] = False
510
+ blockers.append(
511
+ {
512
+ "code": "final-hash-mismatch",
513
+ "message": (
514
+ f"HEAD={head_hash} != expected={expected_hash} "
515
+ f"(source={hash_source}) and expected is not an ancestor "
516
+ f"of HEAD (not pushed?)"
517
+ ),
518
+ }
519
+ )
503
520
  elif head_hash and expected_hash is None:
504
521
  warnings.append(
505
522
  {
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+ """Pre-push gate: skip `npm run check` when a recent check-ok marker matches
3
+ the tree of the commit being pushed.
4
+
5
+ Triple check (all must pass to skip, else exit 1 → hook runs npm run check):
6
+ 1. marker exists and its `ts` is within MAX_AGE_S (10 min) of now
7
+ 2. marker `command` == EXPECTED_CMD ("npm run check")
8
+ 3. current `git rev-parse HEAD^{tree}` == marker `treeHash`
9
+
10
+ The marker is written by harness-submit M5 after a green `npm run check` and
11
+ records `git write-tree`. This remains valid after commit because the new HEAD
12
+ has the same tree. Any mismatch (no marker, stale, command or tree changed)
13
+ forces a full re-run — the safe default. This is a local convenience only;
14
+ non-harness pushes have no marker and always run the check.
15
+
16
+ Exit 0 = skip (recent verified), exit 1 = run npm run check.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import subprocess
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+
26
+ ROOT = Path(__file__).resolve().parents[2] # harness/scripts/../.. = repo root
27
+ MARKER = ROOT / ".harness" / "check-ok.marker"
28
+ MAX_AGE_S = 600 # 10 minutes
29
+ EXPECTED_CMD = "npm run check"
30
+
31
+
32
+ def _git_output(args: list[str]) -> str | None:
33
+ try:
34
+ r = subprocess.run(
35
+ ["git", *args],
36
+ cwd=str(ROOT),
37
+ capture_output=True,
38
+ text=True,
39
+ timeout=5,
40
+ )
41
+ except (OSError, subprocess.SubprocessError):
42
+ return None
43
+ return r.stdout.strip() if r.returncode == 0 else None
44
+
45
+
46
+ def _current_index_tree() -> str | None:
47
+ return _git_output(["write-tree"])
48
+
49
+
50
+ def _current_head_tree() -> str | None:
51
+ return _git_output(["rev-parse", "HEAD^{tree}"])
52
+
53
+
54
+ def write_marker() -> int:
55
+ """Record the verified index tree so the marker survives the next commit."""
56
+ tree = _current_index_tree()
57
+ if tree is None:
58
+ return 1
59
+ MARKER.parent.mkdir(parents=True, exist_ok=True)
60
+ payload = {"ts": time.time(), "treeHash": tree, "command": EXPECTED_CMD}
61
+ MARKER.write_text(
62
+ json.dumps(payload, ensure_ascii=False) + "\n",
63
+ encoding="utf-8",
64
+ newline="\n",
65
+ )
66
+ print(f"check-ok marker written for tree {tree[:7]}")
67
+ return 0
68
+
69
+
70
+ def main(argv: list[str] | None = None) -> int:
71
+ import argparse
72
+
73
+ ap = argparse.ArgumentParser(description="pre-push check gate")
74
+ ap.add_argument(
75
+ "--write",
76
+ action="store_true",
77
+ help="write a check-ok marker for current HEAD (harness-submit M5)",
78
+ )
79
+ args = ap.parse_args(argv)
80
+ if args.write:
81
+ return write_marker()
82
+ if not MARKER.is_file():
83
+ return 1
84
+ try:
85
+ data = json.loads(MARKER.read_text(encoding="utf-8-sig"))
86
+ except (OSError, json.JSONDecodeError):
87
+ return 1
88
+ if not isinstance(data, dict):
89
+ return 1
90
+ ts = data.get("ts")
91
+ if not isinstance(ts, (int, float)) or time.time() - ts > MAX_AGE_S:
92
+ return 1
93
+ if data.get("command") != EXPECTED_CMD:
94
+ return 1
95
+ marker_tree = data.get("treeHash")
96
+ if not isinstance(marker_tree, str):
97
+ return 1
98
+ tree = _current_head_tree()
99
+ if tree is None or tree != marker_tree:
100
+ return 1
101
+ # All three checks passed: a green npm run check ran on this exact HEAD
102
+ # within the last 10 minutes. Safe to skip.
103
+ print(
104
+ f"pre-push: skipping npm run check (verified {int(time.time() - ts)}s ago "
105
+ f"for tree {tree[:7]})"
106
+ )
107
+ return 0
108
+
109
+
110
+ if __name__ == "__main__":
111
+ sys.exit(main())
@@ -14,6 +14,7 @@ import json
14
14
  import os
15
15
  import re
16
16
  import shutil
17
+ import subprocess
17
18
  import sys
18
19
  import uuid
19
20
  from pathlib import Path
@@ -33,6 +34,25 @@ HEADING_RE = re.compile(r"^(#{2,6})\s+(.+?)\s*$")
33
34
  FRAGMENT_HINT_RE = re.compile(r"^>\s*片段:\[\[shared/[^\]]+\]\]\s*.*$")
34
35
 
35
36
  BUILD_MARKER = ".harness-build.json"
37
+ # Install-side managed manifests record which files a prior install owned, so a
38
+ # later conservative install can delete removed managed files while preserving
39
+ # user-owned files (design §3.8 要点4). Skills manifest lives in the skills
40
+ # target dir; agents manifest lives in .claude/agents.
41
+ MANIFEST_NAME = ".harness-managed.json"
42
+ AGENTS_MANAGED_NAME = ".harness-managed-agents.json"
43
+ INSTALL_METADATA = frozenset({MANIFEST_NAME, AGENTS_MANAGED_NAME})
44
+
45
+
46
+ def _read_managed_set(path: Path) -> set[str]:
47
+ """Return the ``managedFiles`` set from a prior install manifest, or empty."""
48
+ if not path.is_file():
49
+ return set()
50
+ try:
51
+ data = json.loads(path.read_text(encoding="utf-8"))
52
+ except (json.JSONDecodeError, ValueError):
53
+ return set()
54
+ files = data.get("managedFiles") if isinstance(data, dict) else None
55
+ return set(files) if isinstance(files, list) else set()
36
56
 
37
57
  SKIP_DIR_NAMES = {
38
58
  "__pycache__",
@@ -270,6 +290,48 @@ def validate_build_paths(skills_root: Path, out_dir: Path) -> tuple[Path, Path]:
270
290
  return skills_root, out_dir
271
291
 
272
292
 
293
+ def _git_repo_root(start: Path) -> Path | None:
294
+ """Return the git toplevel containing ``start``, or None if not a git repo."""
295
+ try:
296
+ r = subprocess.run(
297
+ ["git", "rev-parse", "--show-toplevel"],
298
+ cwd=str(start),
299
+ capture_output=True,
300
+ text=True,
301
+ timeout=5,
302
+ )
303
+ except (OSError, subprocess.SubprocessError):
304
+ return None
305
+ if r.returncode != 0 or not r.stdout.strip():
306
+ return None
307
+ return Path(r.stdout.strip())
308
+
309
+
310
+ def _git_blob_sha(path: Path, repo_root: Path) -> str | None:
311
+ """Return the git blob sha for a tracked file (CRLF-normalized by .gitattributes),
312
+ or None if untracked / git unavailable. Caller falls back to working-copy sha256
313
+ for untracked build inputs (deterministic enough outside git's normalization)."""
314
+ try:
315
+ rel = path.resolve().relative_to(repo_root.resolve())
316
+ except ValueError:
317
+ return None
318
+ try:
319
+ r = subprocess.run(
320
+ ["git", "ls-files", "-s", "--", rel.as_posix()],
321
+ cwd=str(repo_root),
322
+ capture_output=True,
323
+ text=True,
324
+ timeout=5,
325
+ )
326
+ except (OSError, subprocess.SubprocessError):
327
+ return None
328
+ if r.returncode != 0 or not r.stdout.strip():
329
+ return None
330
+ # format: "<mode> <sha> <stage>\t<path>"
331
+ parts = r.stdout.strip().splitlines()[0].split()
332
+ return parts[1] if len(parts) >= 2 else None
333
+
334
+
273
335
  def core_content_hash(
274
336
  skills_root: Path,
275
337
  overlay_dir: Path | None,
@@ -298,11 +360,18 @@ def core_content_hash(
298
360
  files.extend(p for p in overlay_dir.rglob("*") if p.is_file())
299
361
  if agent_overlay_dir and agent_overlay_dir.is_dir():
300
362
  files.extend(p for p in agent_overlay_dir.rglob("*") if p.is_file())
363
+ skills_root_resolved = skills_root.resolve()
364
+ repo_root = _git_repo_root(skills_root_resolved)
301
365
  for f in sorted(set(files)):
302
- rel = f.resolve().relative_to(skills_root.resolve()).as_posix()
366
+ rel = f.resolve().relative_to(skills_root_resolved).as_posix()
303
367
  h.update(rel.encode("utf-8"))
304
368
  h.update(b"\0")
305
- h.update(sha256_file(f).encode("ascii"))
369
+ # Prefer the git blob sha: .gitattributes eol=lf normalization makes the
370
+ # hash independent of working-copy CRLF state (the CRLF working-copy drift
371
+ # that previously caused core hash mismatches across main/worktree). Fall
372
+ # back to working-tree bytes for untracked build inputs (templates, etc.).
373
+ blob = _git_blob_sha(f, repo_root) if repo_root else None
374
+ h.update((blob or sha256_file(f)).encode("ascii"))
306
375
  h.update(b"\0")
307
376
  return h.hexdigest()[:16]
308
377
 
@@ -560,10 +629,37 @@ def cmd_install(build_out: Path, project: Path, target: Path | None) -> dict[str
560
629
  dest = (target or project / ".claude" / "skills").resolve()
561
630
  if not (build_out / BUILD_MARKER).is_file():
562
631
  raise ValueError(f"install source is not a marked harness build: {build_out}")
632
+
633
+ # Managed set = real files in the build (exclude build marker). Recorded so a
634
+ # later conservative install can delete removed managed files and preserve
635
+ # user-owned files (design §3.8 要点4). Paths are relative to dest, which
636
+ # mirrors build_out (agents/ included because copy_tree keeps it there).
637
+ managed_new = {p for p in collect_files(build_out) if p != BUILD_MARKER}
638
+ managed_agents_new = {
639
+ p[len("agents/"):]
640
+ for p in managed_new
641
+ if p.startswith("agents/") and "/" not in p[len("agents/"):]
642
+ }
643
+
563
644
  backup: str | None = None
564
645
  staging = dest.parent / f".{dest.name}.staging-{uuid.uuid4().hex[:8]}"
565
646
  shutil.copytree(build_out, staging)
566
647
  if dest.exists() and any(dest.iterdir()):
648
+ managed_old = _read_managed_set(dest / MANIFEST_NAME)
649
+ # Preserve user-owned files: anything in dest that was NOT managed by a
650
+ # prior install is copied into staging (never overwriting managed files
651
+ # that staging already carries from build_out). Removed managed files
652
+ # (in managed_old but not managed_new) are simply not carried over.
653
+ for rel in collect_files(dest):
654
+ if rel in (BUILD_MARKER, MANIFEST_NAME) or rel in INSTALL_METADATA:
655
+ continue
656
+ if rel in managed_old:
657
+ continue
658
+ dst = staging / rel
659
+ if dst.exists():
660
+ continue
661
+ dst.parent.mkdir(parents=True, exist_ok=True)
662
+ shutil.copy2(dest / rel, dst)
567
663
  stamp = dt.datetime.now().strftime("%Y%m%d-%H%M%S")
568
664
  backup_path = dest.parent / f"skills-backup-{stamp}"
569
665
  os.replace(dest, backup_path)
@@ -576,12 +672,41 @@ def cmd_install(build_out: Path, project: Path, target: Path | None) -> dict[str
576
672
  os.replace(Path(backup), dest)
577
673
  raise
578
674
 
675
+ # Record the managed set so the next conservative install can distinguish
676
+ # managed vs user-owned files.
677
+ (dest / MANIFEST_NAME).write_text(
678
+ json.dumps(
679
+ {"schemaVersion": 1, "managedFiles": sorted(managed_new)},
680
+ ensure_ascii=False,
681
+ indent=2,
682
+ )
683
+ + "\n",
684
+ encoding="utf-8",
685
+ newline="\n",
686
+ )
687
+
579
688
  agents_src = build_out / "agents"
580
689
  agents_dest = project / ".claude" / "agents"
581
690
  if agents_src.is_dir():
582
691
  agents_dest.mkdir(parents=True, exist_ok=True)
692
+ agents_old = _read_managed_set(agents_dest / AGENTS_MANAGED_NAME)
693
+ # Delete managed agents removed since the prior install; user agents
694
+ # (never in a managed set) are left untouched.
695
+ for name in list(agents_old):
696
+ if name not in managed_agents_new:
697
+ (agents_dest / name).unlink(missing_ok=True)
583
698
  for agent in agents_src.glob("*.md"):
584
699
  shutil.copy2(agent, agents_dest / agent.name)
700
+ (agents_dest / AGENTS_MANAGED_NAME).write_text(
701
+ json.dumps(
702
+ {"schemaVersion": 1, "managedFiles": sorted(managed_agents_new)},
703
+ ensure_ascii=False,
704
+ indent=2,
705
+ )
706
+ + "\n",
707
+ encoding="utf-8",
708
+ newline="\n",
709
+ )
585
710
 
586
711
  return {
587
712
  "ok": True,
@@ -613,11 +738,14 @@ def cmd_diff(build_out: Path, project: Path, target: Path | None) -> dict[str, A
613
738
  current = collect_files(installed)
614
739
  outdated = [p for p, h in built.items() if p in current and current[p] != h]
615
740
  missing = [p for p in built if p not in current]
616
- extra = [p for p in current if p not in built]
741
+ # extra includes install-side managed manifests only as metadata, so they
742
+ # are excluded from the extra set; every other untracked file is a real
743
+ # extra and must mark the install stale (design §3.8 要点3).
744
+ extra = [p for p in current if p not in built and p not in INSTALL_METADATA]
617
745
  return {
618
746
  "ok": True,
619
747
  "action": "diff",
620
- "stale": bool(outdated or missing),
748
+ "stale": bool(outdated or missing or extra),
621
749
  "outdated": sorted(outdated),
622
750
  "missing": sorted(missing),
623
751
  "extra": sorted(extra),
@@ -625,6 +753,35 @@ def cmd_diff(build_out: Path, project: Path, target: Path | None) -> dict[str, A
625
753
  }
626
754
 
627
755
 
756
+ def cmd_validate_manifest(
757
+ bundle_dir: Path, manifest_entries: list[dict[str, Any]]
758
+ ) -> dict[str, Any]:
759
+ """Compare a bundle directory's actual file set against the manifest's
760
+ declared set. Both missing (declared but absent) and extra (present but
761
+ undeclared) fail validation (design §3.8 要点2)."""
762
+ bundle_dir = bundle_dir.resolve()
763
+ actual = collect_files(bundle_dir)
764
+ declared: dict[str, str] = {}
765
+ for entry in manifest_entries:
766
+ path = entry.get("path")
767
+ sha = entry.get("sha256")
768
+ if isinstance(path, str) and isinstance(sha, str):
769
+ declared[path] = sha
770
+ missing = sorted(p for p in declared if p not in actual)
771
+ extra = sorted(p for p in actual if p not in declared)
772
+ hash_mismatch = sorted(
773
+ p for p in declared if p in actual and actual[p] != declared[p]
774
+ )
775
+ return {
776
+ "ok": not (missing or extra or hash_mismatch),
777
+ "action": "validate-manifest",
778
+ "missing": missing,
779
+ "extra": extra,
780
+ "hashMismatch": hash_mismatch,
781
+ "comparedAt": now_iso(),
782
+ }
783
+
784
+
628
785
  def build_parser() -> argparse.ArgumentParser:
629
786
  p = argparse.ArgumentParser(description="Harness skills deploy (build/install/diff)")
630
787
  sub = p.add_subparsers(dest="command", required=True)
@@ -647,6 +804,13 @@ def build_parser() -> argparse.ArgumentParser:
647
804
  d.add_argument("--project", type=Path, required=True)
648
805
  d.add_argument("--target", type=Path)
649
806
  d.add_argument("--json", action="store_true")
807
+
808
+ v = sub.add_parser(
809
+ "validate-manifest", help="Validate bundle dir vs manifest (missing/extra/hash)"
810
+ )
811
+ v.add_argument("--bundle", type=Path, required=True)
812
+ v.add_argument("--manifest", type=Path, required=True)
813
+ v.add_argument("--json", action="store_true")
650
814
  return p
651
815
 
652
816
 
@@ -662,6 +826,17 @@ def main(argv: list[str] | None = None) -> int:
662
826
  if args.command == "diff":
663
827
  result = cmd_diff(args.from_dir, args.project, args.target)
664
828
  return emit_json(result) if args.json else 0
829
+ if args.command == "validate-manifest":
830
+ manifest_data = json.loads(args.manifest.read_text(encoding="utf-8"))
831
+ entries = (
832
+ manifest_data.get("files", [])
833
+ if isinstance(manifest_data, dict)
834
+ else []
835
+ )
836
+ result = cmd_validate_manifest(args.bundle, entries)
837
+ if args.json:
838
+ return emit_json(result)
839
+ return 0 if result["ok"] else 1
665
840
  except (FileNotFoundError, KeyError, RuntimeError, ValueError) as exc:
666
841
  if getattr(args, "json", False):
667
842
  return emit_json({"ok": False, "error": str(exc)}, ok=False)