@hunter-harness/workflow-harness 0.2.43 → 0.2.44

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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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 +267 -22
  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 +543 -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
@@ -21,6 +21,7 @@ import hashlib
21
21
  import json
22
22
  import os
23
23
  import re
24
+ import shlex
24
25
  import shutil
25
26
  import subprocess
26
27
  import sys
@@ -44,6 +45,212 @@ SCHEMA_VERSION = 3
44
45
  PROFILE_REL = Path(".harness") / "config" / "build-profile.json"
45
46
 
46
47
 
48
+ def _legacy_command_has_shell_operator(command: str, *, platform: str) -> bool:
49
+ """Return whether direct argv migration would change shell semantics."""
50
+
51
+ quote: str | None = None
52
+ escaped = False
53
+ windows = platform == "windows"
54
+ for character in command:
55
+ if escaped:
56
+ escaped = False
57
+ continue
58
+ if not windows and character == "\\" and quote != "'":
59
+ escaped = True
60
+ continue
61
+ if character == '"' or (not windows and character == "'"):
62
+ quote = None if quote == character else (character if quote is None else quote)
63
+ continue
64
+ if quote is None and character in {"&", "|", "<", ">", ";"}:
65
+ return True
66
+ return False
67
+
68
+
69
+ def _parse_windows_command_line(command: str) -> list[str]:
70
+ """Parse CreateProcess-style argv without POSIX ``shlex`` semantics."""
71
+
72
+ argv: list[str] = []
73
+ length = len(command)
74
+ index = 0
75
+ while index < length:
76
+ while index < length and command[index] in {" ", "\t"}:
77
+ index += 1
78
+ if index >= length:
79
+ break
80
+ token: list[str] = []
81
+ token_started = False
82
+ quoted = False
83
+ while index < length:
84
+ character = command[index]
85
+ if character in {" ", "\t"} and not quoted:
86
+ break
87
+ if character == "\\":
88
+ slash_start = index
89
+ while index < length and command[index] == "\\":
90
+ index += 1
91
+ slash_count = index - slash_start
92
+ if index < length and command[index] == '"':
93
+ token.extend("\\" for _ in range(slash_count // 2))
94
+ if slash_count % 2:
95
+ token.append('"')
96
+ index += 1
97
+ else:
98
+ quoted = not quoted
99
+ token_started = True
100
+ index += 1
101
+ else:
102
+ token.extend("\\" for _ in range(slash_count))
103
+ token_started = True
104
+ continue
105
+ if character == '"':
106
+ quoted = not quoted
107
+ token_started = True
108
+ index += 1
109
+ continue
110
+ token.append(character)
111
+ token_started = True
112
+ index += 1
113
+ if quoted:
114
+ raise ValueError("PROFILE_MIGRATION_UNSAFE: unclosed Windows quote")
115
+ if token_started:
116
+ argv.append("".join(token))
117
+ while index < length and command[index] in {" ", "\t"}:
118
+ index += 1
119
+ return argv
120
+
121
+
122
+ def parse_legacy_service_command(
123
+ command: str,
124
+ *,
125
+ platform: str | None = None,
126
+ ) -> dict[str, Any]:
127
+ """Classify and parse a legacy service command without invoking a shell."""
128
+
129
+ normalized_platform = (
130
+ platform or ("windows" if os.name == "nt" else "posix")
131
+ ).strip().lower()
132
+ if normalized_platform not in {"windows", "posix"}:
133
+ return {
134
+ "ok": False,
135
+ "reasonCode": "PROFILE_INVALID",
136
+ "message": f"unsupported command platform: {normalized_platform}",
137
+ "platform": normalized_platform,
138
+ }
139
+ if not isinstance(command, str) or not command.strip():
140
+ return {
141
+ "ok": False,
142
+ "reasonCode": "PROFILE_INVALID",
143
+ "message": "legacy service command must be a non-empty string",
144
+ "platform": normalized_platform,
145
+ }
146
+ if _legacy_command_has_shell_operator(
147
+ command, platform=normalized_platform
148
+ ):
149
+ return {
150
+ "ok": False,
151
+ "reasonCode": "PROFILE_MIGRATION_UNSAFE",
152
+ "message": "legacy command contains shell operators",
153
+ "platform": normalized_platform,
154
+ }
155
+ try:
156
+ argv = (
157
+ _parse_windows_command_line(command)
158
+ if normalized_platform == "windows"
159
+ else shlex.split(command, posix=True)
160
+ )
161
+ except ValueError as exc:
162
+ return {
163
+ "ok": False,
164
+ "reasonCode": "PROFILE_MIGRATION_UNSAFE",
165
+ "message": str(exc),
166
+ "platform": normalized_platform,
167
+ }
168
+ if not argv or any("\0" in item for item in argv):
169
+ return {
170
+ "ok": False,
171
+ "reasonCode": "PROFILE_MIGRATION_UNSAFE",
172
+ "message": "legacy command cannot be represented as a safe argv array",
173
+ "platform": normalized_platform,
174
+ }
175
+ return {
176
+ "ok": True,
177
+ "reasonCode": None,
178
+ "message": "",
179
+ "platform": normalized_platform,
180
+ "argv": argv,
181
+ }
182
+
183
+
184
+ def normalize_service_start(
185
+ service_start: dict[str, Any] | None,
186
+ *,
187
+ platform: str | None = None,
188
+ ) -> dict[str, Any]:
189
+ """Normalize serviceStart while preserving unsafe legacy strings verbatim."""
190
+
191
+ source = json.loads(json.dumps(service_start or {}))
192
+ has_command = isinstance(source.get("command"), str) and bool(
193
+ source["command"].strip()
194
+ )
195
+ has_argv = isinstance(source.get("argvTemplate"), list)
196
+ if has_command and has_argv:
197
+ raise ValueError(
198
+ "PROFILE_MIGRATION_UNSAFE: serviceStart.command and "
199
+ "serviceStart.argvTemplate are mutually exclusive"
200
+ )
201
+ if has_argv:
202
+ argv = source["argvTemplate"]
203
+ if any(not isinstance(item, str) or "\0" in item for item in argv):
204
+ raise ValueError(
205
+ "PROFILE_INVALID: serviceStart.argvTemplate must contain strings"
206
+ )
207
+ source.pop("command", None)
208
+ source.pop("legacyWarning", None)
209
+ return source
210
+ if has_command:
211
+ parsed = parse_legacy_service_command(
212
+ source["command"], platform=platform
213
+ )
214
+ if parsed["ok"]:
215
+ source["argvTemplate"] = parsed["argv"]
216
+ source.pop("command", None)
217
+ source.pop("legacyWarning", None)
218
+ else:
219
+ source["legacyWarning"] = {
220
+ "reasonCode": parsed["reasonCode"],
221
+ "platform": parsed["platform"],
222
+ "message": parsed["message"],
223
+ }
224
+ return source
225
+ source.pop("command", None)
226
+ source["argvTemplate"] = []
227
+ source.pop("legacyWarning", None)
228
+ return source
229
+
230
+
231
+ def resolve_service_argv(
232
+ service_start: dict[str, Any],
233
+ replacements: dict[str, str] | None = None,
234
+ *,
235
+ platform: str | None = None,
236
+ ) -> list[str]:
237
+ """Resolve placeholders per argv element; never reparse a joined string."""
238
+
239
+ normalized = normalize_service_start(service_start, platform=platform)
240
+ argv = normalized.get("argvTemplate")
241
+ if not isinstance(argv, list):
242
+ warning = normalized.get("legacyWarning") or {}
243
+ reason = warning.get("reasonCode") or "PROFILE_MIGRATION_UNSAFE"
244
+ raise ValueError(f"{reason}: legacy service command requires review")
245
+ resolved: list[str] = []
246
+ for element in argv:
247
+ value = element
248
+ for placeholder, replacement in (replacements or {}).items():
249
+ value = value.replace(placeholder, replacement)
250
+ resolved.append(value)
251
+ return resolved
252
+
253
+
47
254
  def recommend(project: Path) -> dict[str, Any]:
48
255
  """Return a reviewable profile recommendation without writing it."""
49
256
  project = Path(project).resolve()
@@ -147,7 +354,11 @@ def recommend(project: Path) -> dict[str, Any]:
147
354
  }
148
355
 
149
356
 
150
- def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
357
+ def project_profile_v3(
358
+ profile: dict[str, Any],
359
+ *,
360
+ platform: str | None = None,
361
+ ) -> dict[str, Any]:
151
362
  """Compatibility projection for review before a protected v3 update."""
152
363
  projected = json.loads(json.dumps(profile))
153
364
  source_version = projected.get("schemaVersion")
@@ -195,6 +406,16 @@ def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
195
406
  if isinstance(command, dict) and command.get("source") == "user"
196
407
  ),
197
408
  }
409
+ projected["serviceStart"] = normalize_service_start(
410
+ projected.get("serviceStart")
411
+ if isinstance(projected.get("serviceStart"), dict)
412
+ else None,
413
+ platform=platform,
414
+ )
415
+ legacy_warning = projected["serviceStart"].get("legacyWarning")
416
+ if isinstance(legacy_warning, dict):
417
+ projected["migration"]["needsReview"] = True
418
+ projected["migration"]["serviceStartWarning"] = legacy_warning
198
419
  projected["defaultsFingerprint"] = profile_defaults_fingerprint()
199
420
  _derive_verification_inputs(projected)
200
421
  projected["verificationInputs"] = {
@@ -673,7 +894,7 @@ def empty_profile_skeleton(excluded: tuple[str, ...] | list[str]) -> dict[str, A
673
894
  "targets": {},
674
895
  },
675
896
  "serviceStart": {
676
- "command": "",
897
+ "argvTemplate": [],
677
898
  "healthUrl": "",
678
899
  "startTimeoutSec": 120,
679
900
  "inputFiles": [],
@@ -1352,8 +1573,27 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
1352
1573
  if isinstance(val, str) and val.strip():
1353
1574
  dropped.append(f"serviceStart.{field}(无 provenance,已备份;请用 record-quirk 重配)")
1354
1575
 
1576
+ projected: dict[str, Any] | None = None
1577
+ if sv == 2:
1578
+ try:
1579
+ projected = project_profile_v3(existing)
1580
+ except ValueError as exc:
1581
+ code = str(exc).split(":", 1)[0]
1582
+ return {
1583
+ "ok": False,
1584
+ "code": (
1585
+ code
1586
+ if code in {"PROFILE_INVALID", "PROFILE_MIGRATION_UNSAFE"}
1587
+ else "PROFILE_MIGRATION_UNSAFE"
1588
+ ),
1589
+ "action": "migrate",
1590
+ "dry_run": dry_run,
1591
+ "needsMigration": True,
1592
+ "error": str(exc),
1593
+ "profilePath": str(profile_path),
1594
+ }
1595
+
1355
1596
  if dry_run:
1356
- projected = project_profile_v3(existing) if sv == 2 else None
1357
1597
  return {
1358
1598
  "ok": True,
1359
1599
  "code": "PROFILE_MIGRATION_PLANNED",
@@ -1376,7 +1616,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
1376
1616
  if not backup.is_file():
1377
1617
  backup.write_text(profile_path.read_text(encoding="utf-8-sig"), encoding="utf-8", newline="\n")
1378
1618
  if sv == 2:
1379
- projected = project_profile_v3(existing)
1619
+ assert projected is not None
1380
1620
  projected.setdefault("migration", {})["appliedAt"] = now_iso()
1381
1621
  write_json(profile_path, projected)
1382
1622
  else: